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.2 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.2
      • 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
      • Validator
    • Network
      • Email
      • Http
    • Routing
      • Filter
      • 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:             $len = strlen(count($options) + 1);
173:             foreach ($options as $i => $option) {
174:                 $this->out(sprintf("%${len}d. %s", $i + 1, $option));
175:             }
176:             if (empty($prompt)) {
177:                 $prompt = __d('cake_console', 'Make a selection from the choices above');
178:             }
179:             $choice = $this->in($prompt, null, $default);
180:             if (intval($choice) > 0 && intval($choice) <= $max) {
181:                 $valid = true;
182:             }
183:         }
184:         return $choice - 1;
185:     }
186: 
187: /**
188:  * Handles interactive baking
189:  *
190:  * @return boolean
191:  */
192:     protected function _interactive() {
193:         $this->hr();
194:         $this->out(__d('cake_console', "Bake Model\nPath: %s", $this->getPath()));
195:         $this->hr();
196:         $this->interactive = true;
197: 
198:         $primaryKey = 'id';
199:         $validate = $associations = array();
200: 
201:         if (empty($this->connection)) {
202:             $this->connection = $this->DbConfig->getConfig();
203:         }
204:         $currentModelName = $this->getName();
205:         $useTable = $this->getTable($currentModelName);
206:         $db = ConnectionManager::getDataSource($this->connection);
207:         $fullTableName = $db->fullTableName($useTable);
208:         if (!in_array($useTable, $this->_tables)) {
209:             $prompt = __d('cake_console', "The table %s doesn't exist or could not be automatically detected\ncontinue anyway?", $useTable);
210:             $continue = $this->in($prompt, array('y', 'n'));
211:             if (strtolower($continue) == 'n') {
212:                 return false;
213:             }
214:         }
215: 
216:         $tempModel = new Model(array('name' => $currentModelName, 'table' => $useTable, 'ds' => $this->connection));
217: 
218:         $knownToExist = false;
219:         try {
220:             $fields = $tempModel->schema(true);
221:             $knownToExist = true;
222:         } catch (Exception $e) {
223:             $fields = array($tempModel->primaryKey);
224:         }
225:         if (!array_key_exists('id', $fields)) {
226:             $primaryKey = $this->findPrimaryKey($fields);
227:         }
228: 
229:         if ($knownToExist) {
230:             $displayField = $tempModel->hasField(array('name', 'title'));
231:             if (!$displayField) {
232:                 $displayField = $this->findDisplayField($tempModel->schema());
233:             }
234: 
235:             $prompt = __d('cake_console', "Would you like to supply validation criteria \nfor the fields in your model?");
236:             $wannaDoValidation = $this->in($prompt, array('y','n'), 'y');
237:             if (array_search($useTable, $this->_tables) !== false && strtolower($wannaDoValidation) == 'y') {
238:                 $validate = $this->doValidation($tempModel);
239:             }
240: 
241:             $prompt = __d('cake_console', "Would you like to define model associations\n(hasMany, hasOne, belongsTo, etc.)?");
242:             $wannaDoAssoc = $this->in($prompt, array('y','n'), 'y');
243:             if (strtolower($wannaDoAssoc) == 'y') {
244:                 $associations = $this->doAssociations($tempModel);
245:             }
246:         }
247: 
248:         $this->out();
249:         $this->hr();
250:         $this->out(__d('cake_console', 'The following Model will be created:'));
251:         $this->hr();
252:         $this->out(__d('cake_console', "Name:       %s", $currentModelName));
253: 
254:         if ($this->connection !== 'default') {
255:             $this->out(__d('cake_console', "DB Config:  %s", $this->connection));
256:         }
257:         if ($fullTableName !== Inflector::tableize($currentModelName)) {
258:             $this->out(__d('cake_console', 'DB Table:   %s', $fullTableName));
259:         }
260:         if ($primaryKey != 'id') {
261:             $this->out(__d('cake_console', 'Primary Key: %s', $primaryKey));
262:         }
263:         if (!empty($validate)) {
264:             $this->out(__d('cake_console', 'Validation: %s', print_r($validate, true)));
265:         }
266:         if (!empty($associations)) {
267:             $this->out(__d('cake_console', 'Associations:'));
268:             $assocKeys = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
269:             foreach ($assocKeys as $assocKey) {
270:                 $this->_printAssociation($currentModelName, $assocKey, $associations);
271:             }
272:         }
273: 
274:         $this->hr();
275:         $looksGood = $this->in(__d('cake_console', 'Look okay?'), array('y', 'n'), 'y');
276: 
277:         if (strtolower($looksGood) == 'y') {
278:             $vars = compact('associations', 'validate', 'primaryKey', 'useTable', 'displayField');
279:             $vars['useDbConfig'] = $this->connection;
280:             if ($this->bake($currentModelName, $vars)) {
281:                 if ($this->_checkUnitTest()) {
282:                     $this->bakeFixture($currentModelName, $useTable);
283:                     $this->bakeTest($currentModelName, $useTable, $associations);
284:                 }
285:             }
286:         } else {
287:             return false;
288:         }
289:     }
290: 
291: /**
292:  * Print out all the associations of a particular type
293:  *
294:  * @param string $modelName Name of the model relations belong to.
295:  * @param string $type Name of association you want to see. i.e. 'belongsTo'
296:  * @param string $associations Collection of associations.
297:  * @return void
298:  */
299:     protected function _printAssociation($modelName, $type, $associations) {
300:         if (!empty($associations[$type])) {
301:             for ($i = 0, $len = count($associations[$type]); $i < $len; $i++) {
302:                 $out = "\t" . $modelName . ' ' . $type . ' ' . $associations[$type][$i]['alias'];
303:                 $this->out($out);
304:             }
305:         }
306:     }
307: 
308: /**
309:  * Finds a primary Key in a list of fields.
310:  *
311:  * @param array $fields Array of fields that might have a primary key.
312:  * @return string Name of field that is a primary key.
313:  */
314:     public function findPrimaryKey($fields) {
315:         $name = 'id';
316:         foreach ($fields as $name => $field) {
317:             if (isset($field['key']) && $field['key'] == 'primary') {
318:                 break;
319:             }
320:         }
321:         return $this->in(__d('cake_console', 'What is the primaryKey?'), null, $name);
322:     }
323: 
324: /**
325:  * interact with the user to find the displayField value for a model.
326:  *
327:  * @param array $fields Array of fields to look for and choose as a displayField
328:  * @return mixed Name of field to use for displayField or false if the user declines to choose
329:  */
330:     public function findDisplayField($fields) {
331:         $fieldNames = array_keys($fields);
332:         $prompt = __d('cake_console', "A displayField could not be automatically detected\nwould you like to choose one?");
333:         $continue = $this->in($prompt, array('y', 'n'));
334:         if (strtolower($continue) == 'n') {
335:             return false;
336:         }
337:         $prompt = __d('cake_console', 'Choose a field from the options above:');
338:         $choice = $this->inOptions($fieldNames, $prompt);
339:         return $fieldNames[$choice];
340:     }
341: 
342: /**
343:  * Handles Generation and user interaction for creating validation.
344:  *
345:  * @param Model $model Model to have validations generated for.
346:  * @return array $validate Array of user selected validations.
347:  */
348:     public function doValidation($model) {
349:         if (!is_object($model)) {
350:             return false;
351:         }
352:         $fields = $model->schema();
353: 
354:         if (empty($fields)) {
355:             return false;
356:         }
357:         $validate = array();
358:         $this->initValidations();
359:         foreach ($fields as $fieldName => $field) {
360:             $validation = $this->fieldValidation($fieldName, $field, $model->primaryKey);
361:             if (!empty($validation)) {
362:                 $validate[$fieldName] = $validation;
363:             }
364:         }
365:         return $validate;
366:     }
367: 
368: /**
369:  * Populate the _validations array
370:  *
371:  * @return void
372:  */
373:     public function initValidations() {
374:         $options = $choices = array();
375:         if (class_exists('Validation')) {
376:             $options = get_class_methods('Validation');
377:         }
378:         sort($options);
379:         $default = 1;
380:         foreach ($options as $key => $option) {
381:             if ($option{0} != '_') {
382:                 $choices[$default] = strtolower($option);
383:                 $default++;
384:             }
385:         }
386:         $choices[$default] = 'none'; // Needed since index starts at 1
387:         $this->_validations = $choices;
388:         return $choices;
389:     }
390: 
391: /**
392:  * Does individual field validation handling.
393:  *
394:  * @param string $fieldName Name of field to be validated.
395:  * @param array $metaData metadata for field
396:  * @param string $primaryKey
397:  * @return array Array of validation for the field.
398:  */
399:     public function fieldValidation($fieldName, $metaData, $primaryKey = 'id') {
400:         $defaultChoice = count($this->_validations);
401:         $validate = $alreadyChosen = array();
402: 
403:         $anotherValidator = 'y';
404:         while ($anotherValidator == 'y') {
405:             if ($this->interactive) {
406:                 $this->out();
407:                 $this->out(__d('cake_console', 'Field: <info>%s</info>', $fieldName));
408:                 $this->out(__d('cake_console', 'Type: <info>%s</info>', $metaData['type']));
409:                 $this->hr();
410:                 $this->out(__d('cake_console', 'Please select one of the following validation options:'));
411:                 $this->hr();
412: 
413:                 $optionText = '';
414:                 for ($i = 1, $m = $defaultChoice / 2; $i <= $m; $i++) {
415:                     $line = sprintf("%2d. %s", $i, $this->_validations[$i]);
416:                     $optionText .= $line . str_repeat(" ", 31 - strlen($line));
417:                     if ($m + $i !== $defaultChoice) {
418:                         $optionText .= sprintf("%2d. %s\n", $m + $i, $this->_validations[$m + $i]);
419:                     }
420:                 }
421:                 $this->out($optionText);
422:                 $this->out(__d('cake_console', "%s - Do not do any validation on this field.", $defaultChoice));
423:                 $this->hr();
424:             }
425: 
426:             $prompt = __d('cake_console', "... or enter in a valid regex validation string.\n");
427:             $methods = array_flip($this->_validations);
428:             $guess = $defaultChoice;
429:             if ($metaData['null'] != 1 && !in_array($fieldName, array($primaryKey, 'created', 'modified', 'updated'))) {
430:                 if ($fieldName == 'email') {
431:                     $guess = $methods['email'];
432:                 } elseif ($metaData['type'] == 'string' && $metaData['length'] == 36) {
433:                     $guess = $methods['uuid'];
434:                 } elseif ($metaData['type'] == 'string') {
435:                     $guess = $methods['notempty'];
436:                 } elseif ($metaData['type'] == 'text') {
437:                     $guess = $methods['notempty'];
438:                 } elseif ($metaData['type'] == 'integer') {
439:                     $guess = $methods['numeric'];
440:                 } elseif ($metaData['type'] == 'boolean') {
441:                     $guess = $methods['boolean'];
442:                 } elseif ($metaData['type'] == 'date') {
443:                     $guess = $methods['date'];
444:                 } elseif ($metaData['type'] == 'time') {
445:                     $guess = $methods['time'];
446:                 } elseif ($metaData['type'] == 'datetime') {
447:                     $guess = $methods['datetime'];
448:                 } elseif ($metaData['type'] == 'inet') {
449:                     $guess = $methods['ip'];
450:                 }
451:             }
452: 
453:             if ($this->interactive === true) {
454:                 $choice = $this->in($prompt, null, $guess);
455:                 if (in_array($choice, $alreadyChosen)) {
456:                     $this->out(__d('cake_console', "You have already chosen that validation rule,\nplease choose again"));
457:                     continue;
458:                 }
459:                 if (!isset($this->_validations[$choice]) && is_numeric($choice)) {
460:                     $this->out(__d('cake_console', 'Please make a valid selection.'));
461:                     continue;
462:                 }
463:                 $alreadyChosen[] = $choice;
464:             } else {
465:                 $choice = $guess;
466:             }
467: 
468:             if (isset($this->_validations[$choice])) {
469:                 $validatorName = $this->_validations[$choice];
470:             } else {
471:                 $validatorName = Inflector::slug($choice);
472:             }
473: 
474:             if ($choice != $defaultChoice) {
475:                 if (is_numeric($choice) && isset($this->_validations[$choice])) {
476:                     $validate[$validatorName] = $this->_validations[$choice];
477:                 } else {
478:                     $validate[$validatorName] = $choice;
479:                 }
480:             }
481:             if ($this->interactive == true && $choice != $defaultChoice) {
482:                 $anotherValidator = $this->in(__d('cake_console', 'Would you like to add another validation rule?'), array('y', 'n'), 'n');
483:             } else {
484:                 $anotherValidator = 'n';
485:             }
486:         }
487:         return $validate;
488:     }
489: 
490: /**
491:  * Handles associations
492:  *
493:  * @param Model $model
494:  * @return array $associations
495:  */
496:     public function doAssociations($model) {
497:         if (!is_object($model)) {
498:             return false;
499:         }
500:         if ($this->interactive === true) {
501:             $this->out(__d('cake_console', 'One moment while the associations are detected.'));
502:         }
503: 
504:         $fields = $model->schema(true);
505:         if (empty($fields)) {
506:             return array();
507:         }
508: 
509:         if (empty($this->_tables)) {
510:             $this->_tables = (array)$this->getAllTables();
511:         }
512: 
513:         $associations = array(
514:             'belongsTo' => array(),
515:             'hasMany' => array(),
516:             'hasOne' => array(),
517:             'hasAndBelongsToMany' => array()
518:         );
519: 
520:         $associations = $this->findBelongsTo($model, $associations);
521:         $associations = $this->findHasOneAndMany($model, $associations);
522:         $associations = $this->findHasAndBelongsToMany($model, $associations);
523: 
524:         if ($this->interactive !== true) {
525:             unset($associations['hasOne']);
526:         }
527: 
528:         if ($this->interactive === true) {
529:             $this->hr();
530:             if (empty($associations)) {
531:                 $this->out(__d('cake_console', 'None found.'));
532:             } else {
533:                 $this->out(__d('cake_console', 'Please confirm the following associations:'));
534:                 $this->hr();
535:                 $associations = $this->confirmAssociations($model, $associations);
536:             }
537:             $associations = $this->doMoreAssociations($model, $associations);
538:         }
539:         return $associations;
540:     }
541: 
542: /**
543:  * Find belongsTo relations and add them to the associations list.
544:  *
545:  * @param Model $model Model instance of model being generated.
546:  * @param array $associations Array of in progress associations
547:  * @return array $associations with belongsTo added in.
548:  */
549:     public function findBelongsTo(Model $model, $associations) {
550:         $fields = $model->schema(true);
551:         foreach ($fields as $fieldName => $field) {
552:             $offset = strpos($fieldName, '_id');
553:             if ($fieldName != $model->primaryKey && $fieldName != 'parent_id' && $offset !== false) {
554:                 $tmpModelName = $this->_modelNameFromKey($fieldName);
555:                 $associations['belongsTo'][] = array(
556:                     'alias' => $tmpModelName,
557:                     'className' => $tmpModelName,
558:                     'foreignKey' => $fieldName,
559:                 );
560:             } elseif ($fieldName == 'parent_id') {
561:                 $associations['belongsTo'][] = array(
562:                     'alias' => 'Parent' . $model->name,
563:                     'className' => $model->name,
564:                     'foreignKey' => $fieldName,
565:                 );
566:             }
567:         }
568:         return $associations;
569:     }
570: 
571: /**
572:  * Find the hasOne and HasMany relations and add them to associations list
573:  *
574:  * @param Model $model Model instance being generated
575:  * @param array $associations Array of in progress associations
576:  * @return array $associations with hasOne and hasMany added in.
577:  */
578:     public function findHasOneAndMany(Model $model, $associations) {
579:         $foreignKey = $this->_modelKey($model->name);
580:         foreach ($this->_tables as $otherTable) {
581:             $tempOtherModel = $this->_getModelObject($this->_modelName($otherTable), $otherTable);
582:             $modelFieldsTemp = $tempOtherModel->schema(true);
583: 
584:             $pattern = '/_' . preg_quote($model->table, '/') . '|' . preg_quote($model->table, '/') . '_/';
585:             $possibleJoinTable = preg_match($pattern, $otherTable);
586:             if ($possibleJoinTable == true) {
587:                 continue;
588:             }
589:             foreach ($modelFieldsTemp as $fieldName => $field) {
590:                 $assoc = false;
591:                 if ($fieldName != $model->primaryKey && $fieldName == $foreignKey) {
592:                     $assoc = array(
593:                         'alias' => $tempOtherModel->name,
594:                         'className' => $tempOtherModel->name,
595:                         'foreignKey' => $fieldName
596:                     );
597:                 } elseif ($otherTable == $model->table && $fieldName == 'parent_id') {
598:                     $assoc = array(
599:                         'alias' => 'Child' . $model->name,
600:                         'className' => $model->name,
601:                         'foreignKey' => $fieldName
602:                     );
603:                 }
604:                 if ($assoc) {
605:                     $associations['hasOne'][] = $assoc;
606:                     $associations['hasMany'][] = $assoc;
607:                 }
608: 
609:             }
610:         }
611:         return $associations;
612:     }
613: 
614: /**
615:  * Find the hasAndBelongsToMany relations and add them to associations list
616:  *
617:  * @param Model $model Model instance being generated
618:  * @param array $associations Array of in-progress associations
619:  * @return array $associations with hasAndBelongsToMany added in.
620:  */
621:     public function findHasAndBelongsToMany(Model $model, $associations) {
622:         $foreignKey = $this->_modelKey($model->name);
623:         foreach ($this->_tables as $otherTable) {
624:             $tableName = null;
625:             $tempOtherModel = $this->_getModelObject($this->_modelName($otherTable), $otherTable);
626:             $modelFieldsTemp = $tempOtherModel->schema(true);
627: 
628:             $offset = strpos($otherTable, $model->table . '_');
629:             $otherOffset = strpos($otherTable, '_' . $model->table);
630: 
631:             if ($offset === 0) {
632:                 $tableName = substr($otherTable, strlen($model->table . '_'));
633:             } elseif ($otherOffset === 0) {
634:                 $tableName = substr($otherTable, 0, $otherOffset);
635:             }
636:             if ($tableName && in_array($tableName, $this->_tables)) {
637:                 $habtmName = $this->_modelName($tableName);
638:                 $associations['hasAndBelongsToMany'][] = array(
639:                     'alias' => $habtmName,
640:                     'className' => $habtmName,
641:                     'foreignKey' => $foreignKey,
642:                     'associationForeignKey' => $this->_modelKey($habtmName),
643:                     'joinTable' => $otherTable
644:                 );
645:             }
646:         }
647:         return $associations;
648:     }
649: 
650: /**
651:  * Interact with the user and confirm associations.
652:  *
653:  * @param array $model Temporary Model instance.
654:  * @param array $associations Array of associations to be confirmed.
655:  * @return array Array of confirmed associations
656:  */
657:     public function confirmAssociations(Model $model, $associations) {
658:         foreach ($associations as $type => $settings) {
659:             if (!empty($associations[$type])) {
660:                 foreach ($associations[$type] as $i => $assoc) {
661:                     $prompt = "{$model->name} {$type} {$assoc['alias']}?";
662:                     $response = $this->in($prompt, array('y', 'n'), 'y');
663: 
664:                     if ('n' == strtolower($response)) {
665:                         unset($associations[$type][$i]);
666:                     } elseif ($type == 'hasMany') {
667:                         unset($associations['hasOne'][$i]);
668:                     }
669:                 }
670:                 $associations[$type] = array_merge($associations[$type]);
671:             }
672:         }
673:         return $associations;
674:     }
675: 
676: /**
677:  * Interact with the user and generate additional non-conventional associations
678:  *
679:  * @param Model $model Temporary model instance
680:  * @param array $associations Array of associations.
681:  * @return array Array of associations.
682:  */
683:     public function doMoreAssociations(Model $model, $associations) {
684:         $prompt = __d('cake_console', 'Would you like to define some additional model associations?');
685:         $wannaDoMoreAssoc = $this->in($prompt, array('y', 'n'), 'n');
686:         $possibleKeys = $this->_generatePossibleKeys();
687:         while (strtolower($wannaDoMoreAssoc) == 'y') {
688:             $assocs = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
689:             $this->out(__d('cake_console', 'What is the association type?'));
690:             $assocType = intval($this->inOptions($assocs, __d('cake_console', 'Enter a number')));
691: 
692:             $this->out(__d('cake_console', "For the following options be very careful to match your setup exactly.\n" .
693:                 "Any spelling mistakes will cause errors."));
694:             $this->hr();
695: 
696:             $alias = $this->in(__d('cake_console', 'What is the alias for this association?'));
697:             $className = $this->in(__d('cake_console', 'What className will %s use?', $alias), null, $alias );
698: 
699:             if ($assocType == 0) {
700:                 if (!empty($possibleKeys[$model->table])) {
701:                     $showKeys = $possibleKeys[$model->table];
702:                 } else {
703:                     $showKeys = null;
704:                 }
705:                 $suggestedForeignKey = $this->_modelKey($alias);
706:             } else {
707:                 $otherTable = Inflector::tableize($className);
708:                 if (in_array($otherTable, $this->_tables)) {
709:                     if ($assocType < 3) {
710:                         if (!empty($possibleKeys[$otherTable])) {
711:                             $showKeys = $possibleKeys[$otherTable];
712:                         } else {
713:                             $showKeys = null;
714:                         }
715:                     } else {
716:                         $showKeys = null;
717:                     }
718:                 } else {
719:                     $otherTable = $this->in(__d('cake_console', 'What is the table for this model?'));
720:                     $showKeys = $possibleKeys[$otherTable];
721:                 }
722:                 $suggestedForeignKey = $this->_modelKey($model->name);
723:             }
724:             if (!empty($showKeys)) {
725:                 $this->out(__d('cake_console', 'A helpful List of possible keys'));
726:                 $foreignKey = $this->inOptions($showKeys, __d('cake_console', 'What is the foreignKey?'));
727:                 $foreignKey = $showKeys[intval($foreignKey)];
728:             }
729:             if (!isset($foreignKey)) {
730:                 $foreignKey = $this->in(__d('cake_console', 'What is the foreignKey? Specify your own.'), null, $suggestedForeignKey);
731:             }
732:             if ($assocType == 3) {
733:                 $associationForeignKey = $this->in(__d('cake_console', 'What is the associationForeignKey?'), null, $this->_modelKey($model->name));
734:                 $joinTable = $this->in(__d('cake_console', 'What is the joinTable?'));
735:             }
736:             $associations[$assocs[$assocType]] = array_values((array)$associations[$assocs[$assocType]]);
737:             $count = count($associations[$assocs[$assocType]]);
738:             $i = ($count > 0) ? $count : 0;
739:             $associations[$assocs[$assocType]][$i]['alias'] = $alias;
740:             $associations[$assocs[$assocType]][$i]['className'] = $className;
741:             $associations[$assocs[$assocType]][$i]['foreignKey'] = $foreignKey;
742:             if ($assocType == 3) {
743:                 $associations[$assocs[$assocType]][$i]['associationForeignKey'] = $associationForeignKey;
744:                 $associations[$assocs[$assocType]][$i]['joinTable'] = $joinTable;
745:             }
746:             $wannaDoMoreAssoc = $this->in(__d('cake_console', 'Define another association?'), array('y', 'n'), 'y');
747:         }
748:         return $associations;
749:     }
750: 
751: /**
752:  * Finds all possible keys to use on custom associations.
753:  *
754:  * @return array array of tables and possible keys
755:  */
756:     protected function _generatePossibleKeys() {
757:         $possible = array();
758:         foreach ($this->_tables as $otherTable) {
759:             $tempOtherModel = new Model(array('table' => $otherTable, 'ds' => $this->connection));
760:             $modelFieldsTemp = $tempOtherModel->schema(true);
761:             foreach ($modelFieldsTemp as $fieldName => $field) {
762:                 if ($field['type'] == 'integer' || $field['type'] == 'string') {
763:                     $possible[$otherTable][] = $fieldName;
764:                 }
765:             }
766:         }
767:         return $possible;
768:     }
769: 
770: /**
771:  * Assembles and writes a Model file.
772:  *
773:  * @param string|object $name Model name or object
774:  * @param array|boolean $data if array and $name is not an object assume bake data, otherwise boolean.
775:  * @return string
776:  */
777:     public function bake($name, $data = array()) {
778:         if (is_object($name)) {
779:             if ($data == false) {
780:                 $data = array();
781:                 $data['associations'] = $this->doAssociations($name);
782:                 $data['validate'] = $this->doValidation($name);
783:             }
784:             $data['primaryKey'] = $name->primaryKey;
785:             $data['useTable'] = $name->table;
786:             $data['useDbConfig'] = $name->useDbConfig;
787:             $data['name'] = $name = $name->name;
788:         } else {
789:             $data['name'] = $name;
790:         }
791:         $defaults = array(
792:             'associations' => array(),
793:             'validate' => array(),
794:             'primaryKey' => 'id',
795:             'useTable' => null,
796:             'useDbConfig' => 'default',
797:             'displayField' => null
798:         );
799:         $data = array_merge($defaults, $data);
800: 
801:         $pluginPath = '';
802:         if ($this->plugin) {
803:             $pluginPath = $this->plugin . '.';
804:         }
805: 
806:         $this->Template->set($data);
807:         $this->Template->set(array(
808:             'plugin' => $this->plugin,
809:             'pluginPath' => $pluginPath
810:         ));
811:         $out = $this->Template->generate('classes', 'model');
812: 
813:         $path = $this->getPath();
814:         $filename = $path . $name . '.php';
815:         $this->out("\n" . __d('cake_console', 'Baking model class for %s...', $name), 1, Shell::QUIET);
816:         $this->createFile($filename, $out);
817:         ClassRegistry::flush();
818:         return $out;
819:     }
820: 
821: /**
822:  * Assembles and writes a unit test file
823:  *
824:  * @param string $className Model class name
825:  * @return string
826:  */
827:     public function bakeTest($className) {
828:         $this->Test->interactive = $this->interactive;
829:         $this->Test->plugin = $this->plugin;
830:         $this->Test->connection = $this->connection;
831:         return $this->Test->bake('Model', $className);
832:     }
833: 
834: /**
835:  * outputs the a list of possible models or controllers from database
836:  *
837:  * @param string $useDbConfig Database configuration name
838:  * @return array
839:  */
840:     public function listAll($useDbConfig = null) {
841:         $this->_tables = (array)$this->getAllTables($useDbConfig);
842: 
843:         $this->_modelNames = array();
844:         $count = count($this->_tables);
845:         for ($i = 0; $i < $count; $i++) {
846:             $this->_modelNames[] = $this->_modelName($this->_tables[$i]);
847:         }
848:         if ($this->interactive === true) {
849:             $this->out(__d('cake_console', 'Possible Models based on your current database:'));
850:             $len = strlen($count + 1);
851:             for ($i = 0; $i < $count; $i++) {
852:                 $this->out(sprintf("%${len}d. %s", $i + 1, $this->_modelNames[$i]));
853:             }
854:         }
855:         return $this->_tables;
856:     }
857: 
858: /**
859:  * Interact with the user to determine the table name of a particular model
860:  *
861:  * @param string $modelName Name of the model you want a table for.
862:  * @param string $useDbConfig Name of the database config you want to get tables from.
863:  * @return string Table name
864:  */
865:     public function getTable($modelName, $useDbConfig = null) {
866:         $useTable = Inflector::tableize($modelName);
867:         if (in_array($modelName, $this->_modelNames)) {
868:             $modelNames = array_flip($this->_modelNames);
869:             $useTable = $this->_tables[$modelNames[$modelName]];
870:         }
871: 
872:         if ($this->interactive === true) {
873:             if (!isset($useDbConfig)) {
874:                 $useDbConfig = $this->connection;
875:             }
876:             $db = ConnectionManager::getDataSource($useDbConfig);
877:             $fullTableName = $db->fullTableName($useTable, false);
878:             $tableIsGood = false;
879:             if (array_search($useTable, $this->_tables) === false) {
880:                 $this->out();
881:                 $this->out(__d('cake_console', "Given your model named '%s',\nCake would expect a database table named '%s'", $modelName, $fullTableName));
882:                 $tableIsGood = $this->in(__d('cake_console', 'Do you want to use this table?'), array('y', 'n'), 'y');
883:             }
884:             if (strtolower($tableIsGood) == 'n') {
885:                 $useTable = $this->in(__d('cake_console', 'What is the name of the table?'));
886:             }
887:         }
888:         return $useTable;
889:     }
890: 
891: /**
892:  * Get an Array of all the tables in the supplied connection
893:  * will halt the script if no tables are found.
894:  *
895:  * @param string $useDbConfig Connection name to scan.
896:  * @return array Array of tables in the database.
897:  */
898:     public function getAllTables($useDbConfig = null) {
899:         if (!isset($useDbConfig)) {
900:             $useDbConfig = $this->connection;
901:         }
902: 
903:         $tables = array();
904:         $db = ConnectionManager::getDataSource($useDbConfig);
905:         $db->cacheSources = false;
906:         $usePrefix = empty($db->config['prefix']) ? '' : $db->config['prefix'];
907:         if ($usePrefix) {
908:             foreach ($db->listSources() as $table) {
909:                 if (!strncmp($table, $usePrefix, strlen($usePrefix))) {
910:                     $tables[] = substr($table, strlen($usePrefix));
911:                 }
912:             }
913:         } else {
914:             $tables = $db->listSources();
915:         }
916:         if (empty($tables)) {
917:             $this->err(__d('cake_console', 'Your database does not have any tables.'));
918:             $this->_stop();
919:         }
920:         return $tables;
921:     }
922: 
923: /**
924:  * Forces the user to specify the model he wants to bake, and returns the selected model name.
925:  *
926:  * @param string $useDbConfig Database config name
927:  * @return string the model name
928:  */
929:     public function getName($useDbConfig = null) {
930:         $this->listAll($useDbConfig);
931: 
932:         $enteredModel = '';
933: 
934:         while ($enteredModel == '') {
935:             $enteredModel = $this->in(__d('cake_console', "Enter a number from the list above,\n" .
936:                 "type in the name of another model, or 'q' to exit"), null, 'q');
937: 
938:             if ($enteredModel === 'q') {
939:                 $this->out(__d('cake_console', 'Exit'));
940:                 $this->_stop();
941:             }
942: 
943:             if ($enteredModel == '' || intval($enteredModel) > count($this->_modelNames)) {
944:                 $this->err(__d('cake_console', "The model name you supplied was empty,\n" .
945:                     "or the number you selected was not an option. Please try again."));
946:                 $enteredModel = '';
947:             }
948:         }
949:         if (intval($enteredModel) > 0 && intval($enteredModel) <= count($this->_modelNames)) {
950:             $currentModelName = $this->_modelNames[intval($enteredModel) - 1];
951:         } else {
952:             $currentModelName = $enteredModel;
953:         }
954:         return $currentModelName;
955:     }
956: 
957: /**
958:  * get the option parser.
959:  *
960:  * @return void
961:  */
962:     public function getOptionParser() {
963:         $parser = parent::getOptionParser();
964:         return $parser->description(
965:                 __d('cake_console', 'Bake models.')
966:             )->addArgument('name', array(
967:                 'help' => __d('cake_console', 'Name of the model to bake. Can use Plugin.name to bake plugin models.')
968:             ))->addSubcommand('all', array(
969:                 'help' => __d('cake_console', 'Bake all model files with associations and validation.')
970:             ))->addOption('plugin', array(
971:                 'short' => 'p',
972:                 'help' => __d('cake_console', 'Plugin to bake the model into.')
973:             ))->addOption('connection', array(
974:                 'short' => 'c',
975:                 'help' => __d('cake_console', 'The connection the model table is on.')
976:             ))->epilog(__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.'));
977:     }
978: 
979: /**
980:  * Interact with FixtureTask to automatically bake fixtures when baking models.
981:  *
982:  * @param string $className Name of class to bake fixture for
983:  * @param string $useTable Optional table name for fixture to use.
984:  * @return void
985:  * @see FixtureTask::bake
986:  */
987:     public function bakeFixture($className, $useTable = null) {
988:         $this->Fixture->interactive = $this->interactive;
989:         $this->Fixture->connection = $this->connection;
990:         $this->Fixture->plugin = $this->plugin;
991:         $this->Fixture->bake($className, $useTable);
992:     }
993: 
994: }
995: 
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