comparison cake/console/libs/tasks/model.php @ 0:261e66bd5a0c

hg init
author Shoshi TAMAKI <shoshi@cr.ie.u-ryukyu.ac.jp>
date Sun, 24 Jul 2011 21:08:31 +0900
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:261e66bd5a0c
1 <?php
2 /**
3 * The ModelTask handles creating and updating models files.
4 *
5 * PHP versions 4 and 5
6 *
7 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
8 * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
9 *
10 * Licensed under The MIT License
11 * Redistributions of files must retain the above copyright notice.
12 *
13 * @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
14 * @link http://cakephp.org CakePHP(tm) Project
15 * @package cake
16 * @subpackage cake.cake.console.libs.tasks
17 * @since CakePHP(tm) v 1.2
18 * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
19 */
20
21 include_once dirname(__FILE__) . DS . 'bake.php';
22
23 /**
24 * Task class for creating and updating model files.
25 *
26 * @package cake
27 * @subpackage cake.cake.console.libs.tasks
28 */
29 class ModelTask extends BakeTask {
30
31 /**
32 * path to MODELS directory
33 *
34 * @var string
35 * @access public
36 */
37 var $path = MODELS;
38
39 /**
40 * tasks
41 *
42 * @var array
43 * @access public
44 */
45 var $tasks = array('DbConfig', 'Fixture', 'Test', 'Template');
46
47 /**
48 * Tables to skip when running all()
49 *
50 * @var array
51 * @access protected
52 */
53 var $skipTables = array('i18n');
54
55 /**
56 * Holds tables found on connection.
57 *
58 * @var array
59 * @access protected
60 */
61 var $_tables = array();
62
63 /**
64 * Holds validation method map.
65 *
66 * @var array
67 * @access protected
68 */
69 var $_validations = array();
70
71 /**
72 * Execution method always used for tasks
73 *
74 * @access public
75 */
76 function execute() {
77 App::import('Model', 'Model', false);
78
79 if (empty($this->args)) {
80 $this->__interactive();
81 }
82
83 if (!empty($this->args[0])) {
84 $this->interactive = false;
85 if (!isset($this->connection)) {
86 $this->connection = 'default';
87 }
88 if (strtolower($this->args[0]) == 'all') {
89 return $this->all();
90 }
91 $model = $this->_modelName($this->args[0]);
92 $object = $this->_getModelObject($model);
93 if ($this->bake($object, false)) {
94 if ($this->_checkUnitTest()) {
95 $this->bakeFixture($model);
96 $this->bakeTest($model);
97 }
98 }
99 }
100 }
101
102 /**
103 * Bake all models at once.
104 *
105 * @return void
106 */
107 function all() {
108 $this->listAll($this->connection, false);
109 $unitTestExists = $this->_checkUnitTest();
110 foreach ($this->_tables as $table) {
111 if (in_array($table, $this->skipTables)) {
112 continue;
113 }
114 $modelClass = Inflector::classify($table);
115 $this->out(sprintf(__('Baking %s', true), $modelClass));
116 $object = $this->_getModelObject($modelClass);
117 if ($this->bake($object, false) && $unitTestExists) {
118 $this->bakeFixture($modelClass);
119 $this->bakeTest($modelClass);
120 }
121 }
122 }
123
124 /**
125 * Get a model object for a class name.
126 *
127 * @param string $className Name of class you want model to be.
128 * @return object Model instance
129 */
130 function &_getModelObject($className, $table = null) {
131 if (!$table) {
132 $table = Inflector::tableize($className);
133 }
134 $object =& new Model(array('name' => $className, 'table' => $table, 'ds' => $this->connection));
135 return $object;
136 }
137
138 /**
139 * Generate a key value list of options and a prompt.
140 *
141 * @param array $options Array of options to use for the selections. indexes must start at 0
142 * @param string $prompt Prompt to use for options list.
143 * @param integer $default The default option for the given prompt.
144 * @return result of user choice.
145 */
146 function inOptions($options, $prompt = null, $default = null) {
147 $valid = false;
148 $max = count($options);
149 while (!$valid) {
150 foreach ($options as $i => $option) {
151 $this->out($i + 1 .'. ' . $option);
152 }
153 if (empty($prompt)) {
154 $prompt = __('Make a selection from the choices above', true);
155 }
156 $choice = $this->in($prompt, null, $default);
157 if (intval($choice) > 0 && intval($choice) <= $max) {
158 $valid = true;
159 }
160 }
161 return $choice - 1;
162 }
163
164 /**
165 * Handles interactive baking
166 *
167 * @access private
168 */
169 function __interactive() {
170 $this->hr();
171 $this->out(sprintf("Bake Model\nPath: %s", $this->path));
172 $this->hr();
173 $this->interactive = true;
174
175 $primaryKey = 'id';
176 $validate = $associations = array();
177
178 if (empty($this->connection)) {
179 $this->connection = $this->DbConfig->getConfig();
180 }
181 $currentModelName = $this->getName();
182 $useTable = $this->getTable($currentModelName);
183 $db =& ConnectionManager::getDataSource($this->connection);
184 $fullTableName = $db->fullTableName($useTable);
185
186 if (in_array($useTable, $this->_tables)) {
187 $tempModel = new Model(array('name' => $currentModelName, 'table' => $useTable, 'ds' => $this->connection));
188 $fields = $tempModel->schema(true);
189 if (!array_key_exists('id', $fields)) {
190 $primaryKey = $this->findPrimaryKey($fields);
191 }
192 } else {
193 $this->err(sprintf(__('Table %s does not exist, cannot bake a model without a table.', true), $useTable));
194 $this->_stop();
195 return false;
196 }
197 $displayField = $tempModel->hasField(array('name', 'title'));
198 if (!$displayField) {
199 $displayField = $this->findDisplayField($tempModel->schema());
200 }
201
202 $prompt = __("Would you like to supply validation criteria \nfor the fields in your model?", true);
203 $wannaDoValidation = $this->in($prompt, array('y','n'), 'y');
204 if (array_search($useTable, $this->_tables) !== false && strtolower($wannaDoValidation) == 'y') {
205 $validate = $this->doValidation($tempModel);
206 }
207
208 $prompt = __("Would you like to define model associations\n(hasMany, hasOne, belongsTo, etc.)?", true);
209 $wannaDoAssoc = $this->in($prompt, array('y','n'), 'y');
210 if (strtolower($wannaDoAssoc) == 'y') {
211 $associations = $this->doAssociations($tempModel);
212 }
213
214 $this->out();
215 $this->hr();
216 $this->out(__('The following Model will be created:', true));
217 $this->hr();
218 $this->out("Name: " . $currentModelName);
219
220 if ($this->connection !== 'default') {
221 $this->out(sprintf(__("DB Config: %s", true), $this->connection));
222 }
223 if ($fullTableName !== Inflector::tableize($currentModelName)) {
224 $this->out(sprintf(__("DB Table: %s", true), $fullTableName));
225 }
226 if ($primaryKey != 'id') {
227 $this->out(sprintf(__("Primary Key: %s", true), $primaryKey));
228 }
229 if (!empty($validate)) {
230 $this->out(sprintf(__("Validation: %s", true), print_r($validate, true)));
231 }
232 if (!empty($associations)) {
233 $this->out(__("Associations:", true));
234 $assocKeys = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
235 foreach ($assocKeys as $assocKey) {
236 $this->_printAssociation($currentModelName, $assocKey, $associations);
237 }
238 }
239
240 $this->hr();
241 $looksGood = $this->in(__('Look okay?', true), array('y','n'), 'y');
242
243 if (strtolower($looksGood) == 'y') {
244 $vars = compact('associations', 'validate', 'primaryKey', 'useTable', 'displayField');
245 $vars['useDbConfig'] = $this->connection;
246 if ($this->bake($currentModelName, $vars)) {
247 if ($this->_checkUnitTest()) {
248 $this->bakeFixture($currentModelName, $useTable);
249 $this->bakeTest($currentModelName, $useTable, $associations);
250 }
251 }
252 } else {
253 return false;
254 }
255 }
256
257 /**
258 * Print out all the associations of a particular type
259 *
260 * @param string $modelName Name of the model relations belong to.
261 * @param string $type Name of association you want to see. i.e. 'belongsTo'
262 * @param string $associations Collection of associations.
263 * @access protected
264 * @return void
265 */
266 function _printAssociation($modelName, $type, $associations) {
267 if (!empty($associations[$type])) {
268 for ($i = 0; $i < count($associations[$type]); $i++) {
269 $out = "\t" . $modelName . ' ' . $type . ' ' . $associations[$type][$i]['alias'];
270 $this->out($out);
271 }
272 }
273 }
274
275 /**
276 * Finds a primary Key in a list of fields.
277 *
278 * @param array $fields Array of fields that might have a primary key.
279 * @return string Name of field that is a primary key.
280 * @access public
281 */
282 function findPrimaryKey($fields) {
283 foreach ($fields as $name => $field) {
284 if (isset($field['key']) && $field['key'] == 'primary') {
285 break;
286 }
287 }
288 return $this->in(__('What is the primaryKey?', true), null, $name);
289 }
290
291 /**
292 * interact with the user to find the displayField value for a model.
293 *
294 * @param array $fields Array of fields to look for and choose as a displayField
295 * @return mixed Name of field to use for displayField or false if the user declines to choose
296 */
297 function findDisplayField($fields) {
298 $fieldNames = array_keys($fields);
299 $prompt = __("A displayField could not be automatically detected\nwould you like to choose one?", true);
300 $continue = $this->in($prompt, array('y', 'n'));
301 if (strtolower($continue) == 'n') {
302 return false;
303 }
304 $prompt = __('Choose a field from the options above:', true);
305 $choice = $this->inOptions($fieldNames, $prompt);
306 return $fieldNames[$choice];
307 }
308
309 /**
310 * Handles Generation and user interaction for creating validation.
311 *
312 * @param object $model Model to have validations generated for.
313 * @return array $validate Array of user selected validations.
314 * @access public
315 */
316 function doValidation(&$model) {
317 if (!is_object($model)) {
318 return false;
319 }
320 $fields = $model->schema();
321
322 if (empty($fields)) {
323 return false;
324 }
325 $validate = array();
326 $this->initValidations();
327 foreach ($fields as $fieldName => $field) {
328 $validation = $this->fieldValidation($fieldName, $field, $model->primaryKey);
329 if (!empty($validation)) {
330 $validate[$fieldName] = $validation;
331 }
332 }
333 return $validate;
334 }
335
336 /**
337 * Populate the _validations array
338 *
339 * @return void
340 */
341 function initValidations() {
342 $options = $choices = array();
343 if (class_exists('Validation')) {
344 $parent = get_class_methods(get_parent_class('Validation'));
345 $options = get_class_methods('Validation');
346 $options = array_diff($options, $parent);
347 }
348 sort($options);
349 $default = 1;
350 foreach ($options as $key => $option) {
351 if ($option{0} != '_' && strtolower($option) != 'getinstance') {
352 $choices[$default] = strtolower($option);
353 $default++;
354 }
355 }
356 $choices[$default] = 'none'; // Needed since index starts at 1
357 $this->_validations = $choices;
358 return $choices;
359 }
360
361 /**
362 * Does individual field validation handling.
363 *
364 * @param string $fieldName Name of field to be validated.
365 * @param array $metaData metadata for field
366 * @return array Array of validation for the field.
367 */
368 function fieldValidation($fieldName, $metaData, $primaryKey = 'id') {
369 $defaultChoice = count($this->_validations);
370 $validate = $alreadyChosen = array();
371
372 $anotherValidator = 'y';
373 while ($anotherValidator == 'y') {
374 if ($this->interactive) {
375 $this->out();
376 $this->out(sprintf(__('Field: %s', true), $fieldName));
377 $this->out(sprintf(__('Type: %s', true), $metaData['type']));
378 $this->hr();
379 $this->out(__('Please select one of the following validation options:', true));
380 $this->hr();
381 }
382
383 $prompt = '';
384 for ($i = 1; $i < $defaultChoice; $i++) {
385 $prompt .= $i . ' - ' . $this->_validations[$i] . "\n";
386 }
387 $prompt .= sprintf(__("%s - Do not do any validation on this field.\n", true), $defaultChoice);
388 $prompt .= __("... or enter in a valid regex validation string.\n", true);
389
390 $methods = array_flip($this->_validations);
391 $guess = $defaultChoice;
392 if ($metaData['null'] != 1 && !in_array($fieldName, array($primaryKey, 'created', 'modified', 'updated'))) {
393 if ($fieldName == 'email') {
394 $guess = $methods['email'];
395 } elseif ($metaData['type'] == 'string' && $metaData['length'] == 36) {
396 $guess = $methods['uuid'];
397 } elseif ($metaData['type'] == 'string') {
398 $guess = $methods['notempty'];
399 } elseif ($metaData['type'] == 'integer') {
400 $guess = $methods['numeric'];
401 } elseif ($metaData['type'] == 'boolean') {
402 $guess = $methods['boolean'];
403 } elseif ($metaData['type'] == 'date') {
404 $guess = $methods['date'];
405 } elseif ($metaData['type'] == 'time') {
406 $guess = $methods['time'];
407 }
408 }
409
410 if ($this->interactive === true) {
411 $choice = $this->in($prompt, null, $guess);
412 if (in_array($choice, $alreadyChosen)) {
413 $this->out(__("You have already chosen that validation rule,\nplease choose again", true));
414 continue;
415 }
416 if (!isset($this->_validations[$choice]) && is_numeric($choice)) {
417 $this->out(__('Please make a valid selection.', true));
418 continue;
419 }
420 $alreadyChosen[] = $choice;
421 } else {
422 $choice = $guess;
423 }
424
425 if (isset($this->_validations[$choice])) {
426 $validatorName = $this->_validations[$choice];
427 } else {
428 $validatorName = Inflector::slug($choice);
429 }
430
431 if ($choice != $defaultChoice) {
432 if (is_numeric($choice) && isset($this->_validations[$choice])) {
433 $validate[$validatorName] = $this->_validations[$choice];
434 } else {
435 $validate[$validatorName] = $choice;
436 }
437 }
438 if ($this->interactive == true && $choice != $defaultChoice) {
439 $anotherValidator = $this->in(__('Would you like to add another validation rule?', true), array('y', 'n'), 'n');
440 } else {
441 $anotherValidator = 'n';
442 }
443 }
444 return $validate;
445 }
446
447 /**
448 * Handles associations
449 *
450 * @param object $model
451 * @return array $assocaitons
452 * @access public
453 */
454 function doAssociations(&$model) {
455 if (!is_object($model)) {
456 return false;
457 }
458 if ($this->interactive === true) {
459 $this->out(__('One moment while the associations are detected.', true));
460 }
461
462 $fields = $model->schema(true);
463 if (empty($fields)) {
464 return false;
465 }
466
467 if (empty($this->_tables)) {
468 $this->_tables = $this->getAllTables();
469 }
470
471 $associations = array(
472 'belongsTo' => array(), 'hasMany' => array(), 'hasOne'=> array(), 'hasAndBelongsToMany' => array()
473 );
474 $possibleKeys = array();
475
476 $associations = $this->findBelongsTo($model, $associations);
477 $associations = $this->findHasOneAndMany($model, $associations);
478 $associations = $this->findHasAndBelongsToMany($model, $associations);
479
480 if ($this->interactive !== true) {
481 unset($associations['hasOne']);
482 }
483
484 if ($this->interactive === true) {
485 $this->hr();
486 if (empty($associations)) {
487 $this->out(__('None found.', true));
488 } else {
489 $this->out(__('Please confirm the following associations:', true));
490 $this->hr();
491 $associations = $this->confirmAssociations($model, $associations);
492 }
493 $associations = $this->doMoreAssociations($model, $associations);
494 }
495 return $associations;
496 }
497
498 /**
499 * Find belongsTo relations and add them to the associations list.
500 *
501 * @param object $model Model instance of model being generated.
502 * @param array $associations Array of inprogress associations
503 * @return array $associations with belongsTo added in.
504 */
505 function findBelongsTo(&$model, $associations) {
506 $fields = $model->schema(true);
507 foreach ($fields as $fieldName => $field) {
508 $offset = strpos($fieldName, '_id');
509 if ($fieldName != $model->primaryKey && $fieldName != 'parent_id' && $offset !== false) {
510 $tmpModelName = $this->_modelNameFromKey($fieldName);
511 $associations['belongsTo'][] = array(
512 'alias' => $tmpModelName,
513 'className' => $tmpModelName,
514 'foreignKey' => $fieldName,
515 );
516 } elseif ($fieldName == 'parent_id') {
517 $associations['belongsTo'][] = array(
518 'alias' => 'Parent' . $model->name,
519 'className' => $model->name,
520 'foreignKey' => $fieldName,
521 );
522 }
523 }
524 return $associations;
525 }
526
527 /**
528 * Find the hasOne and HasMany relations and add them to associations list
529 *
530 * @param object $model Model instance being generated
531 * @param array $associations Array of inprogress associations
532 * @return array $associations with hasOne and hasMany added in.
533 */
534 function findHasOneAndMany(&$model, $associations) {
535 $foreignKey = $this->_modelKey($model->name);
536 foreach ($this->_tables as $otherTable) {
537 $tempOtherModel = $this->_getModelObject($this->_modelName($otherTable), $otherTable);
538 $modelFieldsTemp = $tempOtherModel->schema(true);
539
540 $pattern = '/_' . preg_quote($model->table, '/') . '|' . preg_quote($model->table, '/') . '_/';
541 $possibleJoinTable = preg_match($pattern , $otherTable);
542 if ($possibleJoinTable == true) {
543 continue;
544 }
545 foreach ($modelFieldsTemp as $fieldName => $field) {
546 $assoc = false;
547 if ($fieldName != $model->primaryKey && $fieldName == $foreignKey) {
548 $assoc = array(
549 'alias' => $tempOtherModel->name,
550 'className' => $tempOtherModel->name,
551 'foreignKey' => $fieldName
552 );
553 } elseif ($otherTable == $model->table && $fieldName == 'parent_id') {
554 $assoc = array(
555 'alias' => 'Child' . $model->name,
556 'className' => $model->name,
557 'foreignKey' => $fieldName
558 );
559 }
560 if ($assoc) {
561 $associations['hasOne'][] = $assoc;
562 $associations['hasMany'][] = $assoc;
563 }
564
565 }
566 }
567 return $associations;
568 }
569
570 /**
571 * Find the hasAndBelongsToMany relations and add them to associations list
572 *
573 * @param object $model Model instance being generated
574 * @param array $associations Array of inprogress associations
575 * @return array $associations with hasAndBelongsToMany added in.
576 */
577 function findHasAndBelongsToMany(&$model, $associations) {
578 $foreignKey = $this->_modelKey($model->name);
579 foreach ($this->_tables as $otherTable) {
580 $tempOtherModel = $this->_getModelObject($this->_modelName($otherTable), $otherTable);
581 $modelFieldsTemp = $tempOtherModel->schema(true);
582
583 $offset = strpos($otherTable, $model->table . '_');
584 $otherOffset = strpos($otherTable, '_' . $model->table);
585
586 if ($offset !== false) {
587 $offset = strlen($model->table . '_');
588 $habtmName = $this->_modelName(substr($otherTable, $offset));
589 $associations['hasAndBelongsToMany'][] = array(
590 'alias' => $habtmName,
591 'className' => $habtmName,
592 'foreignKey' => $foreignKey,
593 'associationForeignKey' => $this->_modelKey($habtmName),
594 'joinTable' => $otherTable
595 );
596 } elseif ($otherOffset !== false) {
597 $habtmName = $this->_modelName(substr($otherTable, 0, $otherOffset));
598 $associations['hasAndBelongsToMany'][] = array(
599 'alias' => $habtmName,
600 'className' => $habtmName,
601 'foreignKey' => $foreignKey,
602 'associationForeignKey' => $this->_modelKey($habtmName),
603 'joinTable' => $otherTable
604 );
605 }
606 }
607 return $associations;
608 }
609
610 /**
611 * Interact with the user and confirm associations.
612 *
613 * @param array $model Temporary Model instance.
614 * @param array $associations Array of associations to be confirmed.
615 * @return array Array of confirmed associations
616 */
617 function confirmAssociations(&$model, $associations) {
618 foreach ($associations as $type => $settings) {
619 if (!empty($associations[$type])) {
620 $count = count($associations[$type]);
621 $response = 'y';
622 foreach ($associations[$type] as $i => $assoc) {
623 $prompt = "{$model->name} {$type} {$assoc['alias']}?";
624 $response = $this->in($prompt, array('y','n'), 'y');
625
626 if ('n' == strtolower($response)) {
627 unset($associations[$type][$i]);
628 } elseif ($type == 'hasMany') {
629 unset($associations['hasOne'][$i]);
630 }
631 }
632 $associations[$type] = array_merge($associations[$type]);
633 }
634 }
635 return $associations;
636 }
637
638 /**
639 * Interact with the user and generate additional non-conventional associations
640 *
641 * @param object $model Temporary model instance
642 * @param array $associations Array of associations.
643 * @return array Array of associations.
644 */
645 function doMoreAssociations($model, $associations) {
646 $prompt = __('Would you like to define some additional model associations?', true);
647 $wannaDoMoreAssoc = $this->in($prompt, array('y','n'), 'n');
648 $possibleKeys = $this->_generatePossibleKeys();
649 while (strtolower($wannaDoMoreAssoc) == 'y') {
650 $assocs = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
651 $this->out(__('What is the association type?', true));
652 $assocType = intval($this->inOptions($assocs, __('Enter a number',true)));
653
654 $this->out(__("For the following options be very careful to match your setup exactly.\nAny spelling mistakes will cause errors.", true));
655 $this->hr();
656
657 $alias = $this->in(__('What is the alias for this association?', true));
658 $className = $this->in(sprintf(__('What className will %s use?', true), $alias), null, $alias );
659 $suggestedForeignKey = null;
660
661 if ($assocType == 0) {
662 $showKeys = $possibleKeys[$model->table];
663 $suggestedForeignKey = $this->_modelKey($alias);
664 } else {
665 $otherTable = Inflector::tableize($className);
666 if (in_array($otherTable, $this->_tables)) {
667 if ($assocType < 3) {
668 $showKeys = $possibleKeys[$otherTable];
669 } else {
670 $showKeys = null;
671 }
672 } else {
673 $otherTable = $this->in(__('What is the table for this model?', true));
674 $showKeys = $possibleKeys[$otherTable];
675 }
676 $suggestedForeignKey = $this->_modelKey($model->name);
677 }
678 if (!empty($showKeys)) {
679 $this->out(__('A helpful List of possible keys', true));
680 $foreignKey = $this->inOptions($showKeys, __('What is the foreignKey?', true));
681 $foreignKey = $showKeys[intval($foreignKey)];
682 }
683 if (!isset($foreignKey)) {
684 $foreignKey = $this->in(__('What is the foreignKey? Specify your own.', true), null, $suggestedForeignKey);
685 }
686 if ($assocType == 3) {
687 $associationForeignKey = $this->in(__('What is the associationForeignKey?', true), null, $this->_modelKey($model->name));
688 $joinTable = $this->in(__('What is the joinTable?', true));
689 }
690 $associations[$assocs[$assocType]] = array_values((array)$associations[$assocs[$assocType]]);
691 $count = count($associations[$assocs[$assocType]]);
692 $i = ($count > 0) ? $count : 0;
693 $associations[$assocs[$assocType]][$i]['alias'] = $alias;
694 $associations[$assocs[$assocType]][$i]['className'] = $className;
695 $associations[$assocs[$assocType]][$i]['foreignKey'] = $foreignKey;
696 if ($assocType == 3) {
697 $associations[$assocs[$assocType]][$i]['associationForeignKey'] = $associationForeignKey;
698 $associations[$assocs[$assocType]][$i]['joinTable'] = $joinTable;
699 }
700 $wannaDoMoreAssoc = $this->in(__('Define another association?', true), array('y','n'), 'y');
701 }
702 return $associations;
703 }
704
705 /**
706 * Finds all possible keys to use on custom associations.
707 *
708 * @return array array of tables and possible keys
709 */
710 function _generatePossibleKeys() {
711 $possible = array();
712 foreach ($this->_tables as $otherTable) {
713 $tempOtherModel = & new Model(array('table' => $otherTable, 'ds' => $this->connection));
714 $modelFieldsTemp = $tempOtherModel->schema(true);
715 foreach ($modelFieldsTemp as $fieldName => $field) {
716 if ($field['type'] == 'integer' || $field['type'] == 'string') {
717 $possible[$otherTable][] = $fieldName;
718 }
719 }
720 }
721 return $possible;
722 }
723
724 /**
725 * Assembles and writes a Model file.
726 *
727 * @param mixed $name Model name or object
728 * @param mixed $data if array and $name is not an object assume bake data, otherwise boolean.
729 * @access private
730 */
731 function bake($name, $data = array()) {
732 if (is_object($name)) {
733 if ($data == false) {
734 $data = $associations = array();
735 $data['associations'] = $this->doAssociations($name, $associations);
736 $data['validate'] = $this->doValidation($name);
737 }
738 $data['primaryKey'] = $name->primaryKey;
739 $data['useTable'] = $name->table;
740 $data['useDbConfig'] = $name->useDbConfig;
741 $data['name'] = $name = $name->name;
742 } else {
743 $data['name'] = $name;
744 }
745 $defaults = array('associations' => array(), 'validate' => array(), 'primaryKey' => 'id',
746 'useTable' => null, 'useDbConfig' => 'default', 'displayField' => null);
747 $data = array_merge($defaults, $data);
748
749 $this->Template->set($data);
750 $this->Template->set('plugin', Inflector::camelize($this->plugin));
751 $out = $this->Template->generate('classes', 'model');
752
753 $path = $this->getPath();
754 $filename = $path . Inflector::underscore($name) . '.php';
755 $this->out("\nBaking model class for $name...");
756 $this->createFile($filename, $out);
757 ClassRegistry::flush();
758 return $out;
759 }
760
761 /**
762 * Assembles and writes a unit test file
763 *
764 * @param string $className Model class name
765 * @access private
766 */
767 function bakeTest($className) {
768 $this->Test->interactive = $this->interactive;
769 $this->Test->plugin = $this->plugin;
770 $this->Test->connection = $this->connection;
771 return $this->Test->bake('Model', $className);
772 }
773
774 /**
775 * outputs the a list of possible models or controllers from database
776 *
777 * @param string $useDbConfig Database configuration name
778 * @access public
779 */
780 function listAll($useDbConfig = null) {
781 $this->_tables = $this->getAllTables($useDbConfig);
782
783 if ($this->interactive === true) {
784 $this->out(__('Possible Models based on your current database:', true));
785 $this->_modelNames = array();
786 $count = count($this->_tables);
787 for ($i = 0; $i < $count; $i++) {
788 $this->_modelNames[] = $this->_modelName($this->_tables[$i]);
789 $this->out($i + 1 . ". " . $this->_modelNames[$i]);
790 }
791 }
792 return $this->_tables;
793 }
794
795 /**
796 * Interact with the user to determine the table name of a particular model
797 *
798 * @param string $modelName Name of the model you want a table for.
799 * @param string $useDbConfig Name of the database config you want to get tables from.
800 * @return void
801 */
802 function getTable($modelName, $useDbConfig = null) {
803 if (!isset($useDbConfig)) {
804 $useDbConfig = $this->connection;
805 }
806 App::import('Model', 'ConnectionManager', false);
807
808 $db =& ConnectionManager::getDataSource($useDbConfig);
809 $useTable = Inflector::tableize($modelName);
810 $fullTableName = $db->fullTableName($useTable, false);
811 $tableIsGood = false;
812
813 if (array_search($useTable, $this->_tables) === false) {
814 $this->out();
815 $this->out(sprintf(__("Given your model named '%s',\nCake would expect a database table named '%s'", true), $modelName, $fullTableName));
816 $tableIsGood = $this->in(__('Do you want to use this table?', true), array('y','n'), 'y');
817 }
818 if (strtolower($tableIsGood) == 'n') {
819 $useTable = $this->in(__('What is the name of the table?', true));
820 }
821 return $useTable;
822 }
823
824 /**
825 * Get an Array of all the tables in the supplied connection
826 * will halt the script if no tables are found.
827 *
828 * @param string $useDbConfig Connection name to scan.
829 * @return array Array of tables in the database.
830 */
831 function getAllTables($useDbConfig = null) {
832 if (!isset($useDbConfig)) {
833 $useDbConfig = $this->connection;
834 }
835 App::import('Model', 'ConnectionManager', false);
836
837 $tables = array();
838 $db =& ConnectionManager::getDataSource($useDbConfig);
839 $db->cacheSources = false;
840 $usePrefix = empty($db->config['prefix']) ? '' : $db->config['prefix'];
841 if ($usePrefix) {
842 foreach ($db->listSources() as $table) {
843 if (!strncmp($table, $usePrefix, strlen($usePrefix))) {
844 $tables[] = substr($table, strlen($usePrefix));
845 }
846 }
847 } else {
848 $tables = $db->listSources();
849 }
850 if (empty($tables)) {
851 $this->err(__('Your database does not have any tables.', true));
852 $this->_stop();
853 }
854 return $tables;
855 }
856
857 /**
858 * Forces the user to specify the model he wants to bake, and returns the selected model name.
859 *
860 * @return string the model name
861 * @access public
862 */
863 function getName($useDbConfig = null) {
864 $this->listAll($useDbConfig);
865
866 $enteredModel = '';
867
868 while ($enteredModel == '') {
869 $enteredModel = $this->in(__("Enter a number from the list above,\ntype in the name of another model, or 'q' to exit", true), null, 'q');
870
871 if ($enteredModel === 'q') {
872 $this->out(__("Exit", true));
873 $this->_stop();
874 }
875
876 if ($enteredModel == '' || intval($enteredModel) > count($this->_modelNames)) {
877 $this->err(__("The model name you supplied was empty,\nor the number you selected was not an option. Please try again.", true));
878 $enteredModel = '';
879 }
880 }
881 if (intval($enteredModel) > 0 && intval($enteredModel) <= count($this->_modelNames)) {
882 $currentModelName = $this->_modelNames[intval($enteredModel) - 1];
883 } else {
884 $currentModelName = $enteredModel;
885 }
886 return $currentModelName;
887 }
888
889 /**
890 * Displays help contents
891 *
892 * @access public
893 */
894 function help() {
895 $this->hr();
896 $this->out("Usage: cake bake model <arg1>");
897 $this->hr();
898 $this->out('Arguments:');
899 $this->out();
900 $this->out("<name>");
901 $this->out("\tName of the model to bake. Can use Plugin.name");
902 $this->out("\tas a shortcut for plugin baking.");
903 $this->out();
904 $this->out('Params:');
905 $this->out();
906 $this->out('-connection <config>');
907 $this->out("\tset db config <config>. uses 'default' if none is specified");
908 $this->out();
909 $this->out('Commands:');
910 $this->out();
911 $this->out("model");
912 $this->out("\tbakes model in interactive mode.");
913 $this->out();
914 $this->out("model <name>");
915 $this->out("\tbakes model file with no associations or validation");
916 $this->out();
917 $this->out("model all");
918 $this->out("\tbakes all model files with associations and validation");
919 $this->out();
920 $this->_stop();
921 }
922
923 /**
924 * Interact with FixtureTask to automatically bake fixtures when baking models.
925 *
926 * @param string $className Name of class to bake fixture for
927 * @param string $useTable Optional table name for fixture to use.
928 * @access public
929 * @return void
930 * @see FixtureTask::bake
931 */
932 function bakeFixture($className, $useTable = null) {
933 $this->Fixture->interactive = $this->interactive;
934 $this->Fixture->connection = $this->connection;
935 $this->Fixture->plugin = $this->plugin;
936 $this->Fixture->bake($className, $useTable);
937 }
938 }