source: Dev/branches/cakephp/cake/console/libs/tasks/model.php @ 169

Last change on this file since 169 was 126, checked in by fpvanagthoven, 14 years ago

Cakephp branch.

File size: 28.7 KB
Line 
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-2011, 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-2011, 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
21include_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 */
29class 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}
Note: See TracBrowser for help on using the repository browser.