1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16:
17:
18: App::uses('AppShell', 'Console/Command');
19: App::uses('BakeTask', 'Console/Command/Task');
20: App::uses('ConnectionManager', 'Model');
21: App::uses('Model', 'Model');
22: App::uses('Validation', 'Utility');
23:
24: 25: 26: 27: 28:
29: class ModelTask extends BakeTask {
30:
31: 32: 33: 34: 35:
36: public $path = null;
37:
38: 39: 40: 41: 42:
43: public $tasks = array('DbConfig', 'Fixture', 'Test', 'Template');
44:
45: 46: 47: 48: 49:
50: public $skipTables = array('i18n');
51:
52: 53: 54: 55: 56:
57: protected $_tables = array();
58:
59: 60: 61: 62: 63:
64: protected $_modelNames = array();
65:
66: 67: 68: 69: 70:
71: protected $_validations = array();
72:
73: 74: 75: 76: 77:
78: public function initialize() {
79: $this->path = current(App::path('Model'));
80: }
81:
82: 83: 84: 85: 86:
87: public function execute() {
88: parent::execute();
89:
90: if (empty($this->args)) {
91: $this->_interactive();
92: }
93:
94: if (!empty($this->args[0])) {
95: $this->interactive = false;
96: if (!isset($this->connection)) {
97: $this->connection = 'default';
98: }
99: if (strtolower($this->args[0]) === 'all') {
100: return $this->all();
101: }
102: $model = $this->_modelName($this->args[0]);
103: $this->listAll($this->connection);
104: $useTable = $this->getTable($model);
105: $object = $this->_getModelObject($model, $useTable);
106: if ($this->bake($object, false)) {
107: if ($this->_checkUnitTest()) {
108: $this->bakeFixture($model, $useTable);
109: $this->bakeTest($model);
110: }
111: }
112: }
113: }
114:
115: 116: 117: 118: 119:
120: public function all() {
121: $this->listAll($this->connection, false);
122: $unitTestExists = $this->_checkUnitTest();
123: foreach ($this->_tables as $table) {
124: if (in_array($table, $this->skipTables)) {
125: continue;
126: }
127: $modelClass = Inflector::classify($table);
128: $this->out(__d('cake_console', 'Baking %s', $modelClass));
129: $object = $this->_getModelObject($modelClass, $table);
130: if ($this->bake($object, false) && $unitTestExists) {
131: $this->bakeFixture($modelClass, $table);
132: $this->bakeTest($modelClass);
133: }
134: }
135: }
136:
137: 138: 139: 140: 141: 142: 143:
144: protected function _getModelObject($className, $table = null) {
145: if (!$table) {
146: $table = Inflector::tableize($className);
147: }
148: $object = new Model(array('name' => $className, 'table' => $table, 'ds' => $this->connection));
149: $fields = $object->schema(true);
150: foreach ($fields as $name => $field) {
151: if (isset($field['key']) && $field['key'] === 'primary') {
152: $object->primaryKey = $name;
153: break;
154: }
155: }
156: return $object;
157: }
158:
159: 160: 161: 162: 163: 164: 165: 166:
167: public function inOptions($options, $prompt = null, $default = null) {
168: $valid = false;
169: $max = count($options);
170: while (!$valid) {
171: $len = strlen(count($options) + 1);
172: foreach ($options as $i => $option) {
173: $this->out(sprintf("%${len}d. %s", $i + 1, $option));
174: }
175: if (empty($prompt)) {
176: $prompt = __d('cake_console', 'Make a selection from the choices above');
177: }
178: $choice = $this->in($prompt, null, $default);
179: if ((int)$choice > 0 && (int)$choice <= $max) {
180: $valid = true;
181: }
182: }
183: return $choice - 1;
184: }
185:
186: 187: 188: 189: 190:
191: protected function _interactive() {
192: $this->hr();
193: $this->out(__d('cake_console', "Bake Model\nPath: %s", $this->getPath()));
194: $this->hr();
195: $this->interactive = true;
196:
197: $primaryKey = 'id';
198: $validate = $associations = array();
199:
200: if (empty($this->connection)) {
201: $this->connection = $this->DbConfig->getConfig();
202: }
203: $currentModelName = $this->getName();
204: $useTable = $this->getTable($currentModelName);
205: $db = ConnectionManager::getDataSource($this->connection);
206: $fullTableName = $db->fullTableName($useTable);
207: if (!in_array($useTable, $this->_tables)) {
208: $prompt = __d('cake_console', "The table %s doesn't exist or could not be automatically detected\ncontinue anyway?", $useTable);
209: $continue = $this->in($prompt, array('y', 'n'));
210: if (strtolower($continue) === 'n') {
211: return false;
212: }
213: }
214:
215: $tempModel = new Model(array('name' => $currentModelName, 'table' => $useTable, 'ds' => $this->connection));
216:
217: $knownToExist = false;
218: try {
219: $fields = $tempModel->schema(true);
220: $knownToExist = true;
221: } catch (Exception $e) {
222: $fields = array($tempModel->primaryKey);
223: }
224: if (!array_key_exists('id', $fields)) {
225: $primaryKey = $this->findPrimaryKey($fields);
226: }
227: $displayField = null;
228: if ($knownToExist) {
229: $displayField = $tempModel->hasField(array('name', 'title'));
230: if (!$displayField) {
231: $displayField = $this->findDisplayField($tempModel->schema());
232: }
233:
234: $prompt = __d('cake_console', "Would you like to supply validation criteria \nfor the fields in your model?");
235: $wannaDoValidation = $this->in($prompt, array('y', 'n'), 'y');
236: if (array_search($useTable, $this->_tables) !== false && strtolower($wannaDoValidation) === 'y') {
237: $validate = $this->doValidation($tempModel);
238: }
239:
240: $prompt = __d('cake_console', "Would you like to define model associations\n(hasMany, hasOne, belongsTo, etc.)?");
241: $wannaDoAssoc = $this->in($prompt, array('y', 'n'), 'y');
242: if (strtolower($wannaDoAssoc) === 'y') {
243: $associations = $this->doAssociations($tempModel);
244: }
245: }
246:
247: $this->out();
248: $this->hr();
249: $this->out(__d('cake_console', 'The following Model will be created:'));
250: $this->hr();
251: $this->out(__d('cake_console', "Name: %s", $currentModelName));
252:
253: if ($this->connection !== 'default') {
254: $this->out(__d('cake_console', "DB Config: %s", $this->connection));
255: }
256: if ($fullTableName !== Inflector::tableize($currentModelName)) {
257: $this->out(__d('cake_console', 'DB Table: %s', $fullTableName));
258: }
259: if ($primaryKey !== 'id') {
260: $this->out(__d('cake_console', 'Primary Key: %s', $primaryKey));
261: }
262: if (!empty($validate)) {
263: $this->out(__d('cake_console', 'Validation: %s', print_r($validate, true)));
264: }
265: if (!empty($associations)) {
266: $this->out(__d('cake_console', 'Associations:'));
267: $assocKeys = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
268: foreach ($assocKeys as $assocKey) {
269: $this->_printAssociation($currentModelName, $assocKey, $associations);
270: }
271: }
272:
273: $this->hr();
274: $looksGood = $this->in(__d('cake_console', 'Look okay?'), array('y', 'n'), 'y');
275:
276: if (strtolower($looksGood) === 'y') {
277: $vars = compact('associations', 'validate', 'primaryKey', 'useTable', 'displayField');
278: $vars['useDbConfig'] = $this->connection;
279: if ($this->bake($currentModelName, $vars)) {
280: if ($this->_checkUnitTest()) {
281: $this->bakeFixture($currentModelName, $useTable);
282: $this->bakeTest($currentModelName, $useTable, $associations);
283: }
284: }
285: } else {
286: return false;
287: }
288: }
289:
290: 291: 292: 293: 294: 295: 296: 297:
298: protected function _printAssociation($modelName, $type, $associations) {
299: if (!empty($associations[$type])) {
300: for ($i = 0, $len = count($associations[$type]); $i < $len; $i++) {
301: $out = "\t" . $modelName . ' ' . $type . ' ' . $associations[$type][$i]['alias'];
302: $this->out($out);
303: }
304: }
305: }
306:
307: 308: 309: 310: 311: 312:
313: public function findPrimaryKey($fields) {
314: $name = 'id';
315: foreach ($fields as $name => $field) {
316: if (isset($field['key']) && $field['key'] === 'primary') {
317: break;
318: }
319: }
320: return $this->in(__d('cake_console', 'What is the primaryKey?'), null, $name);
321: }
322:
323: 324: 325: 326: 327: 328:
329: public function findDisplayField($fields) {
330: $fieldNames = array_keys($fields);
331: $prompt = __d('cake_console', "A displayField could not be automatically detected\nwould you like to choose one?");
332: $continue = $this->in($prompt, array('y', 'n'));
333: if (strtolower($continue) === 'n') {
334: return false;
335: }
336: $prompt = __d('cake_console', 'Choose a field from the options above:');
337: $choice = $this->inOptions($fieldNames, $prompt);
338: return $fieldNames[$choice];
339: }
340:
341: 342: 343: 344: 345: 346:
347: public function doValidation($model) {
348: if (!$model instanceof Model) {
349: return false;
350: }
351:
352: $fields = $model->schema();
353: if (empty($fields)) {
354: return false;
355: }
356:
357: $skipFields = false;
358: $validate = array();
359: $this->initValidations();
360: foreach ($fields as $fieldName => $field) {
361: $validation = $this->fieldValidation($fieldName, $field, $model->primaryKey);
362: if (isset($validation['_skipFields'])) {
363: unset($validation['_skipFields']);
364: $skipFields = true;
365: }
366: if (!empty($validation)) {
367: $validate[$fieldName] = $validation;
368: }
369: if ($skipFields) {
370: return $validate;
371: }
372: }
373: return $validate;
374: }
375:
376: 377: 378: 379: 380:
381: public function initValidations() {
382: $options = $choices = array();
383: if (class_exists('Validation')) {
384: $options = get_class_methods('Validation');
385: }
386: $deprecatedOptions = array('notEmpty', 'between', 'ssn');
387: $options = array_diff($options, $deprecatedOptions);
388: sort($options);
389: $default = 1;
390: foreach ($options as $option) {
391: if ($option[0] !== '_') {
392: $choices[$default] = $option;
393: $default++;
394: }
395: }
396: $choices[$default] = 'none';
397: $this->_validations = $choices;
398: return $choices;
399: }
400:
401: 402: 403: 404: 405: 406: 407: 408:
409: public function fieldValidation($fieldName, $metaData, $primaryKey = 'id') {
410: $defaultChoice = count($this->_validations);
411: $validate = $alreadyChosen = array();
412:
413: $prompt = __d('cake_console',
414: "or enter in a valid regex validation string.\nAlternatively [s] skip the rest of the fields.\n"
415: );
416: $methods = array_flip($this->_validations);
417:
418: $anotherValidator = 'y';
419: while ($anotherValidator === 'y') {
420: if ($this->interactive) {
421: $this->out();
422: $this->out(__d('cake_console', 'Field: <info>%s</info>', $fieldName));
423: $this->out(__d('cake_console', 'Type: <info>%s</info>', $metaData['type']));
424: $this->hr();
425: $this->out(__d('cake_console', 'Please select one of the following validation options:'));
426: $this->hr();
427:
428: $optionText = '';
429: for ($i = 1, $m = $defaultChoice / 2; $i <= $m; $i++) {
430: $line = sprintf("%2d. %s", $i, $this->_validations[$i]);
431: $optionText .= $line . str_repeat(" ", 31 - strlen($line));
432: if ($m + $i !== $defaultChoice) {
433: $optionText .= sprintf("%2d. %s\n", $m + $i, $this->_validations[$m + $i]);
434: }
435: }
436: $this->out($optionText);
437: $this->out(__d('cake_console', "%s - Do not do any validation on this field.", $defaultChoice));
438: $this->hr();
439: }
440:
441: $guess = $defaultChoice;
442: if ($metaData['null'] != 1 && !in_array($fieldName, array($primaryKey, 'created', 'modified', 'updated'))) {
443: if ($fieldName === 'email') {
444: $guess = $methods['email'];
445: } elseif ($metaData['type'] === 'string' && $metaData['length'] == 36) {
446: $guess = $methods['uuid'];
447: } elseif ($metaData['type'] === 'string') {
448: $guess = $methods['notBlank'];
449: } elseif ($metaData['type'] === 'text') {
450: $guess = $methods['notBlank'];
451: } elseif ($metaData['type'] === 'integer') {
452: $guess = $methods['numeric'];
453: } elseif ($metaData['type'] === 'smallinteger') {
454: $guess = $methods['numeric'];
455: } elseif ($metaData['type'] === 'tinyinteger') {
456: $guess = $methods['numeric'];
457: } elseif ($metaData['type'] === 'float') {
458: $guess = $methods['numeric'];
459: } elseif ($metaData['type'] === 'boolean') {
460: $guess = $methods['boolean'];
461: } elseif ($metaData['type'] === 'date') {
462: $guess = $methods['date'];
463: } elseif ($metaData['type'] === 'time') {
464: $guess = $methods['time'];
465: } elseif ($metaData['type'] === 'datetime') {
466: $guess = $methods['datetime'];
467: } elseif ($metaData['type'] === 'inet') {
468: $guess = $methods['ip'];
469: } elseif ($metaData['type'] === 'decimal') {
470: $guess = $methods['decimal'];
471: }
472: }
473:
474: if ($this->interactive === true) {
475: $choice = $this->in($prompt, null, $guess);
476: if ($choice === 's') {
477: $validate['_skipFields'] = true;
478: return $validate;
479: }
480: if (in_array($choice, $alreadyChosen)) {
481: $this->out(__d('cake_console', "You have already chosen that validation rule,\nplease choose again"));
482: continue;
483: }
484: if (!isset($this->_validations[$choice]) && is_numeric($choice)) {
485: $this->out(__d('cake_console', 'Please make a valid selection.'));
486: continue;
487: }
488: $alreadyChosen[] = $choice;
489: } else {
490: $choice = $guess;
491: }
492:
493: if (isset($this->_validations[$choice])) {
494: $validatorName = $this->_validations[$choice];
495: } else {
496: $validatorName = Inflector::slug($choice);
497: }
498:
499: if ($choice != $defaultChoice) {
500: $validate[$validatorName] = $choice;
501: if (is_numeric($choice) && isset($this->_validations[$choice])) {
502: $validate[$validatorName] = $this->_validations[$choice];
503: }
504: }
505: $anotherValidator = 'n';
506: if ($this->interactive && $choice != $defaultChoice) {
507: $anotherValidator = $this->in(__d('cake_console', "Would you like to add another validation rule\n" .
508: "or skip the rest of the fields?"), array('y', 'n', 's'), 'n');
509: if ($anotherValidator === 's') {
510: $validate['_skipFields'] = true;
511: return $validate;
512: }
513: }
514: }
515: return $validate;
516: }
517:
518: 519: 520: 521: 522: 523:
524: public function doAssociations($model) {
525: if (!$model instanceof Model) {
526: return false;
527: }
528: if ($this->interactive === true) {
529: $this->out(__d('cake_console', 'One moment while the associations are detected.'));
530: }
531:
532: $fields = $model->schema(true);
533: if (empty($fields)) {
534: return array();
535: }
536:
537: if (empty($this->_tables)) {
538: $this->_tables = (array)$this->getAllTables();
539: }
540:
541: $associations = array(
542: 'belongsTo' => array(),
543: 'hasMany' => array(),
544: 'hasOne' => array(),
545: 'hasAndBelongsToMany' => array()
546: );
547:
548: $associations = $this->findBelongsTo($model, $associations);
549: $associations = $this->findHasOneAndMany($model, $associations);
550: $associations = $this->findHasAndBelongsToMany($model, $associations);
551:
552: if ($this->interactive !== true) {
553: unset($associations['hasOne']);
554: }
555:
556: if ($this->interactive === true) {
557: $this->hr();
558: if (empty($associations)) {
559: $this->out(__d('cake_console', 'None found.'));
560: } else {
561: $this->out(__d('cake_console', 'Please confirm the following associations:'));
562: $this->hr();
563: $associations = $this->confirmAssociations($model, $associations);
564: }
565: $associations = $this->doMoreAssociations($model, $associations);
566: }
567: return $associations;
568: }
569:
570: 571: 572: 573: 574: 575:
576: public function doActsAs($model) {
577: if (!$model instanceof Model) {
578: return false;
579: }
580: $behaviors = array();
581: $fields = $model->schema(true);
582: if (empty($fields)) {
583: return array();
584: }
585:
586: if (isset($fields['lft']) && $fields['lft']['type'] === 'integer' &&
587: isset($fields['rght']) && $fields['rght']['type'] === 'integer' &&
588: isset($fields['parent_id'])) {
589: $behaviors[] = 'Tree';
590: }
591: return $behaviors;
592: }
593:
594: 595: 596: 597: 598: 599: 600:
601: public function findBelongsTo(Model $model, $associations) {
602: $fieldNames = array_keys($model->schema(true));
603: foreach ($fieldNames as $fieldName) {
604: $offset = substr($fieldName, -3) === '_id';
605: if ($fieldName != $model->primaryKey && $fieldName !== 'parent_id' && $offset !== false) {
606: $tmpModelName = $this->_modelNameFromKey($fieldName);
607: $associations['belongsTo'][] = array(
608: 'alias' => $tmpModelName,
609: 'className' => $tmpModelName,
610: 'foreignKey' => $fieldName,
611: );
612: } elseif ($fieldName === 'parent_id') {
613: $associations['belongsTo'][] = array(
614: 'alias' => 'Parent' . $model->name,
615: 'className' => $model->name,
616: 'foreignKey' => $fieldName,
617: );
618: }
619: }
620: return $associations;
621: }
622:
623: 624: 625: 626: 627: 628: 629:
630: public function findHasOneAndMany(Model $model, $associations) {
631: $foreignKey = $this->_modelKey($model->name);
632: foreach ($this->_tables as $otherTable) {
633: $tempOtherModel = $this->_getModelObject($this->_modelName($otherTable), $otherTable);
634: $tempFieldNames = array_keys($tempOtherModel->schema(true));
635:
636: $pattern = '/_' . preg_quote($model->table, '/') . '|' . preg_quote($model->table, '/') . '_/';
637: $possibleJoinTable = preg_match($pattern, $otherTable);
638: if ($possibleJoinTable) {
639: continue;
640: }
641: foreach ($tempFieldNames as $fieldName) {
642: $assoc = false;
643: if ($fieldName !== $model->primaryKey && $fieldName === $foreignKey) {
644: $assoc = array(
645: 'alias' => $tempOtherModel->name,
646: 'className' => $tempOtherModel->name,
647: 'foreignKey' => $fieldName
648: );
649: } elseif ($otherTable === $model->table && $fieldName === 'parent_id') {
650: $assoc = array(
651: 'alias' => 'Child' . $model->name,
652: 'className' => $model->name,
653: 'foreignKey' => $fieldName
654: );
655: }
656: if ($assoc) {
657: $associations['hasOne'][] = $assoc;
658: $associations['hasMany'][] = $assoc;
659: }
660:
661: }
662: }
663: return $associations;
664: }
665:
666: 667: 668: 669: 670: 671: 672:
673: public function findHasAndBelongsToMany(Model $model, $associations) {
674: $foreignKey = $this->_modelKey($model->name);
675: foreach ($this->_tables as $otherTable) {
676: $tableName = null;
677: $offset = strpos($otherTable, $model->table . '_');
678: $otherOffset = strpos($otherTable, '_' . $model->table);
679:
680: if ($offset !== false) {
681: $tableName = substr($otherTable, strlen($model->table . '_'));
682: } elseif ($otherOffset !== false) {
683: $tableName = substr($otherTable, 0, $otherOffset);
684: }
685: if ($tableName && in_array($tableName, $this->_tables)) {
686: $habtmName = $this->_modelName($tableName);
687: $associations['hasAndBelongsToMany'][] = array(
688: 'alias' => $habtmName,
689: 'className' => $habtmName,
690: 'foreignKey' => $foreignKey,
691: 'associationForeignKey' => $this->_modelKey($habtmName),
692: 'joinTable' => $otherTable
693: );
694: }
695: }
696: return $associations;
697: }
698:
699: 700: 701: 702: 703: 704: 705:
706: public function confirmAssociations(Model $model, $associations) {
707: foreach ($associations as $type => $settings) {
708: if (!empty($associations[$type])) {
709: foreach ($associations[$type] as $i => $assoc) {
710: $prompt = "{$model->name} {$type} {$assoc['alias']}?";
711: $response = $this->in($prompt, array('y', 'n'), 'y');
712:
713: if (strtolower($response) === 'n') {
714: unset($associations[$type][$i]);
715: } elseif ($type === 'hasMany') {
716: unset($associations['hasOne'][$i]);
717: }
718: }
719: $associations[$type] = array_merge($associations[$type]);
720: }
721: }
722: return $associations;
723: }
724:
725: 726: 727: 728: 729: 730: 731:
732: public function doMoreAssociations(Model $model, $associations) {
733: $prompt = __d('cake_console', 'Would you like to define some additional model associations?');
734: $wannaDoMoreAssoc = $this->in($prompt, array('y', 'n'), 'n');
735: $possibleKeys = $this->_generatePossibleKeys();
736: while (strtolower($wannaDoMoreAssoc) === 'y') {
737: $assocs = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
738: $this->out(__d('cake_console', 'What is the association type?'));
739: $assocType = (int)$this->inOptions($assocs, __d('cake_console', 'Enter a number'));
740:
741: $this->out(__d('cake_console', "For the following options be very careful to match your setup exactly.\n" .
742: "Any spelling mistakes will cause errors."));
743: $this->hr();
744:
745: $alias = $this->in(__d('cake_console', 'What is the alias for this association?'));
746: $className = $this->in(__d('cake_console', 'What className will %s use?', $alias), null, $alias);
747:
748: if ($assocType === 0) {
749: if (!empty($possibleKeys[$model->table])) {
750: $showKeys = $possibleKeys[$model->table];
751: } else {
752: $showKeys = null;
753: }
754: $suggestedForeignKey = $this->_modelKey($alias);
755: } else {
756: $otherTable = Inflector::tableize($className);
757: if (in_array($otherTable, $this->_tables)) {
758: if ($assocType < 3) {
759: if (!empty($possibleKeys[$otherTable])) {
760: $showKeys = $possibleKeys[$otherTable];
761: } else {
762: $showKeys = null;
763: }
764: } else {
765: $showKeys = null;
766: }
767: } else {
768: $otherTable = $this->in(__d('cake_console', 'What is the table for this model?'));
769: $showKeys = $possibleKeys[$otherTable];
770: }
771: $suggestedForeignKey = $this->_modelKey($model->name);
772: }
773: if (!empty($showKeys)) {
774: $this->out(__d('cake_console', 'A helpful List of possible keys'));
775: $foreignKey = $this->inOptions($showKeys, __d('cake_console', 'What is the foreignKey?'));
776: $foreignKey = $showKeys[(int)$foreignKey];
777: }
778: if (!isset($foreignKey)) {
779: $foreignKey = $this->in(__d('cake_console', 'What is the foreignKey? Specify your own.'), null, $suggestedForeignKey);
780: }
781: if ($assocType === 3) {
782: $associationForeignKey = $this->in(__d('cake_console', 'What is the associationForeignKey?'), null, $this->_modelKey($model->name));
783: $joinTable = $this->in(__d('cake_console', 'What is the joinTable?'));
784: }
785: $associations[$assocs[$assocType]] = array_values((array)$associations[$assocs[$assocType]]);
786: $count = count($associations[$assocs[$assocType]]);
787: $i = ($count > 0) ? $count : 0;
788: $associations[$assocs[$assocType]][$i]['alias'] = $alias;
789: $associations[$assocs[$assocType]][$i]['className'] = $className;
790: $associations[$assocs[$assocType]][$i]['foreignKey'] = $foreignKey;
791: if ($assocType === 3) {
792: $associations[$assocs[$assocType]][$i]['associationForeignKey'] = $associationForeignKey;
793: $associations[$assocs[$assocType]][$i]['joinTable'] = $joinTable;
794: }
795: $wannaDoMoreAssoc = $this->in(__d('cake_console', 'Define another association?'), array('y', 'n'), 'y');
796: }
797: return $associations;
798: }
799:
800: 801: 802: 803: 804:
805: protected function _generatePossibleKeys() {
806: $possible = array();
807: foreach ($this->_tables as $otherTable) {
808: $tempOtherModel = new Model(array('table' => $otherTable, 'ds' => $this->connection));
809: $modelFieldsTemp = $tempOtherModel->schema(true);
810: foreach ($modelFieldsTemp as $fieldName => $field) {
811: if ($field['type'] === 'integer' || $field['type'] === 'string') {
812: $possible[$otherTable][] = $fieldName;
813: }
814: }
815: }
816: return $possible;
817: }
818:
819: 820: 821: 822: 823: 824: 825:
826: public function bake($name, $data = array()) {
827: if ($name instanceof Model) {
828: if (!$data) {
829: $data = array();
830: $data['associations'] = $this->doAssociations($name);
831: $data['validate'] = $this->doValidation($name);
832: $data['actsAs'] = $this->doActsAs($name);
833: }
834: $data['primaryKey'] = $name->primaryKey;
835: $data['useTable'] = $name->table;
836: $data['useDbConfig'] = $name->useDbConfig;
837: $data['name'] = $name = $name->name;
838: } else {
839: $data['name'] = $name;
840: }
841:
842: $defaults = array(
843: 'associations' => array(),
844: 'actsAs' => array(),
845: 'validate' => array(),
846: 'primaryKey' => 'id',
847: 'useTable' => null,
848: 'useDbConfig' => 'default',
849: 'displayField' => null
850: );
851: $data = array_merge($defaults, $data);
852:
853: $pluginPath = '';
854: if ($this->plugin) {
855: $pluginPath = $this->plugin . '.';
856: }
857:
858: $this->Template->set($data);
859: $this->Template->set(array(
860: 'plugin' => $this->plugin,
861: 'pluginPath' => $pluginPath
862: ));
863: $out = $this->Template->generate('classes', 'model');
864:
865: $path = $this->getPath();
866: $filename = $path . $name . '.php';
867: $this->out("\n" . __d('cake_console', 'Baking model class for %s...', $name), 1, Shell::QUIET);
868: $this->createFile($filename, $out);
869: ClassRegistry::flush();
870: return $out;
871: }
872:
873: 874: 875: 876: 877: 878:
879: public function bakeTest($className) {
880: $this->Test->interactive = $this->interactive;
881: $this->Test->plugin = $this->plugin;
882: $this->Test->connection = $this->connection;
883: return $this->Test->bake('Model', $className);
884: }
885:
886: 887: 888: 889: 890: 891:
892: public function listAll($useDbConfig = null) {
893: $this->_tables = $this->getAllTables($useDbConfig);
894:
895: $this->_modelNames = array();
896: $count = count($this->_tables);
897: for ($i = 0; $i < $count; $i++) {
898: $this->_modelNames[] = $this->_modelName($this->_tables[$i]);
899: }
900: if ($this->interactive === true) {
901: $this->out(__d('cake_console', 'Possible Models based on your current database:'));
902: $len = strlen($count + 1);
903: for ($i = 0; $i < $count; $i++) {
904: $this->out(sprintf("%${len}d. %s", $i + 1, $this->_modelNames[$i]));
905: }
906: }
907: return $this->_tables;
908: }
909:
910: 911: 912: 913: 914: 915: 916:
917: public function getTable($modelName, $useDbConfig = null) {
918: $useTable = Inflector::tableize($modelName);
919: if (in_array($modelName, $this->_modelNames)) {
920: $modelNames = array_flip($this->_modelNames);
921: $useTable = $this->_tables[$modelNames[$modelName]];
922: }
923:
924: if ($this->interactive === true) {
925: if (!isset($useDbConfig)) {
926: $useDbConfig = $this->connection;
927: }
928: $db = ConnectionManager::getDataSource($useDbConfig);
929: $fullTableName = $db->fullTableName($useTable, false);
930: $tableIsGood = false;
931: if (array_search($useTable, $this->_tables) === false) {
932: $this->out();
933: $this->out(__d('cake_console', "Given your model named '%s',\nCake would expect a database table named '%s'", $modelName, $fullTableName));
934: $tableIsGood = $this->in(__d('cake_console', 'Do you want to use this table?'), array('y', 'n'), 'y');
935: }
936: if (strtolower($tableIsGood) === 'n') {
937: $useTable = $this->in(__d('cake_console', 'What is the name of the table (without prefix)?'));
938: }
939: }
940: return $useTable;
941: }
942:
943: 944: 945: 946: 947: 948: 949:
950: public function getAllTables($useDbConfig = null) {
951: if (!isset($useDbConfig)) {
952: $useDbConfig = $this->connection;
953: }
954:
955: $tables = array();
956: $db = ConnectionManager::getDataSource($useDbConfig);
957: $db->cacheSources = false;
958: $usePrefix = empty($db->config['prefix']) ? '' : $db->config['prefix'];
959: if ($usePrefix) {
960: foreach ($db->listSources() as $table) {
961: if (!strncmp($table, $usePrefix, strlen($usePrefix))) {
962: $tables[] = substr($table, strlen($usePrefix));
963: }
964: }
965: } else {
966: $tables = $db->listSources();
967: }
968: if (empty($tables)) {
969: $this->err(__d('cake_console', 'Your database does not have any tables.'));
970: return $this->_stop();
971: }
972: sort($tables);
973: return $tables;
974: }
975:
976: 977: 978: 979: 980: 981:
982: public function getName($useDbConfig = null) {
983: $this->listAll($useDbConfig);
984:
985: $enteredModel = '';
986:
987: while (!$enteredModel) {
988: $enteredModel = $this->in(__d('cake_console', "Enter a number from the list above,\n" .
989: "type in the name of another model, or 'q' to exit"), null, 'q');
990:
991: if ($enteredModel === 'q') {
992: $this->out(__d('cake_console', 'Exit'));
993: return $this->_stop();
994: }
995:
996: if (!$enteredModel || (int)$enteredModel > count($this->_modelNames)) {
997: $this->err(__d('cake_console', "The model name you supplied was empty,\n" .
998: "or the number you selected was not an option. Please try again."));
999: $enteredModel = '';
1000: }
1001: }
1002: if ((int)$enteredModel > 0 && (int)$enteredModel <= count($this->_modelNames)) {
1003: return $this->_modelNames[(int)$enteredModel - 1];
1004: }
1005:
1006: return $enteredModel;
1007: }
1008:
1009: 1010: 1011: 1012: 1013:
1014: public function getOptionParser() {
1015: $parser = parent::getOptionParser();
1016:
1017: $parser->description(
1018: __d('cake_console', 'Bake models.')
1019: )->addArgument('name', array(
1020: 'help' => __d('cake_console', 'Name of the model to bake. Can use Plugin.name to bake plugin models.')
1021: ))->addSubcommand('all', array(
1022: 'help' => __d('cake_console', 'Bake all model files with associations and validation.')
1023: ))->addOption('plugin', array(
1024: 'short' => 'p',
1025: 'help' => __d('cake_console', 'Plugin to bake the model into.')
1026: ))->addOption('theme', array(
1027: 'short' => 't',
1028: 'help' => __d('cake_console', 'Theme to use when baking code.')
1029: ))->addOption('connection', array(
1030: 'short' => 'c',
1031: 'help' => __d('cake_console', 'The connection the model table is on.')
1032: ))->addOption('force', array(
1033: 'short' => 'f',
1034: 'help' => __d('cake_console', 'Force overwriting existing files without prompting.')
1035: ))->epilog(
1036: __d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.')
1037: );
1038:
1039: return $parser;
1040: }
1041:
1042: 1043: 1044: 1045: 1046: 1047: 1048: 1049:
1050: public function bakeFixture($className, $useTable = null) {
1051: $this->Fixture->interactive = $this->interactive;
1052: $this->Fixture->connection = $this->connection;
1053: $this->Fixture->plugin = $this->plugin;
1054: $this->Fixture->bake($className, $useTable);
1055: }
1056:
1057: }
1058: