CakePHP
  • Documentation
    • Book
    • API
    • Videos
    • Reporting Security Issues
    • Privacy Policy
    • Logos & Trademarks
  • Business Solutions
  • Swag
  • Road Trip
  • Team
  • Community
    • Community
    • Get Involved
    • Issues (GitHub)
    • Bakery
    • Featured Resources
    • Training
    • Meetups
    • My CakePHP
    • CakeFest
    • Newsletter
    • Linkedin
    • YouTube
    • Facebook
    • Twitter
    • Mastodon
    • Help & Support
    • Forum
    • Stack Overflow
    • Slack
    • Paid Support
CakePHP

C CakePHP 2.1 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.1
      • 4.2
      • 4.1
      • 4.0
      • 3.9
      • 3.8
      • 3.7
      • 3.6
      • 3.5
      • 3.4
      • 3.3
      • 3.2
      • 3.1
      • 3.0
      • 2.10
      • 2.9
      • 2.8
      • 2.7
      • 2.6
      • 2.5
      • 2.4
      • 2.3
      • 2.2
      • 2.1
      • 2.0
      • 1.3
      • 1.2

Packages

  • Cake
    • Cache
      • Engine
    • Configure
    • Console
      • Command
        • Task
    • Controller
      • Component
        • Acl
        • Auth
    • Core
    • Error
    • Event
    • I18n
    • Log
      • Engine
    • Model
      • Behavior
      • Datasource
        • Database
        • Session
    • Network
      • Email
      • Http
    • Routing
      • Route
    • TestSuite
      • Coverage
      • Fixture
      • Reporter
    • Utility
    • View
      • Helper

Classes

  • BakeTask
  • ControllerTask
  • DbConfigTask
  • ExtractTask
  • FixtureTask
  • ModelTask
  • PluginTask
  • ProjectTask
  • TemplateTask
  • TestTask
  • ViewTask
  1: <?php
  2: /**
  3:  * The ModelTask handles creating and updating models files.
  4:  *
  5:  * PHP 5
  6:  *
  7:  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  8:  * Copyright 2005-2012, 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-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
 14:  * @link          http://cakephp.org CakePHP(tm) Project
 15:  * @since         CakePHP(tm) v 1.2
 16:  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
 17:  */
 18: 
 19: App::uses('AppShell', 'Console/Command');
 20: App::uses('BakeTask', 'Console/Command/Task');
 21: App::uses('ConnectionManager', 'Model');
 22: App::uses('Model', 'Model');
 23: App::uses('Validation', 'Utility');
 24: 
 25: /**
 26:  * Task class for creating and updating model files.
 27:  *
 28:  * @package    Cake.Console.Command.Task
 29:  */
 30: class ModelTask extends BakeTask {
 31: 
 32: /**
 33:  * path to Model directory
 34:  *
 35:  * @var string
 36:  */
 37:     public $path = null;
 38: 
 39: /**
 40:  * tasks
 41:  *
 42:  * @var array
 43:  */
 44:     public $tasks = array('DbConfig', 'Fixture', 'Test', 'Template');
 45: 
 46: /**
 47:  * Tables to skip when running all()
 48:  *
 49:  * @var array
 50:  */
 51:     public $skipTables = array('i18n');
 52: 
 53: /**
 54:  * Holds tables found on connection.
 55:  *
 56:  * @var array
 57:  */
 58:     protected $_tables = array();
 59: 
 60: /**
 61:  * Holds the model names
 62:  *
 63:  * @var array
 64:  */
 65:     protected $_modelNames = array();
 66: 
 67: /**
 68:  * Holds validation method map.
 69:  *
 70:  * @var array
 71:  */
 72:     protected $_validations = array();
 73: 
 74: /**
 75:  * Override initialize
 76:  *
 77:  * @return void
 78:  */
 79:     public function initialize() {
 80:         $this->path = current(App::path('Model'));
 81:     }
 82: 
 83: /**
 84:  * Execution method always used for tasks
 85:  *
 86:  * @return void
 87:  */
 88:     public function execute() {
 89:         parent::execute();
 90: 
 91:         if (empty($this->args)) {
 92:             $this->_interactive();
 93:         }
 94: 
 95:         if (!empty($this->args[0])) {
 96:             $this->interactive = false;
 97:             if (!isset($this->connection)) {
 98:                 $this->connection = 'default';
 99:             }
100:             if (strtolower($this->args[0]) == 'all') {
101:                 return $this->all();
102:             }
103:             $model = $this->_modelName($this->args[0]);
104:             $this->listAll($this->connection);
105:             $useTable = $this->getTable($model);
106:             $object = $this->_getModelObject($model, $useTable);
107:             if ($this->bake($object, false)) {
108:                 if ($this->_checkUnitTest()) {
109:                     $this->bakeFixture($model, $useTable);
110:                     $this->bakeTest($model);
111:                 }
112:             }
113:         }
114:     }
115: 
116: /**
117:  * Bake all models at once.
118:  *
119:  * @return void
120:  */
121:     public function all() {
122:         $this->listAll($this->connection, false);
123:         $unitTestExists = $this->_checkUnitTest();
124:         foreach ($this->_tables as $table) {
125:             if (in_array($table, $this->skipTables)) {
126:                 continue;
127:             }
128:             $modelClass = Inflector::classify($table);
129:             $this->out(__d('cake_console', 'Baking %s', $modelClass));
130:             $object = $this->_getModelObject($modelClass, $table);
131:             if ($this->bake($object, false) && $unitTestExists) {
132:                 $this->bakeFixture($modelClass, $table);
133:                 $this->bakeTest($modelClass);
134:             }
135:         }
136:     }
137: 
138: /**
139:  * Get a model object for a class name.
140:  *
141:  * @param string $className Name of class you want model to be.
142:  * @param string $table Table name
143:  * @return Model Model instance
144:  */
145:     protected function _getModelObject($className, $table = null) {
146:         if (!$table) {
147:             $table = Inflector::tableize($className);
148:         }
149:         $object = new Model(array('name' => $className, 'table' => $table, 'ds' => $this->connection));
150:         $fields = $object->schema(true);
151:         foreach ($fields as $name => $field) {
152:             if (isset($field['key']) && $field['key'] == 'primary') {
153:                 $object->primaryKey = $name;
154:                 break;
155:             }
156:         }
157:         return $object;
158:     }
159: 
160: /**
161:  * Generate a key value list of options and a prompt.
162:  *
163:  * @param array $options Array of options to use for the selections. indexes must start at 0
164:  * @param string $prompt Prompt to use for options list.
165:  * @param integer $default The default option for the given prompt.
166:  * @return integer result of user choice.
167:  */
168:     public function inOptions($options, $prompt = null, $default = null) {
169:         $valid = false;
170:         $max = count($options);
171:         while (!$valid) {
172:             foreach ($options as $i => $option) {
173:                 $this->out($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 (intval($choice) > 0 && intval($choice) <= $max) {
180:                 $valid = true;
181:             }
182:         }
183:         return $choice - 1;
184:     }
185: 
186: /**
187:  * Handles interactive baking
188:  *
189:  * @return boolean
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: 
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:  * Print out all the associations of a particular type
292:  *
293:  * @param string $modelName Name of the model relations belong to.
294:  * @param string $type Name of association you want to see. i.e. 'belongsTo'
295:  * @param string $associations Collection of associations.
296:  * @return void
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:  * Finds a primary Key in a list of fields.
309:  *
310:  * @param array $fields Array of fields that might have a primary key.
311:  * @return string Name of field that is a primary key.
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:  * interact with the user to find the displayField value for a model.
325:  *
326:  * @param array $fields Array of fields to look for and choose as a displayField
327:  * @return mixed Name of field to use for displayField or false if the user declines to choose
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:  * Handles Generation and user interaction for creating validation.
343:  *
344:  * @param Model $model Model to have validations generated for.
345:  * @return array $validate Array of user selected validations.
346:  */
347:     public function doValidation($model) {
348:         if (!is_object($model)) {
349:             return false;
350:         }
351:         $fields = $model->schema();
352: 
353:         if (empty($fields)) {
354:             return false;
355:         }
356:         $validate = array();
357:         $this->initValidations();
358:         foreach ($fields as $fieldName => $field) {
359:             $validation = $this->fieldValidation($fieldName, $field, $model->primaryKey);
360:             if (!empty($validation)) {
361:                 $validate[$fieldName] = $validation;
362:             }
363:         }
364:         return $validate;
365:     }
366: 
367: /**
368:  * Populate the _validations array
369:  *
370:  * @return void
371:  */
372:     public function initValidations() {
373:         $options = $choices = array();
374:         if (class_exists('Validation')) {
375:             $options = get_class_methods('Validation');
376:         }
377:         sort($options);
378:         $default = 1;
379:         foreach ($options as $key => $option) {
380:             if ($option{0} != '_') {
381:                 $choices[$default] = strtolower($option);
382:                 $default++;
383:             }
384:         }
385:         $choices[$default] = 'none'; // Needed since index starts at 1
386:         $this->_validations = $choices;
387:         return $choices;
388:     }
389: 
390: /**
391:  * Does individual field validation handling.
392:  *
393:  * @param string $fieldName Name of field to be validated.
394:  * @param array $metaData metadata for field
395:  * @param string $primaryKey
396:  * @return array Array of validation for the field.
397:  */
398:     public function fieldValidation($fieldName, $metaData, $primaryKey = 'id') {
399:         $defaultChoice = count($this->_validations);
400:         $validate = $alreadyChosen = array();
401: 
402:         $anotherValidator = 'y';
403:         while ($anotherValidator == 'y') {
404:             if ($this->interactive) {
405:                 $this->out();
406:                 $this->out(__d('cake_console', 'Field: %s', $fieldName));
407:                 $this->out(__d('cake_console', 'Type: %s', $metaData['type']));
408:                 $this->hr();
409:                 $this->out(__d('cake_console', 'Please select one of the following validation options:'));
410:                 $this->hr();
411:             }
412: 
413:             $prompt = '';
414:             for ($i = 1; $i < $defaultChoice; $i++) {
415:                 $prompt .= $i . ' - ' . $this->_validations[$i] . "\n";
416:             }
417:             $prompt .= __d('cake_console', "%s - Do not do any validation on this field.\n", $defaultChoice);
418:             $prompt .= __d('cake_console', "... or enter in a valid regex validation string.\n");
419: 
420:             $methods = array_flip($this->_validations);
421:             $guess = $defaultChoice;
422:             if ($metaData['null'] != 1 && !in_array($fieldName, array($primaryKey, 'created', 'modified', 'updated'))) {
423:                 if ($fieldName == 'email') {
424:                     $guess = $methods['email'];
425:                 } elseif ($metaData['type'] == 'string' && $metaData['length'] == 36) {
426:                     $guess = $methods['uuid'];
427:                 } elseif ($metaData['type'] == 'string') {
428:                     $guess = $methods['notempty'];
429:                 } elseif ($metaData['type'] == 'text') {
430:                     $guess = $methods['notempty'];
431:                 } elseif ($metaData['type'] == 'integer') {
432:                     $guess = $methods['numeric'];
433:                 } elseif ($metaData['type'] == 'boolean') {
434:                     $guess = $methods['boolean'];
435:                 } elseif ($metaData['type'] == 'date') {
436:                     $guess = $methods['date'];
437:                 } elseif ($metaData['type'] == 'time') {
438:                     $guess = $methods['time'];
439:                 } elseif ($metaData['type'] == 'inet') {
440:                     $guess = $methods['ip'];
441:                 }
442:             }
443: 
444:             if ($this->interactive === true) {
445:                 $choice = $this->in($prompt, null, $guess);
446:                 if (in_array($choice, $alreadyChosen)) {
447:                     $this->out(__d('cake_console', "You have already chosen that validation rule,\nplease choose again"));
448:                     continue;
449:                 }
450:                 if (!isset($this->_validations[$choice]) && is_numeric($choice)) {
451:                     $this->out(__d('cake_console', 'Please make a valid selection.'));
452:                     continue;
453:                 }
454:                 $alreadyChosen[] = $choice;
455:             } else {
456:                 $choice = $guess;
457:             }
458: 
459:             if (isset($this->_validations[$choice])) {
460:                 $validatorName = $this->_validations[$choice];
461:             } else {
462:                 $validatorName = Inflector::slug($choice);
463:             }
464: 
465:             if ($choice != $defaultChoice) {
466:                 if (is_numeric($choice) && isset($this->_validations[$choice])) {
467:                     $validate[$validatorName] = $this->_validations[$choice];
468:                 } else {
469:                     $validate[$validatorName] = $choice;
470:                 }
471:             }
472:             if ($this->interactive == true && $choice != $defaultChoice) {
473:                 $anotherValidator = $this->in(__d('cake_console', 'Would you like to add another validation rule?'), array('y', 'n'), 'n');
474:             } else {
475:                 $anotherValidator = 'n';
476:             }
477:         }
478:         return $validate;
479:     }
480: 
481: /**
482:  * Handles associations
483:  *
484:  * @param Model $model
485:  * @return array $associations
486:  */
487:     public function doAssociations($model) {
488:         if (!is_object($model)) {
489:             return false;
490:         }
491:         if ($this->interactive === true) {
492:             $this->out(__d('cake_console', 'One moment while the associations are detected.'));
493:         }
494: 
495:         $fields = $model->schema(true);
496:         if (empty($fields)) {
497:             return array();
498:         }
499: 
500:         if (empty($this->_tables)) {
501:             $this->_tables = (array)$this->getAllTables();
502:         }
503: 
504:         $associations = array(
505:             'belongsTo' => array(),
506:             'hasMany' => array(),
507:             'hasOne' => array(),
508:             'hasAndBelongsToMany' => array()
509:         );
510: 
511:         $associations = $this->findBelongsTo($model, $associations);
512:         $associations = $this->findHasOneAndMany($model, $associations);
513:         $associations = $this->findHasAndBelongsToMany($model, $associations);
514: 
515:         if ($this->interactive !== true) {
516:             unset($associations['hasOne']);
517:         }
518: 
519:         if ($this->interactive === true) {
520:             $this->hr();
521:             if (empty($associations)) {
522:                 $this->out(__d('cake_console', 'None found.'));
523:             } else {
524:                 $this->out(__d('cake_console', 'Please confirm the following associations:'));
525:                 $this->hr();
526:                 $associations = $this->confirmAssociations($model, $associations);
527:             }
528:             $associations = $this->doMoreAssociations($model, $associations);
529:         }
530:         return $associations;
531:     }
532: 
533: /**
534:  * Find belongsTo relations and add them to the associations list.
535:  *
536:  * @param Model $model Model instance of model being generated.
537:  * @param array $associations Array of in progress associations
538:  * @return array $associations with belongsTo added in.
539:  */
540:     public function findBelongsTo(Model $model, $associations) {
541:         $fields = $model->schema(true);
542:         foreach ($fields as $fieldName => $field) {
543:             $offset = strpos($fieldName, '_id');
544:             if ($fieldName != $model->primaryKey && $fieldName != 'parent_id' && $offset !== false) {
545:                 $tmpModelName = $this->_modelNameFromKey($fieldName);
546:                 $associations['belongsTo'][] = array(
547:                     'alias' => $tmpModelName,
548:                     'className' => $tmpModelName,
549:                     'foreignKey' => $fieldName,
550:                 );
551:             } elseif ($fieldName == 'parent_id') {
552:                 $associations['belongsTo'][] = array(
553:                     'alias' => 'Parent' . $model->name,
554:                     'className' => $model->name,
555:                     'foreignKey' => $fieldName,
556:                 );
557:             }
558:         }
559:         return $associations;
560:     }
561: 
562: /**
563:  * Find the hasOne and HasMany relations and add them to associations list
564:  *
565:  * @param Model $model Model instance being generated
566:  * @param array $associations Array of in progress associations
567:  * @return array $associations with hasOne and hasMany added in.
568:  */
569:     public function findHasOneAndMany(Model $model, $associations) {
570:         $foreignKey = $this->_modelKey($model->name);
571:         foreach ($this->_tables as $otherTable) {
572:             $tempOtherModel = $this->_getModelObject($this->_modelName($otherTable), $otherTable);
573:             $modelFieldsTemp = $tempOtherModel->schema(true);
574: 
575:             $pattern = '/_' . preg_quote($model->table, '/') . '|' . preg_quote($model->table, '/') . '_/';
576:             $possibleJoinTable = preg_match($pattern, $otherTable);
577:             if ($possibleJoinTable == true) {
578:                 continue;
579:             }
580:             foreach ($modelFieldsTemp as $fieldName => $field) {
581:                 $assoc = false;
582:                 if ($fieldName != $model->primaryKey && $fieldName == $foreignKey) {
583:                     $assoc = array(
584:                         'alias' => $tempOtherModel->name,
585:                         'className' => $tempOtherModel->name,
586:                         'foreignKey' => $fieldName
587:                     );
588:                 } elseif ($otherTable == $model->table && $fieldName == 'parent_id') {
589:                     $assoc = array(
590:                         'alias' => 'Child' . $model->name,
591:                         'className' => $model->name,
592:                         'foreignKey' => $fieldName
593:                     );
594:                 }
595:                 if ($assoc) {
596:                     $associations['hasOne'][] = $assoc;
597:                     $associations['hasMany'][] = $assoc;
598:                 }
599: 
600:             }
601:         }
602:         return $associations;
603:     }
604: 
605: /**
606:  * Find the hasAndBelongsToMany relations and add them to associations list
607:  *
608:  * @param Model $model Model instance being generated
609:  * @param array $associations Array of in-progress associations
610:  * @return array $associations with hasAndBelongsToMany added in.
611:  */
612:     public function findHasAndBelongsToMany(Model $model, $associations) {
613:         $foreignKey = $this->_modelKey($model->name);
614:         foreach ($this->_tables as $otherTable) {
615:             $tempOtherModel = $this->_getModelObject($this->_modelName($otherTable), $otherTable);
616:             $modelFieldsTemp = $tempOtherModel->schema(true);
617: 
618:             $offset = strpos($otherTable, $model->table . '_');
619:             $otherOffset = strpos($otherTable, '_' . $model->table);
620: 
621:             if ($offset !== false) {
622:                 $offset = strlen($model->table . '_');
623:                 $habtmName = $this->_modelName(substr($otherTable, $offset));
624:                 $associations['hasAndBelongsToMany'][] = array(
625:                     'alias' => $habtmName,
626:                     'className' => $habtmName,
627:                     'foreignKey' => $foreignKey,
628:                     'associationForeignKey' => $this->_modelKey($habtmName),
629:                     'joinTable' => $otherTable
630:                 );
631:             } elseif ($otherOffset !== false) {
632:                 $habtmName = $this->_modelName(substr($otherTable, 0, $otherOffset));
633:                 $associations['hasAndBelongsToMany'][] = array(
634:                     'alias' => $habtmName,
635:                     'className' => $habtmName,
636:                     'foreignKey' => $foreignKey,
637:                     'associationForeignKey' => $this->_modelKey($habtmName),
638:                     'joinTable' => $otherTable
639:                 );
640:             }
641:         }
642:         return $associations;
643:     }
644: 
645: /**
646:  * Interact with the user and confirm associations.
647:  *
648:  * @param array $model Temporary Model instance.
649:  * @param array $associations Array of associations to be confirmed.
650:  * @return array Array of confirmed associations
651:  */
652:     public function confirmAssociations(Model $model, $associations) {
653:         foreach ($associations as $type => $settings) {
654:             if (!empty($associations[$type])) {
655:                 foreach ($associations[$type] as $i => $assoc) {
656:                     $prompt = "{$model->name} {$type} {$assoc['alias']}?";
657:                     $response = $this->in($prompt, array('y', 'n'), 'y');
658: 
659:                     if ('n' == strtolower($response)) {
660:                         unset($associations[$type][$i]);
661:                     } elseif ($type == 'hasMany') {
662:                         unset($associations['hasOne'][$i]);
663:                     }
664:                 }
665:                 $associations[$type] = array_merge($associations[$type]);
666:             }
667:         }
668:         return $associations;
669:     }
670: 
671: /**
672:  * Interact with the user and generate additional non-conventional associations
673:  *
674:  * @param Model $model Temporary model instance
675:  * @param array $associations Array of associations.
676:  * @return array Array of associations.
677:  */
678:     public function doMoreAssociations(Model $model, $associations) {
679:         $prompt = __d('cake_console', 'Would you like to define some additional model associations?');
680:         $wannaDoMoreAssoc = $this->in($prompt, array('y', 'n'), 'n');
681:         $possibleKeys = $this->_generatePossibleKeys();
682:         while (strtolower($wannaDoMoreAssoc) == 'y') {
683:             $assocs = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
684:             $this->out(__d('cake_console', 'What is the association type?'));
685:             $assocType = intval($this->inOptions($assocs, __d('cake_console', 'Enter a number')));
686: 
687:             $this->out(__d('cake_console', "For the following options be very careful to match your setup exactly.\n" .
688:                 "Any spelling mistakes will cause errors."));
689:             $this->hr();
690: 
691:             $alias = $this->in(__d('cake_console', 'What is the alias for this association?'));
692:             $className = $this->in(__d('cake_console', 'What className will %s use?', $alias), null, $alias );
693: 
694:             if ($assocType == 0) {
695:                 if (!empty($possibleKeys[$model->table])) {
696:                     $showKeys = $possibleKeys[$model->table];
697:                 } else {
698:                     $showKeys = null;
699:                 }
700:                 $suggestedForeignKey = $this->_modelKey($alias);
701:             } else {
702:                 $otherTable = Inflector::tableize($className);
703:                 if (in_array($otherTable, $this->_tables)) {
704:                     if ($assocType < 3) {
705:                         if (!empty($possibleKeys[$otherTable])) {
706:                             $showKeys = $possibleKeys[$otherTable];
707:                         } else {
708:                             $showKeys = null;
709:                         }
710:                     } else {
711:                         $showKeys = null;
712:                     }
713:                 } else {
714:                     $otherTable = $this->in(__d('cake_console', 'What is the table for this model?'));
715:                     $showKeys = $possibleKeys[$otherTable];
716:                 }
717:                 $suggestedForeignKey = $this->_modelKey($model->name);
718:             }
719:             if (!empty($showKeys)) {
720:                 $this->out(__d('cake_console', 'A helpful List of possible keys'));
721:                 $foreignKey = $this->inOptions($showKeys, __d('cake_console', 'What is the foreignKey?'));
722:                 $foreignKey = $showKeys[intval($foreignKey)];
723:             }
724:             if (!isset($foreignKey)) {
725:                 $foreignKey = $this->in(__d('cake_console', 'What is the foreignKey? Specify your own.'), null, $suggestedForeignKey);
726:             }
727:             if ($assocType == 3) {
728:                 $associationForeignKey = $this->in(__d('cake_console', 'What is the associationForeignKey?'), null, $this->_modelKey($model->name));
729:                 $joinTable = $this->in(__d('cake_console', 'What is the joinTable?'));
730:             }
731:             $associations[$assocs[$assocType]] = array_values((array)$associations[$assocs[$assocType]]);
732:             $count = count($associations[$assocs[$assocType]]);
733:             $i = ($count > 0) ? $count : 0;
734:             $associations[$assocs[$assocType]][$i]['alias'] = $alias;
735:             $associations[$assocs[$assocType]][$i]['className'] = $className;
736:             $associations[$assocs[$assocType]][$i]['foreignKey'] = $foreignKey;
737:             if ($assocType == 3) {
738:                 $associations[$assocs[$assocType]][$i]['associationForeignKey'] = $associationForeignKey;
739:                 $associations[$assocs[$assocType]][$i]['joinTable'] = $joinTable;
740:             }
741:             $wannaDoMoreAssoc = $this->in(__d('cake_console', 'Define another association?'), array('y', 'n'), 'y');
742:         }
743:         return $associations;
744:     }
745: 
746: /**
747:  * Finds all possible keys to use on custom associations.
748:  *
749:  * @return array array of tables and possible keys
750:  */
751:     protected function _generatePossibleKeys() {
752:         $possible = array();
753:         foreach ($this->_tables as $otherTable) {
754:             $tempOtherModel = new Model(array('table' => $otherTable, 'ds' => $this->connection));
755:             $modelFieldsTemp = $tempOtherModel->schema(true);
756:             foreach ($modelFieldsTemp as $fieldName => $field) {
757:                 if ($field['type'] == 'integer' || $field['type'] == 'string') {
758:                     $possible[$otherTable][] = $fieldName;
759:                 }
760:             }
761:         }
762:         return $possible;
763:     }
764: 
765: /**
766:  * Assembles and writes a Model file.
767:  *
768:  * @param mixed $name Model name or object
769:  * @param mixed $data if array and $name is not an object assume bake data, otherwise boolean.
770:  * @return string
771:  */
772:     public function bake($name, $data = array()) {
773:         if (is_object($name)) {
774:             if ($data == false) {
775:                 $data = array();
776:                 $data['associations'] = $this->doAssociations($name);
777:                 $data['validate'] = $this->doValidation($name);
778:             }
779:             $data['primaryKey'] = $name->primaryKey;
780:             $data['useTable'] = $name->table;
781:             $data['useDbConfig'] = $name->useDbConfig;
782:             $data['name'] = $name = $name->name;
783:         } else {
784:             $data['name'] = $name;
785:         }
786:         $defaults = array(
787:             'associations' => array(),
788:             'validate' => array(),
789:             'primaryKey' => 'id',
790:             'useTable' => null,
791:             'useDbConfig' => 'default',
792:             'displayField' => null
793:         );
794:         $data = array_merge($defaults, $data);
795: 
796:         $pluginPath = '';
797:         if ($this->plugin) {
798:             $pluginPath = $this->plugin . '.';
799:         }
800: 
801:         $this->Template->set($data);
802:         $this->Template->set(array(
803:             'plugin' => $this->plugin,
804:             'pluginPath' => $pluginPath
805:         ));
806:         $out = $this->Template->generate('classes', 'model');
807: 
808:         $path = $this->getPath();
809:         $filename = $path . $name . '.php';
810:         $this->out("\n" . __d('cake_console', 'Baking model class for %s...', $name), 1, Shell::QUIET);
811:         $this->createFile($filename, $out);
812:         ClassRegistry::flush();
813:         return $out;
814:     }
815: 
816: /**
817:  * Assembles and writes a unit test file
818:  *
819:  * @param string $className Model class name
820:  * @return string
821:  */
822:     public function bakeTest($className) {
823:         $this->Test->interactive = $this->interactive;
824:         $this->Test->plugin = $this->plugin;
825:         $this->Test->connection = $this->connection;
826:         return $this->Test->bake('Model', $className);
827:     }
828: 
829: /**
830:  * outputs the a list of possible models or controllers from database
831:  *
832:  * @param string $useDbConfig Database configuration name
833:  * @return array
834:  */
835:     public function listAll($useDbConfig = null) {
836:         $this->_tables = (array)$this->getAllTables($useDbConfig);
837: 
838:         $this->_modelNames = array();
839:         $count = count($this->_tables);
840:         for ($i = 0; $i < $count; $i++) {
841:             $this->_modelNames[] = $this->_modelName($this->_tables[$i]);
842:         }
843:         if ($this->interactive === true) {
844:             $this->out(__d('cake_console', 'Possible Models based on your current database:'));
845:             for ($i = 0; $i < $count; $i++) {
846:                 $this->out($i + 1 . ". " . $this->_modelNames[$i]);
847:             }
848:         }
849:         return $this->_tables;
850:     }
851: 
852: /**
853:  * Interact with the user to determine the table name of a particular model
854:  *
855:  * @param string $modelName Name of the model you want a table for.
856:  * @param string $useDbConfig Name of the database config you want to get tables from.
857:  * @return string Table name
858:  */
859:     public function getTable($modelName, $useDbConfig = null) {
860:         $useTable = Inflector::tableize($modelName);
861:         if (in_array($modelName, $this->_modelNames)) {
862:             $modelNames = array_flip($this->_modelNames);
863:             $useTable = $this->_tables[$modelNames[$modelName]];
864:         }
865: 
866:         if ($this->interactive === true) {
867:             if (!isset($useDbConfig)) {
868:                 $useDbConfig = $this->connection;
869:             }
870:             $db = ConnectionManager::getDataSource($useDbConfig);
871:             $fullTableName = $db->fullTableName($useTable, false);
872:             $tableIsGood = false;
873:             if (array_search($useTable, $this->_tables) === false) {
874:                 $this->out();
875:                 $this->out(__d('cake_console', "Given your model named '%s',\nCake would expect a database table named '%s'", $modelName, $fullTableName));
876:                 $tableIsGood = $this->in(__d('cake_console', 'Do you want to use this table?'), array('y', 'n'), 'y');
877:             }
878:             if (strtolower($tableIsGood) == 'n') {
879:                 $useTable = $this->in(__d('cake_console', 'What is the name of the table?'));
880:             }
881:         }
882:         return $useTable;
883:     }
884: 
885: /**
886:  * Get an Array of all the tables in the supplied connection
887:  * will halt the script if no tables are found.
888:  *
889:  * @param string $useDbConfig Connection name to scan.
890:  * @return array Array of tables in the database.
891:  */
892:     public function getAllTables($useDbConfig = null) {
893:         if (!isset($useDbConfig)) {
894:             $useDbConfig = $this->connection;
895:         }
896: 
897:         $tables = array();
898:         $db = ConnectionManager::getDataSource($useDbConfig);
899:         $db->cacheSources = false;
900:         $usePrefix = empty($db->config['prefix']) ? '' : $db->config['prefix'];
901:         if ($usePrefix) {
902:             foreach ($db->listSources() as $table) {
903:                 if (!strncmp($table, $usePrefix, strlen($usePrefix))) {
904:                     $tables[] = substr($table, strlen($usePrefix));
905:                 }
906:             }
907:         } else {
908:             $tables = $db->listSources();
909:         }
910:         if (empty($tables)) {
911:             $this->err(__d('cake_console', 'Your database does not have any tables.'));
912:             $this->_stop();
913:         }
914:         return $tables;
915:     }
916: 
917: /**
918:  * Forces the user to specify the model he wants to bake, and returns the selected model name.
919:  *
920:  * @param string $useDbConfig Database config name
921:  * @return string the model name
922:  */
923:     public function getName($useDbConfig = null) {
924:         $this->listAll($useDbConfig);
925: 
926:         $enteredModel = '';
927: 
928:         while ($enteredModel == '') {
929:             $enteredModel = $this->in(__d('cake_console', "Enter a number from the list above,\n" .
930:                 "type in the name of another model, or 'q' to exit"), null, 'q');
931: 
932:             if ($enteredModel === 'q') {
933:                 $this->out(__d('cake_console', 'Exit'));
934:                 $this->_stop();
935:             }
936: 
937:             if ($enteredModel == '' || intval($enteredModel) > count($this->_modelNames)) {
938:                 $this->err(__d('cake_console', "The model name you supplied was empty,\n" .
939:                     "or the number you selected was not an option. Please try again."));
940:                 $enteredModel = '';
941:             }
942:         }
943:         if (intval($enteredModel) > 0 && intval($enteredModel) <= count($this->_modelNames)) {
944:             $currentModelName = $this->_modelNames[intval($enteredModel) - 1];
945:         } else {
946:             $currentModelName = $enteredModel;
947:         }
948:         return $currentModelName;
949:     }
950: 
951: /**
952:  * get the option parser.
953:  *
954:  * @return void
955:  */
956:     public function getOptionParser() {
957:         $parser = parent::getOptionParser();
958:         return $parser->description(
959:                 __d('cake_console', 'Bake models.')
960:             )->addArgument('name', array(
961:                 'help' => __d('cake_console', 'Name of the model to bake. Can use Plugin.name to bake plugin models.')
962:             ))->addSubcommand('all', array(
963:                 'help' => __d('cake_console', 'Bake all model files with associations and validation.')
964:             ))->addOption('plugin', array(
965:                 'short' => 'p',
966:                 'help' => __d('cake_console', 'Plugin to bake the model into.')
967:             ))->addOption('connection', array(
968:                 'short' => 'c',
969:                 'help' => __d('cake_console', 'The connection the model table is on.')
970:             ))->epilog(__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.'));
971:     }
972: 
973: /**
974:  * Interact with FixtureTask to automatically bake fixtures when baking models.
975:  *
976:  * @param string $className Name of class to bake fixture for
977:  * @param string $useTable Optional table name for fixture to use.
978:  * @return void
979:  * @see FixtureTask::bake
980:  */
981:     public function bakeFixture($className, $useTable = null) {
982:         $this->Fixture->interactive = $this->interactive;
983:         $this->Fixture->connection = $this->connection;
984:         $this->Fixture->plugin = $this->plugin;
985:         $this->Fixture->bake($className, $useTable);
986:     }
987: 
988: }
989: 
OpenHub
Rackspace
Rackspace
  • Business Solutions
  • Showcase
  • Documentation
  • Book
  • API
  • Videos
  • Reporting Security Issues
  • Privacy Policy
  • Logos & Trademarks
  • Community
  • Get Involved
  • Issues (GitHub)
  • Bakery
  • Featured Resources
  • Training
  • Meetups
  • My CakePHP
  • CakeFest
  • Newsletter
  • Linkedin
  • YouTube
  • Facebook
  • Twitter
  • Mastodon
  • Help & Support
  • Forum
  • Stack Overflow
  • Slack
  • Paid Support

Generated using CakePHP API Docs