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

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