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

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