model.php

Go to the documentation of this file.
00001 <?php
00002 /* SVN FILE: $Id: console_2libs_2tasks_2model_8php-source.html 580 2008-07-01 14:45:49Z gwoo $ */
00003 /**
00004  * The ModelTask handles creating and updating models files.
00005  *
00006  * Long description for file
00007  *
00008  * PHP versions 4 and 5
00009  *
00010  * CakePHP(tm) :  Rapid Development Framework <http://www.cakephp.org/>
00011  * Copyright 2005-2008, Cake Software Foundation, Inc.
00012  *                              1785 E. Sahara Avenue, Suite 490-204
00013  *                              Las Vegas, Nevada 89104
00014  *
00015  * Licensed under The MIT License
00016  * Redistributions of files must retain the above copyright notice.
00017  *
00018  * @filesource
00019  * @copyright       Copyright 2005-2008, Cake Software Foundation, Inc.
00020  * @link                http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
00021  * @package         cake
00022  * @subpackage      cake.cake.console.libs.tasks
00023  * @since           CakePHP(tm) v 1.2
00024  * @version         $Revision: 580 $
00025  * @modifiedby      $LastChangedBy: gwoo $
00026  * @lastmodified    $Date: 2008-07-01 09:45:49 -0500 (Tue, 01 Jul 2008) $
00027  * @license         http://www.opensource.org/licenses/mit-license.php The MIT License
00028  */
00029 App::import('Model', 'ConnectionManager');
00030 /**
00031  * Task class for creating and updating model files.
00032  *
00033  * @package     cake
00034  * @subpackage  cake.cake.console.libs.tasks
00035  */
00036 class ModelTask extends Shell {
00037 /**
00038  * Name of plugin
00039  *
00040  * @var string
00041  * @access public
00042  */
00043     var $plugin = null;
00044 /**
00045  * path to MODELS directory
00046  *
00047  * @var string
00048  * @access public
00049  */
00050     var $path = MODELS;
00051 
00052 /**
00053  * tasks
00054  *
00055  * @var array
00056  * @access public
00057  */
00058     var $tasks = array('DbConfig');
00059 /**
00060  * Execution method always used for tasks
00061  *
00062  * @access public
00063  */
00064     function execute() {
00065         if (empty($this->args)) {
00066             $this->__interactive();
00067         }
00068 
00069         if (!empty($this->args[0])) {
00070             $model = Inflector::camelize($this->args[0]);
00071             if ($this->bake($model)) {
00072                 if ($this->_checkUnitTest()) {
00073                     $this->bakeTest($model);
00074                 }
00075             }
00076         }
00077     }
00078 /**
00079  * Handles interactive baking
00080  *
00081  * @access private
00082  */
00083     function __interactive() {
00084         $this->hr();
00085         $this->out(sprintf("Bake Model\nPath: %s", $this->path));
00086         $this->hr();
00087         $this->interactive = true;
00088 
00089         $useTable = null;
00090         $primaryKey = 'id';
00091         $validate = array();
00092         $associations = array('belongsTo'=> array(), 'hasOne'=> array(), 'hasMany' => array(), 'hasAndBelongsToMany'=> array());
00093 
00094         $useDbConfig = 'default';
00095         $configs = get_class_vars('DATABASE_CONFIG');
00096 
00097         if (!is_array($configs)) {
00098             return $this->DbConfig->execute();
00099         }
00100 
00101         $connections = array_keys($configs);
00102         if (count($connections) > 1) {
00103             $useDbConfig = $this->in(__('Use Database Config', true) .':', $connections, 'default');
00104         }
00105 
00106         $currentModelName = $this->getName($useDbConfig);
00107         $db =& ConnectionManager::getDataSource($useDbConfig);
00108         $useTable = Inflector::tableize($currentModelName);
00109         $fullTableName = $db->fullTableName($useTable, false);
00110         $tableIsGood = false;
00111 
00112         if (array_search($useTable, $this->__tables) === false) {
00113             $this->out('');
00114             $this->out(sprintf(__("Given your model named '%s', Cake would expect a database table named %s", true), $currentModelName, $fullTableName));
00115             $tableIsGood = $this->in(__('Do you want to use this table?', true), array('y','n'), 'y');
00116         }
00117 
00118         if (low($tableIsGood) == 'n' || low($tableIsGood) == 'no') {
00119             $useTable = $this->in(__('What is the name of the table (enter "null" to use NO table)?', true));
00120         }
00121 
00122         while ($tableIsGood == false && low($useTable) != 'null') {
00123             if (is_array($this->__tables) && !in_array($useTable, $this->__tables)) {
00124                 $fullTableName = $db->fullTableName($useTable, false);
00125                 $this->out($fullTableName . ' does not exist.');
00126                 $useTable = $this->in(__('What is the name of the table (enter "null" to use NO table)?', true));
00127                 $tableIsGood = false;
00128             } else {
00129                 $tableIsGood = true;
00130             }
00131         }
00132 
00133         $wannaDoValidation = $this->in(__('Would you like to supply validation criteria for the fields in your model?', true), array('y','n'), 'y');
00134 
00135         if (in_array($useTable, $this->__tables)) {
00136             App::import('Model');
00137             $tempModel = new Model(array('name' => $currentModelName, 'table' => $useTable, 'ds' => $useDbConfig));
00138 
00139             $fields = $tempModel->schema();
00140             if (!array_key_exists('id', $fields)) {
00141                 foreach ($fields as $name => $field) {
00142                     if (isset($field['key']) && $field['key'] == 'primary') {
00143                         break;
00144                     }
00145                 }
00146                 $primaryKey = $this->in(__('What is the primaryKey?', true), null, $name);
00147             }
00148         }
00149 
00150         if (array_search($useTable, $this->__tables) !== false && (low($wannaDoValidation) == 'y' || low($wannaDoValidation) == 'yes')) {
00151             $validate = $this->doValidation($tempModel);
00152         }
00153 
00154         $wannaDoAssoc = $this->in(__('Would you like to define model associations (hasMany, hasOne, belongsTo, etc.)?', true), array('y','n'), 'y');
00155         if ((low($wannaDoAssoc) == 'y' || low($wannaDoAssoc) == 'yes')) {
00156             $associations = $this->doAssociations($tempModel);
00157         }
00158 
00159         $this->out('');
00160         $this->hr();
00161         $this->out(__('The following Model will be created:', true));
00162         $this->hr();
00163         $this->out("Name:       " . $currentModelName);
00164 
00165         if ($useDbConfig !== 'default') {
00166             $this->out("DB Config:  " . $useDbConfig);
00167         }
00168         if ($fullTableName !== Inflector::tableize($currentModelName)) {
00169             $this->out("DB Table:   " . $fullTableName);
00170         }
00171         if ($primaryKey != 'id') {
00172             $this->out("Primary Key: " . $primaryKey);
00173         }
00174         if (!empty($validate)) {
00175             $this->out("Validation: " . print_r($validate, true));
00176         }
00177         if (!empty($associations)) {
00178             $this->out("Associations:");
00179 
00180             if (!empty($associations['belongsTo'])) {
00181                 for ($i = 0; $i < count($associations['belongsTo']); $i++) {
00182                     $this->out("            $currentModelName belongsTo {$associations['belongsTo'][$i]['alias']}");
00183                 }
00184             }
00185 
00186             if (!empty($associations['hasOne'])) {
00187                 for ($i = 0; $i < count($associations['hasOne']); $i++) {
00188                     $this->out("            $currentModelName hasOne    {$associations['hasOne'][$i]['alias']}");
00189                 }
00190             }
00191 
00192             if (!empty($associations['hasMany'])) {
00193                 for ($i = 0; $i < count($associations['hasMany']); $i++) {
00194                     $this->out("            $currentModelName hasMany   {$associations['hasMany'][$i]['alias']}");
00195                 }
00196             }
00197 
00198             if (!empty($associations['hasAndBelongsToMany'])) {
00199                 for ($i = 0; $i < count($associations['hasAndBelongsToMany']); $i++) {
00200                     $this->out("            $currentModelName hasAndBelongsToMany {$associations['hasAndBelongsToMany'][$i]['alias']}");
00201                 }
00202             }
00203         }
00204         $this->hr();
00205         $looksGood = $this->in(__('Look okay?', true), array('y','n'), 'y');
00206 
00207         if (low($looksGood) == 'y' || low($looksGood) == 'yes') {
00208             if ($this->bake($currentModelName, $associations, $validate, $primaryKey, $useTable, $useDbConfig)) {
00209                 if ($this->_checkUnitTest()) {
00210                     $this->bakeTest($currentModelName, $useTable, $associations);
00211                 }
00212             }
00213         } else {
00214             return false;
00215         }
00216     }
00217 /**
00218  * Handles associations
00219  *
00220  * @param object $model
00221  * @param boolean $interactive
00222  * @return array $validate
00223  * @access public
00224  */
00225     function doValidation(&$model, $interactive = true) {
00226         if (!is_object($model)) {
00227             return false;
00228         }
00229         $fields = $model->schema();
00230 
00231         if (empty($fields)) {
00232             return false;
00233         }
00234 
00235         $validate = array();
00236         $options = array('VALID_NOT_EMPTY', 'VALID_EMAIL', 'VALID_NUMER', 'VALID_YEAR');
00237 
00238         if (class_exists('Validation')) {
00239             $parent = get_class_methods(get_parent_class('Validation'));
00240             $options = array_diff(get_class_methods('Validation'), $parent);
00241         }
00242 
00243         foreach ($fields as $fieldName => $field) {
00244             $prompt = 'Field: ' . $fieldName . "\n";
00245             $prompt .= 'Type: ' . $field['type'] . "\n";
00246             $prompt .= '---------------------------------------------------------------'."\n";
00247             $prompt .= 'Please select one of the following validation options:'."\n";
00248             $prompt .= '---------------------------------------------------------------'."\n";
00249             $choices = array();
00250             $skip = 1;
00251             sort($options);
00252             foreach ($options as $key => $option) {
00253                 if ($option{0} != '_' && strtolower($option) != 'getinstance') {
00254                     $prompt .= "{$skip} - {$option}\n";
00255                     $choices[$skip] = strtolower($option);
00256                     $skip++;
00257                 }
00258             }
00259             $methods = array_flip($choices);
00260 
00261             $prompt .=  "{$skip} - Do not do any validation on this field.\n";
00262             $prompt .= "... or enter in a valid regex validation string.\n";
00263 
00264             $guess = $skip;
00265             if ($field['null'] != 1 && $fieldName != $model->primaryKey && !in_array($fieldName, array('created', 'modified', 'updated'))) {
00266                 if ($fieldName == 'email') {
00267                     $guess = $methods['email'];
00268                 } elseif ($field['type'] == 'string') {
00269                     $guess = $methods['alphanumeric'];
00270                 } elseif ($field['type'] == 'integer') {
00271                     $guess = $methods['numeric'];
00272                 } elseif ($field['type'] == 'boolean') {
00273                     $guess = $methods['numeric'];
00274                 } elseif ($field['type'] == 'datetime') {
00275                     $guess = $methods['date'];
00276                 }
00277             }
00278 
00279             if ($interactive === true) {
00280                 $this->out('');
00281                 $choice = $this->in($prompt, null, $guess);
00282             } else {
00283                 $choice = $guess;
00284             }
00285             if ($choice != $skip) {
00286                 if (is_numeric($choice) && isset($choices[$choice])) {
00287                     $validate[$fieldName] = $choices[$choice];
00288                 } else {
00289                     $validate[$fieldName] = $choice;
00290                 }
00291             }
00292         }
00293         return $validate;
00294     }
00295 
00296 /**
00297  * Handles associations
00298  *
00299  * @param object $model
00300  * @param boolean $interactive
00301  * @return array $assocaitons
00302  * @access public
00303  */
00304     function doAssociations(&$model, $interactive = true) {
00305 
00306         if (!is_object($model)) {
00307             return false;
00308         }
00309         $this->out(__('One moment while the associations are detected.', true));
00310 
00311         $fields = $model->schema();
00312 
00313         if (empty($fields)) {
00314             return false;
00315         }
00316 
00317         $primaryKey = $model->primaryKey;
00318         $foreignKey = $this->_modelKey($model->name);
00319 
00320         $associations = $possibleKeys = array();
00321 
00322         //Look for belongsTo
00323         $i = 0;
00324         foreach ($fields as $fieldName => $field) {
00325             $offset = strpos($fieldName, '_id');
00326             if ($fieldName != $model->primaryKey && $offset !== false) {
00327                 $tmpModelName = $this->_modelNameFromKey($fieldName);
00328                 $associations['belongsTo'][$i]['alias'] = $tmpModelName;
00329                 $associations['belongsTo'][$i]['className'] = $tmpModelName;
00330                 $associations['belongsTo'][$i]['foreignKey'] = $fieldName;
00331                 $i++;
00332             }
00333         }
00334         //Look for hasOne and hasMany and hasAndBelongsToMany
00335         $i = $j = 0;
00336 
00337         foreach ($this->__tables as $otherTable) {
00338             App::import('Model');
00339             $tmpModelName = $this->_modelName($otherTable);
00340             $tempOtherModel = & new Model(array('name' => $tmpModelName, 'table' => $otherTable, 'ds' => $model->useDbConfig));
00341             $modelFieldsTemp = $tempOtherModel->schema();
00342 
00343             $offset = strpos($otherTable, $model->table . '_');
00344             $otherOffset = strpos($otherTable, '_' . $model->table);
00345 
00346             foreach ($modelFieldsTemp as $fieldName => $field) {
00347                 if ($field['type'] == 'integer' || $field['type'] == 'string') {
00348                     $possibleKeys[$otherTable][] = $fieldName;
00349                 }
00350                 if ($fieldName != $model->primaryKey && $fieldName == $foreignKey && $offset === false && $otherOffset === false) {
00351                     $associations['hasOne'][$j]['alias'] = $tempOtherModel->name;
00352                     $associations['hasOne'][$j]['className'] = $tempOtherModel->name;
00353                     $associations['hasOne'][$j]['foreignKey'] = $fieldName;
00354 
00355                     $associations['hasMany'][$j]['alias'] = $tempOtherModel->name;
00356                     $associations['hasMany'][$j]['className'] = $tempOtherModel->name;
00357                     $associations['hasMany'][$j]['foreignKey'] = $fieldName;
00358                     $j++;
00359                 }
00360             }
00361 
00362             if ($offset !== false) {
00363                 $offset = strlen($model->table . '_');
00364                 $tmpModelName = $this->_modelName(substr($otherTable, $offset));
00365                 $associations['hasAndBelongsToMany'][$i]['alias'] = $tmpModelName;
00366                 $associations['hasAndBelongsToMany'][$i]['className'] = $tmpModelName;
00367                 $associations['hasAndBelongsToMany'][$i]['foreignKey'] = $foreignKey;
00368                 $associations['hasAndBelongsToMany'][$i]['associationForeignKey'] = $this->_modelKey($tmpModelName);
00369                 $associations['hasAndBelongsToMany'][$i]['joinTable'] = $otherTable;
00370                 $i++;
00371             }
00372 
00373             if ($otherOffset !== false) {
00374                 $tmpModelName = $this->_modelName(substr($otherTable, 0, $otherOffset));
00375                 $associations['hasAndBelongsToMany'][$i]['alias'] = $tmpModelName;
00376                 $associations['hasAndBelongsToMany'][$i]['className'] = $tmpModelName;
00377                 $associations['hasAndBelongsToMany'][$i]['foreignKey'] = $foreignKey;
00378                 $associations['hasAndBelongsToMany'][$i]['associationForeignKey'] = $this->_modelKey($tmpModelName);
00379                 $associations['hasAndBelongsToMany'][$i]['joinTable'] = $otherTable;
00380                 $i++;
00381             }
00382         }
00383 
00384         if ($interactive !== true) {
00385             unset($associations['hasOne']);
00386         }
00387 
00388         if ($interactive === true) {
00389             $this->hr();
00390             if (empty($associations)) {
00391                 $this->out(__('None found.', true));
00392             } else {
00393                 $this->out(__('Please confirm the following associations:', true));
00394                 $this->hr();
00395                 foreach ($associations as $type => $settings) {
00396                     if (!empty($associations[$type])) {
00397                         $count = count($associations[$type]);
00398                         $response = 'y';
00399                         for ($i = 0; $i < $count; $i++) {
00400                             $prompt = "{$model->name} {$type} {$associations[$type][$i]['alias']}";
00401                             $response = $this->in("{$prompt}?", array('y','n'), 'y');
00402 
00403                             if ('n' == low($response) || 'no' == low($response)) {
00404                                 unset($associations[$type][$i]);
00405                             } else {
00406                                 if ($model->name === $associations[$type][$i]['alias']) {
00407                                     if ($type === 'belongsTo') {
00408                                         $alias = 'Parent' . $associations[$type][$i]['alias'];
00409                                     }
00410                                     if($type === 'hasOne' || $type === 'hasMany') {
00411                                         $alias = 'Child' . $associations[$type][$i]['alias'];
00412                                     }
00413 
00414                                     $alternateAlias = $this->in(sprintf(__('This is a self join. Use %s as the alias', true), $alias), array('y', 'n'), 'y');
00415 
00416                                     if ('n' == low($alternateAlias) || 'no' == low($alternateAlias)) {
00417                                         $associations[$type][$i]['alias'] = $this->in(__('Specify an alternate alias.', true));
00418                                     } else {
00419                                         $associations[$type][$i]['alias'] = $alias;
00420                                     }
00421                                 }
00422                             }
00423                         }
00424                         $associations[$type] = array_merge($associations[$type]);
00425                     }
00426                 }
00427             }
00428 
00429             $wannaDoMoreAssoc = $this->in(__('Would you like to define some additional model associations?', true), array('y','n'), 'n');
00430 
00431             while ((low($wannaDoMoreAssoc) == 'y' || low($wannaDoMoreAssoc) == 'yes')) {
00432                 $assocs = array(1 => 'belongsTo', 2 => 'hasOne', 3 => 'hasMany', 4 => 'hasAndBelongsToMany');
00433                 $bad = true;
00434                 while ($bad) {
00435                     $this->out(__('What is the association type?', true));
00436                     $prompt = "1. belongsTo\n";
00437                     $prompt .= "2. hasOne\n";
00438                     $prompt .= "3. hasMany\n";
00439                     $prompt .= "4. hasAndBelongsToMany\n";
00440                     $assocType = intval($this->in($prompt, null, __("Enter a number", true)));
00441 
00442                     if (intval($assocType) < 1 || intval($assocType) > 4) {
00443                         $this->out(__('The selection you entered was invalid. Please enter a number between 1 and 4.', true));
00444                     } else {
00445                         $bad = false;
00446                     }
00447                 }
00448                 $this->out(__('For the following options be very careful to match your setup exactly. Any spelling mistakes will cause errors.', true));
00449                 $this->hr();
00450                 $alias = $this->in(__('What is the alias for this association?', true));
00451                 $className = $this->in(sprintf(__('What className will %s use?', true), $alias), null, $alias );
00452                 $suggestedForeignKey = null;
00453                 if ($assocType == '1') {
00454                     $showKeys = $possibleKeys[$model->table];
00455                     $suggestedForeignKey = $this->_modelKey($alias);
00456                 } else {
00457                     $otherTable = Inflector::tableize($className);
00458                     if (in_array($otherTable, $this->__tables)) {
00459                         if ($assocType < '4') {
00460                             $showKeys = $possibleKeys[$otherTable];
00461                         } else {
00462                             $showKeys = null;
00463                         }
00464                     } else {
00465                         $otherTable = $this->in(__('What is the table for this model?', true));
00466                         $showKeys = $possibleKeys[$otherTable];
00467                     }
00468                     $suggestedForeignKey = $this->_modelKey($model->name);
00469                 }
00470                 if (!empty($showKeys)) {
00471                     $this->out(__('A helpful List of possible keys', true));
00472                     for ($i = 0; $i < count($showKeys); $i++) {
00473                         $this->out($i + 1 . ". " . $showKeys[$i]);
00474                     }
00475                     $foreignKey = $this->in(__('What is the foreignKey?', true), null, __("Enter a number", true));
00476                     if (intval($foreignKey) > 0 && intval($foreignKey) <= $i ) {
00477                         $foreignKey = $showKeys[intval($foreignKey) - 1];
00478                     }
00479                 }
00480                 if (!isset($foreignKey)) {
00481                     $foreignKey = $this->in(__('What is the foreignKey? Specify your own.', true), null, $suggestedForeignKey);
00482                 }
00483                 if ($assocType == '4') {
00484                     $associationForeignKey = $this->in(__('What is the associationForeignKey?', true), null, $this->_modelKey($model->name));
00485                     $joinTable = $this->in(__('What is the joinTable?', true));
00486                 }
00487                 $associations[$assocs[$assocType]] = array_values((array)$associations[$assocs[$assocType]]);
00488                 $count = count($associations[$assocs[$assocType]]);
00489                 $i = ($count > 0) ? $count : 0;
00490                 $associations[$assocs[$assocType]][$i]['alias'] = $alias;
00491                 $associations[$assocs[$assocType]][$i]['className'] = $className;
00492                 $associations[$assocs[$assocType]][$i]['foreignKey'] = $foreignKey;
00493                 if ($assocType == '4') {
00494                     $associations[$assocs[$assocType]][$i]['associationForeignKey'] = $associationForeignKey;
00495                     $associations[$assocs[$assocType]][$i]['joinTable'] = $joinTable;
00496                 }
00497                 $wannaDoMoreAssoc = $this->in(__('Define another association?', true), array('y','n'), 'y');
00498             }
00499         }
00500         return $associations;
00501     }
00502 /**
00503  * Assembles and writes a Model file.
00504  *
00505  * @param mixed $name Model name or object
00506  * @param mixed $associations if array and $name is not an object assume Model associations array otherwise boolean interactive
00507  * @param array $validate Validation rules
00508  * @param string $primaryKey Primary key to use
00509  * @param string $useTable Table to use
00510  * @param string $useDbConfig Database configuration setting to use
00511  * @access private
00512  */
00513     function bake($name, $associations = array(),  $validate = array(), $primaryKey = 'id', $useTable = null, $useDbConfig = 'default') {
00514 
00515         if (is_object($name)) {
00516             if (!is_array($associations)) {
00517                 $associations = $this->doAssociations($name, $associations);
00518                 $validate = $this->doValidation($name, $associations);
00519             }
00520             $primaryKey = $name->primaryKey;
00521             $useTable = $name->table;
00522             $useDbConfig = $name->useDbConfig;
00523             $name = $name->name;
00524         }
00525 
00526         $out = "<?php\n";
00527         $out .= "class {$name} extends {$this->plugin}AppModel {\n\n";
00528         $out .= "\tvar \$name = '{$name}';\n";
00529 
00530         if ($useDbConfig !== 'default') {
00531             $out .= "\tvar \$useDbConfig = '$useDbConfig';\n";
00532         }
00533 
00534         if (($useTable && $useTable !== Inflector::tableize($name)) || $useTable === false) {
00535             $table = "'$useTable'";
00536             if (!$useTable) {
00537                 $table = 'false';
00538             }
00539             $out .= "\tvar \$useTable = $table;\n";
00540         }
00541 
00542         if ($primaryKey !== 'id') {
00543             $out .= "\tvar \$primaryKey = '$primaryKey';\n";
00544         }
00545 
00546         $validateCount = count($validate);
00547         if (is_array($validate) && $validateCount > 0) {
00548             $out .= "\tvar \$validate = array(\n";
00549             $keys = array_keys($validate);
00550             for ($i = 0; $i < $validateCount; $i++) {
00551                 $out .= "\t\t'" . $keys[$i] . "' => array('" . $validate[$keys[$i]] . "')";
00552                 if ($i + 1 < $validateCount) {
00553                     $out .= ",";
00554                 }
00555                 $out .= "\n";
00556             }
00557             $out .= "\t);\n";
00558         }
00559         $out .= "\n";
00560 
00561         if (!empty($associations)) {
00562             if (!empty($associations['belongsTo']) || !empty($associations['$hasOne']) || !empty($associations['hasMany']) || !empty($associations['hasAndBelongsToMany'])) {
00563                 $out.= "\t//The Associations below have been created with all possible keys, those that are not needed can be removed\n";
00564             }
00565 
00566             if (!empty($associations['belongsTo'])) {
00567                 $out .= "\tvar \$belongsTo = array(\n";
00568                 $belongsToCount = count($associations['belongsTo']);
00569 
00570                 for ($i = 0; $i < $belongsToCount; $i++) {
00571                     $out .= "\t\t\t'{$associations['belongsTo'][$i]['alias']}' => ";
00572                     $out .= "array('className' => '{$associations['belongsTo'][$i]['className']}',\n";
00573                     $out .= "\t\t\t\t\t\t\t\t'foreignKey' => '{$associations['belongsTo'][$i]['foreignKey']}',\n";
00574                     $out .= "\t\t\t\t\t\t\t\t'conditions' => '',\n";
00575                     $out .= "\t\t\t\t\t\t\t\t'fields' => '',\n";
00576                     $out .= "\t\t\t\t\t\t\t\t'order' => ''\n";
00577                     $out .= "\t\t\t)";
00578                     if ($i + 1 < $belongsToCount) {
00579                         $out .= ",";
00580                     }
00581                     $out .= "\n";
00582 
00583                 }
00584                 $out .= "\t);\n\n";
00585             }
00586 
00587             if (!empty($associations['hasOne'])) {
00588                 $out .= "\tvar \$hasOne = array(\n";
00589                 $hasOneCount = count($associations['hasOne']);
00590 
00591                 for ($i = 0; $i < $hasOneCount; $i++) {
00592                     $out .= "\t\t\t'{$associations['hasOne'][$i]['alias']}' => ";
00593                     $out .= "array('className' => '{$associations['hasOne'][$i]['className']}',\n";
00594                     $out .= "\t\t\t\t\t\t\t\t'foreignKey' => '{$associations['hasOne'][$i]['foreignKey']}',\n";
00595                     $out .= "\t\t\t\t\t\t\t\t'dependent' => false,\n";
00596                     $out .= "\t\t\t\t\t\t\t\t'conditions' => '',\n";
00597                     $out .= "\t\t\t\t\t\t\t\t'fields' => '',\n";
00598                     $out .= "\t\t\t\t\t\t\t\t'order' => ''\n";
00599                     $out .= "\t\t\t)";
00600                     if ($i + 1 < $hasOneCount) {
00601                         $out .= ",";
00602                     }
00603                     $out .= "\n";
00604 
00605                 }
00606                 $out .= "\t);\n\n";
00607             }
00608 
00609             if (!empty($associations['hasMany'])) {
00610                 $out .= "\tvar \$hasMany = array(\n";
00611                 $hasManyCount = count($associations['hasMany']);
00612 
00613                 for ($i = 0; $i < $hasManyCount; $i++) {
00614                     $out .= "\t\t\t'{$associations['hasMany'][$i]['alias']}' => ";
00615                     $out .= "array('className' => '{$associations['hasMany'][$i]['className']}',\n";
00616                     $out .= "\t\t\t\t\t\t\t\t'foreignKey' => '{$associations['hasMany'][$i]['foreignKey']}',\n";
00617                     $out .= "\t\t\t\t\t\t\t\t'dependent' => false,\n";
00618                     $out .= "\t\t\t\t\t\t\t\t'conditions' => '',\n";
00619                     $out .= "\t\t\t\t\t\t\t\t'fields' => '',\n";
00620                     $out .= "\t\t\t\t\t\t\t\t'order' => '',\n";
00621                     $out .= "\t\t\t\t\t\t\t\t'limit' => '',\n";
00622                     $out .= "\t\t\t\t\t\t\t\t'offset' => '',\n";
00623                     $out .= "\t\t\t\t\t\t\t\t'exclusive' => '',\n";
00624                     $out .= "\t\t\t\t\t\t\t\t'finderQuery' => '',\n";
00625                     $out .= "\t\t\t\t\t\t\t\t'counterQuery' => ''\n";
00626                     $out .= "\t\t\t)";
00627                     if ($i + 1 < $hasManyCount) {
00628                         $out .= ",";
00629                     }
00630                     $out .= "\n";
00631                 }
00632                 $out .= "\t);\n\n";
00633             }
00634 
00635             if (!empty($associations['hasAndBelongsToMany'])) {
00636                 $out .= "\tvar \$hasAndBelongsToMany = array(\n";
00637                 $hasAndBelongsToManyCount = count($associations['hasAndBelongsToMany']);
00638 
00639                 for ($i = 0; $i < $hasAndBelongsToManyCount; $i++) {
00640                     $out .= "\t\t\t'{$associations['hasAndBelongsToMany'][$i]['alias']}' => ";
00641                     $out .= "array('className' => '{$associations['hasAndBelongsToMany'][$i]['className']}',\n";
00642                     $out .= "\t\t\t\t\t\t'joinTable' => '{$associations['hasAndBelongsToMany'][$i]['joinTable']}',\n";
00643                     $out .= "\t\t\t\t\t\t'foreignKey' => '{$associations['hasAndBelongsToMany'][$i]['foreignKey']}',\n";
00644                     $out .= "\t\t\t\t\t\t'associationForeignKey' => '{$associations['hasAndBelongsToMany'][$i]['associationForeignKey']}',\n";
00645                     $out .= "\t\t\t\t\t\t'unique' => true,\n";
00646                     $out .= "\t\t\t\t\t\t'conditions' => '',\n";
00647                     $out .= "\t\t\t\t\t\t'fields' => '',\n";
00648                     $out .= "\t\t\t\t\t\t'order' => '',\n";
00649                     $out .= "\t\t\t\t\t\t'limit' => '',\n";
00650                     $out .= "\t\t\t\t\t\t'offset' => '',\n";
00651                     $out .= "\t\t\t\t\t\t'finderQuery' => '',\n";
00652                     $out .= "\t\t\t\t\t\t'deleteQuery' => '',\n";
00653                     $out .= "\t\t\t\t\t\t'insertQuery' => ''\n";
00654                     $out .= "\t\t\t)";
00655                     if ($i + 1 < $hasAndBelongsToManyCount) {
00656                         $out .= ",";
00657                     }
00658                     $out .= "\n";
00659                 }
00660                 $out .= "\t);\n\n";
00661             }
00662         }
00663         $out .= "}\n";
00664         $out .= "?>";
00665         $filename = $this->path . Inflector::underscore($name) . '.php';
00666         $this->out("\nBaking model class for $name...");
00667         return $this->createFile($filename, $out);
00668     }
00669 
00670 /**
00671  * Assembles and writes a unit test file
00672  *
00673  * @param string $className Model class name
00674  * @access private
00675  */
00676     function bakeTest($className, $useTable = null, $associations = array()) {
00677         $results = $this->fixture($className, $useTable);
00678 
00679         if ($results) {
00680             $fixtureInc = 'app';
00681             if ($this->plugin) {
00682                 $fixtureInc = 'plugin.'.Inflector::underscore($this->plugin);
00683             }
00684 
00685             $fixture[] = "'{$fixtureInc}." . Inflector::underscore($className) ."'";
00686 
00687             if (!empty($associations)) {
00688                 $assoc[] = Set::extract($associations, 'belongsTo.{n}.className');
00689                 $assoc[] = Set::extract($associations, 'hasOne.{n}.className');
00690                 $assoc[] = Set::extract($associations, 'hasMany.{n}.className');
00691                 foreach ($assoc as $key => $value) {
00692                     if (is_array($value)) {
00693                         foreach ($value as $class) {
00694                             $fixture[] = "'{$fixtureInc}." . Inflector::underscore($class) ."'";
00695                         }
00696                     }
00697                 }
00698             }
00699             $fixture = join(", ", $fixture);
00700 
00701             $import = $className;
00702             if (isset($this->plugin)) {
00703                 $import = $this->plugin . '.' . $className;
00704             }
00705 
00706             $out = "App::import('Model', '$import');\n\n";
00707             $out .= "class Test{$className} extends {$className} {\n";
00708             $out .= "\tvar \$cacheSources = false;\n";
00709             $out .= "\tvar \$useDbConfig  = 'test_suite';\n}\n\n";
00710             $out .= "class {$className}TestCase extends CakeTestCase {\n";
00711             $out .= "\tvar \${$className} = null;\n";
00712             $out .= "\tvar \$fixtures = array($fixture);\n\n";
00713             $out .= "\tfunction start() {\n\t\tparent::start();\n\t\t\$this->{$className} = new Test{$className}();\n\t}\n\n";
00714             $out .= "\tfunction test{$className}Instance() {\n";
00715             $out .= "\t\t\$this->assertTrue(is_a(\$this->{$className}, '{$className}'));\n\t}\n\n";
00716             $out .= "\tfunction test{$className}Find() {\n";
00717             $out .= "\t\t\$results = \$this->{$className}->recursive = -1;\n";
00718             $out .= "\t\t\$results = \$this->{$className}->find('first');\n\t\t\$this->assertTrue(!empty(\$results));\n\n";
00719             $out .= "\t\t\$expected = array('$className' => array(\n$results\n\t\t\t));\n";
00720             $out .= "\t\t\$this->assertEqual(\$results, \$expected);\n\t}\n}\n";
00721 
00722             $path = MODEL_TESTS;
00723             if (isset($this->plugin)) {
00724                 $pluginPath = 'plugins' . DS . Inflector::underscore($this->plugin) . DS;
00725                 $path = APP . $pluginPath . 'tests' . DS . 'cases' . DS . 'models' . DS;
00726             }
00727 
00728             $filename = Inflector::underscore($className).'.test.php';
00729             $this->out("\nBaking unit test for $className...");
00730 
00731             $header = '$Id';
00732             $content = "<?php \n/* SVN FILE: $header$ */\n/* ". $className ." Test cases generated on: " . date('Y-m-d H:m:s') . " : ". time() . "*/\n{$out}?>";
00733             return $this->createFile($path . $filename, $content);
00734         }
00735         return false;
00736     }
00737 /**
00738  * outputs the a list of possible models or controllers from database
00739  *
00740  * @param string $useDbConfig Database configuration name
00741  * @access public
00742  */
00743     function listAll($useDbConfig = 'default', $interactive = true) {
00744         $db =& ConnectionManager::getDataSource($useDbConfig);
00745         $usePrefix = empty($db->config['prefix']) ? '' : $db->config['prefix'];
00746         if ($usePrefix) {
00747             $tables = array();
00748             foreach ($db->listSources() as $table) {
00749                 if (!strncmp($table, $usePrefix, strlen($usePrefix))) {
00750                     $tables[] = substr($table, strlen($usePrefix));
00751                 }
00752             }
00753         } else {
00754             $tables = $db->listSources();
00755         }
00756         if (empty($tables)) {
00757             $this->err(__('Your database does not have any tables.', true));
00758             $this->_stop();
00759         }
00760 
00761         $this->__tables = $tables;
00762 
00763         if ($interactive === true) {
00764             $this->out(__('Possible Models based on your current database:', true));
00765             $this->_modelNames = array();
00766             $count = count($tables);
00767             for ($i = 0; $i < $count; $i++) {
00768                 $this->_modelNames[] = $this->_modelName($tables[$i]);
00769                 $this->out($i + 1 . ". " . $this->_modelNames[$i]);
00770             }
00771         }
00772     }
00773 /**
00774  * Forces the user to specify the model he wants to bake, and returns the selected model name.
00775  *
00776  * @return string the model name
00777  * @access public
00778  */
00779     function getName($useDbConfig) {
00780         $this->listAll($useDbConfig);
00781 
00782         $enteredModel = '';
00783 
00784         while ($enteredModel == '') {
00785             $enteredModel = $this->in(__("Enter a number from the list above, type in the name of another model, or 'q' to exit", true), null, 'q');
00786 
00787             if ($enteredModel === 'q') {
00788                 $this->out(__("Exit", true));
00789                 $this->_stop();
00790             }
00791 
00792             if ($enteredModel == '' || intval($enteredModel) > count($this->_modelNames)) {
00793                 $this->err(__("The model name you supplied was empty, or the number you selected was not an option. Please try again.", true));
00794                 $enteredModel = '';
00795             }
00796         }
00797 
00798         if (intval($enteredModel) > 0 && intval($enteredModel) <= count($this->_modelNames)) {
00799             $currentModelName = $this->_modelNames[intval($enteredModel) - 1];
00800         } else {
00801             $currentModelName = $enteredModel;
00802         }
00803 
00804         return $currentModelName;
00805     }
00806 /**
00807  * Displays help contents
00808  *
00809  * @access public
00810  */
00811     function help() {
00812         $this->hr();
00813         $this->out("Usage: cake bake model <arg1>");
00814         $this->hr();
00815         $this->out('Commands:');
00816         $this->out("\n\tmodel\n\t\tbakes model in interactive mode.");
00817         $this->out("\n\tmodel <name>\n\t\tbakes model file with no associations or validation");
00818         $this->out("");
00819         $this->_stop();
00820     }
00821 /**
00822  * Builds the tests fixtures for the model and create the file
00823  *
00824  * @param string $model the name of the model
00825  * @param string $useTable table name
00826  * @return array $records, used in ModelTask::bakeTest() to create $expected
00827  * @todo move this to a task
00828  */
00829     function fixture($model, $useTable = null) {
00830         if (!class_exists('CakeSchema')) {
00831             App::import('Model', 'Schema');
00832         }
00833         $out = "\nclass {$model}Fixture extends CakeTestFixture {\n";
00834         $out .= "\tvar \$name = '$model';\n";
00835 
00836         if (!$useTable) {
00837             $useTable = Inflector::tableize($model);
00838         } else {
00839             $out .= "\tvar \$table = '$useTable';\n";
00840         }
00841         $schema = new CakeSchema();
00842         $data = $schema->read(array('models' => false));
00843 
00844         if (!isset($data['tables'][$useTable])) {
00845             return false;
00846         }
00847         $tables[$model] = $data['tables'][$useTable];
00848 
00849         foreach ($tables as $table => $fields) {
00850             if (!is_numeric($table) && $table !== 'missing') {
00851                 $out .= "\tvar \$fields = array(\n";
00852                 $records = array();
00853                 if (is_array($fields)) {
00854                     $cols = array();
00855                     foreach ($fields as $field => $value) {
00856                         if ($field != 'indexes') {
00857                             if (is_string($value)) {
00858                                 $type = $value;
00859                                 $value = array('type'=> $type);
00860                             }
00861                             $col = "\t\t\t'{$field}' => array('type'=>'" . $value['type'] . "', ";
00862 
00863                             switch ($value['type']) {
00864                                 case 'integer':
00865                                     $insert = 1;
00866                                 break;
00867                                 case 'string';
00868                                     $insert = "Lorem ipsum dolor sit amet";
00869                                     if (!empty($value['length'])) {
00870                                         $insert = substr($insert, 0, (int)$value['length'] - 2);
00871                                     }
00872                                     $insert = "'$insert'";
00873                                 break;
00874                                 case 'datetime':
00875                                     $ts = date('Y-m-d H:i:s');
00876                                     $insert = "'$ts'";
00877                                 break;
00878                                 case 'date':
00879                                     $ts = date('Y-m-d');
00880                                     $insert = "'$ts'";
00881                                 break;
00882                                 case 'time':
00883                                     $ts = date('H:i:s');
00884                                     $insert = "'$ts'";
00885                                 break;
00886                                 case 'boolean':
00887                                     $insert = 1;
00888                                 break;
00889                                 case 'text':
00890                                     $insert =
00891                                     '\'Lorem ipsum dolor sit amet, aliquet feugiat. Convallis morbi fringilla gravida,
00892                                     phasellus feugiat dapibus velit nunc, pulvinar eget sollicitudin venenatis cum nullam,
00893                                     vivamus ut a sed, mollitia lectus. Nulla vestibulum massa neque ut et, id hendrerit sit,
00894                                     feugiat in taciti enim proin nibh, tempor dignissim, rhoncus duis vestibulum nunc mattis convallis.
00895                                     Orci aliquet, in lorem et velit maecenas luctus, wisi nulla at, mauris nam ut a, lorem et et elit eu.
00896                                     Sed dui facilisi, adipiscing mollis lacus congue integer, faucibus consectetuer eros amet sit sit,
00897                                     magna dolor posuere. Placeat et, ac occaecat rutrum ante ut fusce. Sit velit sit porttitor non enim purus,
00898                                     id semper consectetuer justo enim, nulla etiam quis justo condimentum vel, malesuada ligula arcu. Nisl neque,
00899                                     ligula cras suscipit nunc eget, et tellus in varius urna odio est. Fuga urna dis metus euismod laoreet orci,
00900                                     litora luctus suspendisse sed id luctus ut. Pede volutpat quam vitae, ut ornare wisi. Velit dis tincidunt,
00901                                     pede vel eleifend nec curabitur dui pellentesque, volutpat taciti aliquet vivamus viverra, eget tellus ut
00902                                     feugiat lacinia mauris sed, lacinia et felis.\'';
00903                                 break;
00904                             }
00905                             $records[] = "\t\t\t'$field'  => $insert";
00906                             unset($value['type']);
00907                             $col .= join(', ',  $schema->__values($value));
00908                         } else {
00909                             $col = "\t\t\t'indexes' => array(";
00910                             $props = array();
00911                             foreach ((array)$value as $key => $index) {
00912                                 $props[] = "'{$key}' => array(".join(', ',  $schema->__values($index)).")";
00913                             }
00914                             $col .= join(', ', $props);
00915                         }
00916                         $col .= ")";
00917                         $cols[] = $col;
00918                     }
00919                     $out .= join(",\n", $cols);
00920                 }
00921                 $out .= "\n\t\t\t);\n";
00922             }
00923         }
00924         $records = join(",\n", $records);
00925         $out .= "\tvar \$records = array(array(\n$records\n\t\t\t));\n";
00926         $out .= "}\n";
00927         $path = TESTS . DS . 'fixtures' . DS;
00928         if (isset($this->plugin)) {
00929             $pluginPath = 'plugins' . DS . Inflector::underscore($this->plugin) . DS;
00930             $path = APP . $pluginPath . 'tests' . DS . 'fixtures' . DS;
00931         }
00932         $filename = Inflector::underscore($model).'_fixture.php';
00933         $header = '$Id';
00934         $content = "<?php \n/* SVN FILE: $header$ */\n/* ". $model ." Fixture generated on: " . date('Y-m-d H:m:s') . " : ". time() . "*/\n{$out}?>";
00935         $this->out("\nBaking test fixture for $model...");
00936         if ($this->createFile($path . $filename, $content)) {
00937             return $records;
00938         }
00939         return false;
00940     }
00941 }
00942 ?>