PageRenderTime 63ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/cake/console/libs/tasks/model.php

https://github.com/msadouni/cakephp2x
PHP | 907 lines | 615 code | 62 blank | 230 comment | 148 complexity | e93328a9bd4673b6e35d819d8d7f06b1 MD5 | raw file
  1. <?php
  2. /**
  3. * The ModelTask handles creating and updating models files.
  4. *
  5. * Long description for file
  6. *
  7. * PHP Version 5.x
  8. *
  9. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  10. * Copyright 2005-2009, Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. *
  12. * Licensed under The MIT License
  13. * Redistributions of files must retain the above copyright notice.
  14. *
  15. * @copyright Copyright 2005-2009, Cake Software Foundation, Inc. (http://cakefoundation.org)
  16. * @link http://cakephp.org CakePHP(tm) Project
  17. * @package cake
  18. * @subpackage cake.cake.console.libs.tasks
  19. * @since CakePHP(tm) v 1.2
  20. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  21. */
  22. /**
  23. * Task class for creating and updating model files.
  24. *
  25. * @package cake
  26. * @subpackage cake.cake.console.libs.tasks
  27. */
  28. class ModelTask extends Shell {
  29. /**
  30. * Name of plugin
  31. *
  32. * @var string
  33. * @access public
  34. */
  35. var $plugin = null;
  36. /**
  37. * Name of the db connection used.
  38. *
  39. * @var string
  40. * @access public
  41. */
  42. var $connection = null;
  43. /**
  44. * path to MODELS directory
  45. *
  46. * @var string
  47. * @access public
  48. */
  49. var $path = MODELS;
  50. /**
  51. * tasks
  52. *
  53. * @var array
  54. * @access public
  55. */
  56. var $tasks = array('DbConfig', 'Fixture', 'Test', 'Template');
  57. /**
  58. * Holds tables found on connection.
  59. *
  60. * @var array
  61. */
  62. var $__tables = array();
  63. /**
  64. * Holds validation method map.
  65. *
  66. * @var array
  67. */
  68. var $__validations = array();
  69. /**
  70. * Execution method always used for tasks
  71. *
  72. * @access public
  73. */
  74. function execute() {
  75. App::import('Model', 'Model', false);
  76. if (empty($this->args)) {
  77. $this->__interactive();
  78. }
  79. if (!empty($this->args[0])) {
  80. $this->interactive = false;
  81. if (!isset($this->connection)) {
  82. $this->connection = 'default';
  83. }
  84. if (strtolower($this->args[0]) == 'all') {
  85. return $this->all();
  86. }
  87. $model = Inflector::camelize($this->args[0]);
  88. $object = $this->_getModelObject($model);
  89. if ($this->bake($object, false)) {
  90. if ($this->_checkUnitTest()) {
  91. $this->bakeFixture($model);
  92. $this->bakeTest($model);
  93. }
  94. }
  95. }
  96. }
  97. /**
  98. * Bake all models at once.
  99. *
  100. * @return void
  101. */
  102. function all() {
  103. $this->listAll($this->connection, false);
  104. $unitTestExists = $this->_checkUnitTest();
  105. foreach ($this->__tables as $table) {
  106. $modelClass = Inflector::classify($table);
  107. $this->out(sprintf(__('Baking %s', true), $modelClass));
  108. $object = $this->_getModelObject($modelClass);
  109. if ($this->bake($object, false) && $unitTestExists) {
  110. $this->bakeFixture($modelClass);
  111. $this->bakeTest($modelClass);
  112. }
  113. }
  114. }
  115. /**
  116. * Get a model object for a class name.
  117. *
  118. * @param string $className Name of class you want model to be.
  119. * @return object Model instance
  120. */
  121. function &_getModelObject($className, $table = null) {
  122. if (!$table) {
  123. $table = Inflector::tableize($className);
  124. }
  125. $object = new Model(array('name' => $className, 'table' => $table, 'ds' => $this->connection));
  126. return $object;
  127. }
  128. /**
  129. * Generate a key value list of options and a prompt.
  130. *
  131. * @param array $options Array of options to use for the selections. indexes must start at 0
  132. * @param string $prompt Prompt to use for options list.
  133. * @param integer $default The default option for the given prompt.
  134. * @return result of user choice.
  135. */
  136. function inOptions($options, $prompt = null, $default = null) {
  137. $valid = false;
  138. $max = count($options);
  139. while (!$valid) {
  140. foreach ($options as $i => $option) {
  141. $this->out($i + 1 .'. ' . $option);
  142. }
  143. if (empty($prompt)) {
  144. $prompt = __('Make a selection from the choices above', true);
  145. }
  146. $choice = $this->in($prompt, null, $default);
  147. if (intval($choice) > 0 && intval($choice) <= $max) {
  148. $valid = true;
  149. }
  150. }
  151. return $choice - 1;
  152. }
  153. /**
  154. * Handles interactive baking
  155. *
  156. * @access private
  157. */
  158. function __interactive() {
  159. $this->hr();
  160. $this->out(sprintf("Bake Model\nPath: %s", $this->path));
  161. $this->hr();
  162. $this->interactive = true;
  163. $primaryKey = 'id';
  164. $validate = $associations = array();
  165. if (empty($this->connection)) {
  166. $this->connection = $this->DbConfig->getConfig();
  167. }
  168. $currentModelName = $this->getName();
  169. $useTable = $this->getTable($currentModelName);
  170. $db = ConnectionManager::getDataSource($this->connection);
  171. $fullTableName = $db->fullTableName($useTable);
  172. if (in_array($useTable, $this->__tables)) {
  173. $tempModel = new Model(array('name' => $currentModelName, 'table' => $useTable, 'ds' => $this->connection));
  174. $fields = $tempModel->schema();
  175. if (!array_key_exists('id', $fields)) {
  176. $primaryKey = $this->findPrimaryKey($fields);
  177. }
  178. } else {
  179. $this->err(sprintf(__('Table %s does not exist, cannot bake a model without a table.', true), $useTable));
  180. $this->_stop();
  181. return false;
  182. }
  183. $displayField = $tempModel->hasField(array('name', 'title'));
  184. if (!$displayField) {
  185. $displayField = $this->findDisplayField($tempModel->schema());
  186. }
  187. $prompt = __("Would you like to supply validation criteria \nfor the fields in your model?", true);
  188. $wannaDoValidation = $this->in($prompt, array('y','n'), 'y');
  189. if (array_search($useTable, $this->__tables) !== false && strtolower($wannaDoValidation) == 'y') {
  190. $validate = $this->doValidation($tempModel);
  191. }
  192. $prompt = __("Would you like to define model associations\n(hasMany, hasOne, belongsTo, etc.)?", true);
  193. $wannaDoAssoc = $this->in($prompt, array('y','n'), 'y');
  194. if (strtolower($wannaDoAssoc) == 'y') {
  195. $associations = $this->doAssociations($tempModel);
  196. }
  197. $this->out();
  198. $this->hr();
  199. $this->out(__('The following Model will be created:', true));
  200. $this->hr();
  201. $this->out("Name: " . $currentModelName);
  202. if ($this->connection !== 'default') {
  203. $this->out(sprintf(__("DB Config: %s", true), $this->connection));
  204. }
  205. if ($fullTableName !== Inflector::tableize($currentModelName)) {
  206. $this->out(sprintf(__("DB Table: %s", true), $fullTableName));
  207. }
  208. if ($primaryKey != 'id') {
  209. $this->out(sprintf(__("Primary Key: %s", true), $primaryKey));
  210. }
  211. if (!empty($validate)) {
  212. $this->out(sprintf(__("Validation: %s", true), print_r($validate, true)));
  213. }
  214. if (!empty($associations)) {
  215. $this->out(__("Associations:", true));
  216. $assocKeys = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
  217. foreach ($assocKeys as $assocKey) {
  218. $this->_printAssociation($currentModelName, $assocKey, $associations);
  219. }
  220. }
  221. $this->hr();
  222. $looksGood = $this->in(__('Look okay?', true), array('y','n'), 'y');
  223. if (strtolower($looksGood) == 'y') {
  224. $vars = compact('associations', 'validate', 'primaryKey', 'useTable', 'displayField');
  225. $vars['useDbConfig'] = $this->connection;
  226. if ($this->bake($currentModelName, $vars)) {
  227. if ($this->_checkUnitTest()) {
  228. $this->bakeFixture($currentModelName, $useTable);
  229. $this->bakeTest($currentModelName, $useTable, $associations);
  230. }
  231. }
  232. } else {
  233. return false;
  234. }
  235. }
  236. /**
  237. * Print out all the associations of a particular type
  238. *
  239. * @param string $modelName Name of the model relations belong to.
  240. * @param string $type Name of association you want to see. i.e. 'belongsTo'
  241. * @param string $associations Collection of associations.
  242. * @access protected
  243. * @return void
  244. */
  245. function _printAssociation($modelName, $type, $associations) {
  246. if (!empty($associations[$type])) {
  247. for ($i = 0; $i < count($associations[$type]); $i++) {
  248. $out = "\t" . $modelName . ' ' . $type . ' ' . $associations[$type][$i]['alias'];
  249. $this->out($out);
  250. }
  251. }
  252. }
  253. /**
  254. * Finds a primary Key in a list of fields.
  255. *
  256. * @param array $fields Array of fields that might have a primary key.
  257. * @return string Name of field that is a primary key.
  258. * @access public
  259. */
  260. function findPrimaryKey($fields) {
  261. foreach ($fields as $name => $field) {
  262. if (isset($field['key']) && $field['key'] == 'primary') {
  263. break;
  264. }
  265. }
  266. return $this->in(__('What is the primaryKey?', true), null, $name);
  267. }
  268. /**
  269. * interact with the user to find the displayField value for a model.
  270. *
  271. * @param array $fields Array of fields to look for and choose as a displayField
  272. * @return mixed Name of field to use for displayField or false if the user declines to choose
  273. */
  274. function findDisplayField($fields) {
  275. $fieldNames = array_keys($fields);
  276. $prompt = __("A displayField could not be automatically detected\nwould you like to choose one?", true);
  277. $continue = $this->in($prompt, array('y', 'n'));
  278. if (strtolower($continue) == 'n') {
  279. return false;
  280. }
  281. $prompt = __('Choose a field from the options above:', true);
  282. $choice = $this->inOptions($fieldNames, $prompt);
  283. return $fieldNames[$choice];
  284. }
  285. /**
  286. * Handles Generation and user interaction for creating validation.
  287. *
  288. * @param object $model Model to have validations generated for.
  289. * @return array $validate Array of user selected validations.
  290. * @access public
  291. */
  292. function doValidation(&$model) {
  293. if (!is_object($model)) {
  294. return false;
  295. }
  296. $fields = $model->schema();
  297. if (empty($fields)) {
  298. return false;
  299. }
  300. $validate = array();
  301. $this->initValidations();
  302. foreach ($fields as $fieldName => $field) {
  303. $validation = $this->fieldValidation($fieldName, $field, $model->primaryKey);
  304. if (!empty($validation)) {
  305. $validate[$fieldName] = $validation;
  306. }
  307. }
  308. return $validate;
  309. }
  310. /**
  311. * Populate the __validations array
  312. *
  313. * @return void
  314. */
  315. function initValidations() {
  316. $options = $choices = array();
  317. if (class_exists('Validation')) {
  318. $parent = get_class_methods(get_parent_class('Validation'));
  319. $options = get_class_methods('Validation');
  320. $options = array_diff($options, $parent);
  321. }
  322. sort($options);
  323. $index = 1;
  324. foreach ($options as $key => $option) {
  325. $choices[$index] = strtolower($option);
  326. $index++;
  327. }
  328. $this->__validations = $choices;
  329. return $choices;
  330. }
  331. /**
  332. * Does individual field validation handling.
  333. *
  334. * @param string $fieldName Name of field to be validated.
  335. * @param array $metaData metadata for field
  336. * @return array Array of validation for the field.
  337. */
  338. function fieldValidation($fieldName, $metaData, $primaryKey = 'id') {
  339. $defaultChoice = count($this->__validations);
  340. $validate = $alreadyChosen = array();
  341. $anotherValidator = 'y';
  342. while ($anotherValidator == 'y') {
  343. if ($this->interactive) {
  344. $this->out();
  345. $this->out(sprintf(__('Field: %s', true), $fieldName));
  346. $this->out(sprintf(__('Type: %s', true), $metaData['type']));
  347. $this->hr();
  348. $this->out(__('Please select one of the following validation options:', true));
  349. $this->hr();
  350. }
  351. $prompt = '';
  352. for ($i = 1; $i < $defaultChoice; $i++) {
  353. $prompt .= $i . ' - ' . $this->__validations[$i] . "\n";
  354. }
  355. $prompt .= sprintf(__("%s - Do not do any validation on this field.\n", true), $defaultChoice);
  356. $prompt .= __("... or enter in a valid regex validation string.\n", true);
  357. $methods = array_flip($this->__validations);
  358. $guess = $defaultChoice;
  359. if ($metaData['null'] != 1 && !in_array($fieldName, array($primaryKey, 'created', 'modified', 'updated'))) {
  360. if ($fieldName == 'email') {
  361. $guess = $methods['email'];
  362. } elseif ($metaData['type'] == 'string') {
  363. $guess = $methods['notempty'];
  364. } elseif ($metaData['type'] == 'integer') {
  365. $guess = $methods['numeric'];
  366. } elseif ($metaData['type'] == 'boolean') {
  367. $guess = $methods['boolean'];
  368. } elseif ($metaData['type'] == 'date') {
  369. $guess = $methods['date'];
  370. } elseif ($metaData['type'] == 'time') {
  371. $guess = $methods['time'];
  372. }
  373. }
  374. if ($this->interactive === true) {
  375. $choice = $this->in($prompt, null, $guess);
  376. if (in_array($choice, $alreadyChosen)) {
  377. $this->out(__("You have already chosen that validation rule,\nplease choose again", true));
  378. continue;
  379. }
  380. $alreadyChosen[] = $choice;
  381. } else {
  382. $choice = $guess;
  383. }
  384. $validatorName = $this->__validations[$choice];
  385. if ($choice != $defaultChoice) {
  386. if (is_numeric($choice) && isset($this->__validations[$choice])) {
  387. $validate[$validatorName] = $this->__validations[$choice];
  388. } else {
  389. $validate[$validatorName] = $choice;
  390. }
  391. }
  392. if ($this->interactive == true && $choice != $defaultChoice) {
  393. $anotherValidator = $this->in(__('Would you like to add another validation rule?', true), array('y', 'n'), 'n');
  394. } else {
  395. $anotherValidator = 'n';
  396. }
  397. }
  398. return $validate;
  399. }
  400. /**
  401. * Handles associations
  402. *
  403. * @param object $model
  404. * @return array $assocaitons
  405. * @access public
  406. */
  407. function doAssociations(&$model) {
  408. if (!is_object($model)) {
  409. return false;
  410. }
  411. if ($this->interactive === true) {
  412. $this->out(__('One moment while the associations are detected.', true));
  413. }
  414. $fields = $model->schema();
  415. if (empty($fields)) {
  416. return false;
  417. }
  418. $associations = array(
  419. 'belongsTo' => array(), 'hasMany' => array(), 'hasOne'=> array(), 'hasAndBelongsToMany' => array()
  420. );
  421. $possibleKeys = array();
  422. $associations = $this->findBelongsTo($model, $associations);
  423. $associations = $this->findHasOneAndMany($model, $associations);
  424. $associations = $this->findHasAndBelongsToMany($model, $associations);
  425. if ($this->interactive !== true) {
  426. unset($associations['hasOne']);
  427. }
  428. if ($this->interactive === true) {
  429. $this->hr();
  430. if (empty($associations)) {
  431. $this->out(__('None found.', true));
  432. } else {
  433. $this->out(__('Please confirm the following associations:', true));
  434. $this->hr();
  435. $associations = $this->confirmAssociations($model, $associations);
  436. }
  437. $associations = $this->doMoreAssociations($model, $associations);
  438. }
  439. return $associations;
  440. }
  441. /**
  442. * Find belongsTo relations and add them to the associations list.
  443. *
  444. * @param object $model Model instance of model being generated.
  445. * @param array $associations Array of inprogress associations
  446. * @return array $associations with belongsTo added in.
  447. */
  448. function findBelongsTo(&$model, $associations) {
  449. $fields = $model->schema();
  450. foreach ($fields as $fieldName => $field) {
  451. $offset = strpos($fieldName, '_id');
  452. if ($fieldName != $model->primaryKey && $fieldName != 'parent_id' && $offset !== false) {
  453. $tmpModelName = $this->_modelNameFromKey($fieldName);
  454. $associations['belongsTo'][] = array(
  455. 'alias' => $tmpModelName,
  456. 'className' => $tmpModelName,
  457. 'foreignKey' => $fieldName,
  458. );
  459. } elseif ($fieldName == 'parent_id') {
  460. $associations['belongsTo'][] = array(
  461. 'alias' => 'Parent' . $model->name,
  462. 'className' => $model->name,
  463. 'foreignKey' => $fieldName,
  464. );
  465. }
  466. }
  467. return $associations;
  468. }
  469. /**
  470. * Find the hasOne and HasMany relations and add them to associations list
  471. *
  472. * @param object $model Model instance being generated
  473. * @param array $associations Array of inprogress associations
  474. * @return array $associations with hasOne and hasMany added in.
  475. */
  476. function findHasOneAndMany(&$model, $associations) {
  477. $foreignKey = $this->_modelKey($model->name);
  478. foreach ($this->__tables as $otherTable) {
  479. $tempOtherModel = $this->_getModelObject($this->_modelName($otherTable), $otherTable);
  480. $modelFieldsTemp = $tempOtherModel->schema();
  481. $pattern = '/_' . preg_quote($model->table, '/') . '|' . preg_quote($model->table, '/') . '_/';
  482. $possibleJoinTable = preg_match($pattern , $otherTable);
  483. if ($possibleJoinTable == true) {
  484. continue;
  485. }
  486. foreach ($modelFieldsTemp as $fieldName => $field) {
  487. $assoc = false;
  488. if ($fieldName != $model->primaryKey && $fieldName == $foreignKey) {
  489. $assoc = array(
  490. 'alias' => $tempOtherModel->name,
  491. 'className' => $tempOtherModel->name,
  492. 'foreignKey' => $fieldName
  493. );
  494. } elseif ($otherTable == $model->table && $fieldName == 'parent_id') {
  495. $assoc = array(
  496. 'alias' => 'Child' . $model->name,
  497. 'className' => $model->name,
  498. 'foreignKey' => $fieldName
  499. );
  500. }
  501. if ($assoc) {
  502. $associations['hasOne'][] = $assoc;
  503. $associations['hasMany'][] = $assoc;
  504. }
  505. }
  506. }
  507. return $associations;
  508. }
  509. /**
  510. * Find the hasAndBelongsToMany relations and add them to associations list
  511. *
  512. * @param object $model Model instance being generated
  513. * @param array $associations Array of inprogress associations
  514. * @return array $associations with hasAndBelongsToMany added in.
  515. */
  516. function findHasAndBelongsToMany(&$model, $associations) {
  517. $foreignKey = $this->_modelKey($model->name);
  518. foreach ($this->__tables as $otherTable) {
  519. $tempOtherModel = $this->_getModelObject($this->_modelName($otherTable), $otherTable);
  520. $modelFieldsTemp = $tempOtherModel->schema();
  521. $offset = strpos($otherTable, $model->table . '_');
  522. $otherOffset = strpos($otherTable, '_' . $model->table);
  523. if ($offset !== false) {
  524. $offset = strlen($model->table . '_');
  525. $habtmName = $this->_modelName(substr($otherTable, $offset));
  526. $associations['hasAndBelongsToMany'][] = array(
  527. 'alias' => $habtmName,
  528. 'className' => $habtmName,
  529. 'foreignKey' => $foreignKey,
  530. 'associationForeignKey' => $this->_modelKey($habtmName),
  531. 'joinTable' => $otherTable
  532. );
  533. } elseif ($otherOffset !== false) {
  534. $habtmName = $this->_modelName(substr($otherTable, 0, $otherOffset));
  535. $associations['hasAndBelongsToMany'][] = array(
  536. 'alias' => $habtmName,
  537. 'className' => $habtmName,
  538. 'foreignKey' => $foreignKey,
  539. 'associationForeignKey' => $this->_modelKey($habtmName),
  540. 'joinTable' => $otherTable
  541. );
  542. }
  543. }
  544. return $associations;
  545. }
  546. /**
  547. * Interact with the user and confirm associations.
  548. *
  549. * @param array $model Temporary Model instance.
  550. * @param array $associations Array of associations to be confirmed.
  551. * @return array Array of confirmed associations
  552. */
  553. function confirmAssociations(&$model, $associations) {
  554. foreach ($associations as $type => $settings) {
  555. if (!empty($associations[$type])) {
  556. $count = count($associations[$type]);
  557. $response = 'y';
  558. for ($i = 0; $i < $count; $i++) {
  559. $prompt = "{$model->name} {$type} {$associations[$type][$i]['alias']}";
  560. $response = $this->in("{$prompt}?", array('y','n'), 'y');
  561. if ('n' == strtolower($response)) {
  562. unset($associations[$type][$i]);
  563. } elseif ($type == 'hasMany') {
  564. unset($associations['hasOne'][$i]);
  565. }
  566. }
  567. $associations[$type] = array_merge($associations[$type]);
  568. }
  569. }
  570. return $associations;
  571. }
  572. /**
  573. * Interact with the user and generate additional non-conventional associations
  574. *
  575. * @param object $model Temporary model instance
  576. * @param array $associations Array of associations.
  577. * @return array Array of associations.
  578. */
  579. function doMoreAssociations($model, $associations) {
  580. $prompt = __('Would you like to define some additional model associations?', true);
  581. $wannaDoMoreAssoc = $this->in($prompt, array('y','n'), 'n');
  582. $possibleKeys = $this->_generatePossibleKeys();
  583. while (strtolower($wannaDoMoreAssoc) == 'y') {
  584. $assocs = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
  585. $this->out(__('What is the association type?', true));
  586. $assocType = intval($this->inOptions($assocs, __('Enter a number',true)));
  587. $this->out(__("For the following options be very careful to match your setup exactly.\nAny spelling mistakes will cause errors.", true));
  588. $this->hr();
  589. $alias = $this->in(__('What is the alias for this association?', true));
  590. $className = $this->in(sprintf(__('What className will %s use?', true), $alias), null, $alias );
  591. $suggestedForeignKey = null;
  592. if ($assocType == 0) {
  593. $showKeys = $possibleKeys[$model->table];
  594. $suggestedForeignKey = $this->_modelKey($alias);
  595. } else {
  596. $otherTable = Inflector::tableize($className);
  597. if (in_array($otherTable, $this->__tables)) {
  598. if ($assocType < 3) {
  599. $showKeys = $possibleKeys[$otherTable];
  600. } else {
  601. $showKeys = null;
  602. }
  603. } else {
  604. $otherTable = $this->in(__('What is the table for this model?', true));
  605. $showKeys = $possibleKeys[$otherTable];
  606. }
  607. $suggestedForeignKey = $this->_modelKey($model->name);
  608. }
  609. if (!empty($showKeys)) {
  610. $this->out(__('A helpful List of possible keys', true));
  611. $foreignKey = $this->inOptions($showKeys, __('What is the foreignKey?', true));
  612. $foreignKey = $showKeys[intval($foreignKey)];
  613. }
  614. if (!isset($foreignKey)) {
  615. $foreignKey = $this->in(__('What is the foreignKey? Specify your own.', true), null, $suggestedForeignKey);
  616. }
  617. if ($assocType == 3) {
  618. $associationForeignKey = $this->in(__('What is the associationForeignKey?', true), null, $this->_modelKey($model->name));
  619. $joinTable = $this->in(__('What is the joinTable?', true));
  620. }
  621. $associations[$assocs[$assocType]] = array_values((array)$associations[$assocs[$assocType]]);
  622. $count = count($associations[$assocs[$assocType]]);
  623. $i = ($count > 0) ? $count : 0;
  624. $associations[$assocs[$assocType]][$i]['alias'] = $alias;
  625. $associations[$assocs[$assocType]][$i]['className'] = $className;
  626. $associations[$assocs[$assocType]][$i]['foreignKey'] = $foreignKey;
  627. if ($assocType == 3) {
  628. $associations[$assocs[$assocType]][$i]['associationForeignKey'] = $associationForeignKey;
  629. $associations[$assocs[$assocType]][$i]['joinTable'] = $joinTable;
  630. }
  631. $wannaDoMoreAssoc = $this->in(__('Define another association?', true), array('y','n'), 'y');
  632. }
  633. return $associations;
  634. }
  635. /**
  636. * Finds all possible keys to use on custom associations.
  637. *
  638. * @return array array of tables and possible keys
  639. */
  640. function _generatePossibleKeys() {
  641. $possible = array();
  642. foreach ($this->__tables as $otherTable) {
  643. $tempOtherModel = & new Model(array('table' => $otherTable, 'ds' => $this->connection));
  644. $modelFieldsTemp = $tempOtherModel->schema();
  645. foreach ($modelFieldsTemp as $fieldName => $field) {
  646. if ($field['type'] == 'integer' || $field['type'] == 'string') {
  647. $possible[$otherTable][] = $fieldName;
  648. }
  649. }
  650. }
  651. return $possible;
  652. }
  653. /**
  654. * Assembles and writes a Model file.
  655. *
  656. * @param mixed $name Model name or object
  657. * @param mixed $data if array and $name is not an object assume bake data, otherwise boolean.
  658. * @access private
  659. */
  660. function bake($name, $data = array()) {
  661. if (is_object($name)) {
  662. if ($data == false) {
  663. $data = $associations = array();
  664. $data['associations'] = $this->doAssociations($name, $associations);
  665. $data['validate'] = $this->doValidation($name);
  666. }
  667. $data['primaryKey'] = $name->primaryKey;
  668. $data['useTable'] = $name->table;
  669. $data['useDbConfig'] = $name->useDbConfig;
  670. $data['name'] = $name = $name->name;
  671. } else {
  672. $data['name'] = $name;
  673. }
  674. $defaults = array('associations' => array(), 'validate' => array(), 'primaryKey' => 'id',
  675. 'useTable' => null, 'useDbConfig' => 'default', 'displayField' => null);
  676. $data = array_merge($defaults, $data);
  677. $this->Template->set($data);
  678. $this->Template->set('plugin', Inflector::camelize($this->plugin));
  679. $out = $this->Template->generate('classes', 'model');
  680. $path = $this->path;
  681. if (isset($this->plugin)) {
  682. $path = $this->_pluginPath($this->plugin) . 'models' . DS;
  683. }
  684. $filename = $path . Inflector::underscore($name) . '.php';
  685. $this->out("\nBaking model class for $name...");
  686. $this->createFile($filename, $out);
  687. return $out;
  688. }
  689. /**
  690. * Assembles and writes a unit test file
  691. *
  692. * @param string $className Model class name
  693. * @access private
  694. */
  695. function bakeTest($className) {
  696. $this->Test->plugin = $this->plugin;
  697. $this->Test->connection = $this->connection;
  698. return $this->Test->bake('Model', $className);
  699. }
  700. /**
  701. * outputs the a list of possible models or controllers from database
  702. *
  703. * @param string $useDbConfig Database configuration name
  704. * @access public
  705. */
  706. function listAll($useDbConfig = null) {
  707. $this->__tables = $this->getAllTables($useDbConfig);
  708. if ($this->interactive === true) {
  709. $this->out(__('Possible Models based on your current database:', true));
  710. $this->_modelNames = array();
  711. $count = count($this->__tables);
  712. for ($i = 0; $i < $count; $i++) {
  713. $this->_modelNames[] = $this->_modelName($this->__tables[$i]);
  714. $this->out($i + 1 . ". " . $this->_modelNames[$i]);
  715. }
  716. }
  717. return $this->__tables;
  718. }
  719. /**
  720. * Interact with the user to determine the table name of a particular model
  721. *
  722. * @param string $modelName Name of the model you want a table for.
  723. * @param string $useDbConfig Name of the database config you want to get tables from.
  724. * @return void
  725. */
  726. function getTable($modelName, $useDbConfig = null) {
  727. if (!isset($useDbConfig)) {
  728. $useDbConfig = $this->connection;
  729. }
  730. App::import('Model', 'ConnectionManager', false);
  731. $db = ConnectionManager::getDataSource($useDbConfig);
  732. $useTable = Inflector::tableize($modelName);
  733. $fullTableName = $db->fullTableName($useTable, false);
  734. $tableIsGood = false;
  735. if (array_search($useTable, $this->__tables) === false) {
  736. $this->out();
  737. $this->out(sprintf(__("Given your model named '%s',\nCake would expect a database table named '%s'", true), $modelName, $fullTableName));
  738. $tableIsGood = $this->in(__('Do you want to use this table?', true), array('y','n'), 'y');
  739. }
  740. if (strtolower($tableIsGood) == 'n') {
  741. $useTable = $this->in(__('What is the name of the table?', true));
  742. }
  743. return $useTable;
  744. }
  745. /**
  746. * Get an Array of all the tables in the supplied connection
  747. * will halt the script if no tables are found.
  748. *
  749. * @param string $useDbConfig Connection name to scan.
  750. * @return array Array of tables in the database.
  751. */
  752. function getAllTables($useDbConfig = null) {
  753. if (!isset($useDbConfig)) {
  754. $useDbConfig = $this->connection;
  755. }
  756. App::import('Model', 'ConnectionManager', false);
  757. $tables = array();
  758. $db = ConnectionManager::getDataSource($useDbConfig);
  759. $usePrefix = empty($db->config['prefix']) ? '' : $db->config['prefix'];
  760. if ($usePrefix) {
  761. foreach ($db->listSources() as $table) {
  762. if (!strncmp($table, $usePrefix, strlen($usePrefix))) {
  763. $tables[] = substr($table, strlen($usePrefix));
  764. }
  765. }
  766. } else {
  767. $tables = $db->listSources();
  768. }
  769. if (empty($tables)) {
  770. $this->err(__('Your database does not have any tables.', true));
  771. $this->_stop();
  772. }
  773. return $tables;
  774. }
  775. /**
  776. * Forces the user to specify the model he wants to bake, and returns the selected model name.
  777. *
  778. * @return string the model name
  779. * @access public
  780. */
  781. function getName($useDbConfig = null) {
  782. $this->listAll($useDbConfig);
  783. $enteredModel = '';
  784. while ($enteredModel == '') {
  785. $enteredModel = $this->in(__("Enter a number from the list above,\ntype in the name of another model, or 'q' to exit", true), null, 'q');
  786. if ($enteredModel === 'q') {
  787. $this->out(__("Exit", true));
  788. $this->_stop();
  789. }
  790. if ($enteredModel == '' || intval($enteredModel) > count($this->_modelNames)) {
  791. $this->err(__("The model name you supplied was empty,\nor the number you selected was not an option. Please try again.", true));
  792. $enteredModel = '';
  793. }
  794. }
  795. if (intval($enteredModel) > 0 && intval($enteredModel) <= count($this->_modelNames)) {
  796. $currentModelName = $this->_modelNames[intval($enteredModel) - 1];
  797. } else {
  798. $currentModelName = $enteredModel;
  799. }
  800. return $currentModelName;
  801. }
  802. /**
  803. * Displays help contents
  804. *
  805. * @access public
  806. */
  807. function help() {
  808. $this->hr();
  809. $this->out("Usage: cake bake model <arg1>");
  810. $this->hr();
  811. $this->out('Arguments:');
  812. $this->out();
  813. $this->out("<name>");
  814. $this->out("\tName of the model to bake. Can use Plugin.name");
  815. $this->out("\tas a shortcut for plugin baking.");
  816. $this->out();
  817. $this->out('Commands:');
  818. $this->out();
  819. $this->out("model");
  820. $this->out("\tbakes model in interactive mode.");
  821. $this->out();
  822. $this->out("model <name>");
  823. $this->out("\tbakes model file with no associations or validation");
  824. $this->out();
  825. $this->out("model all");
  826. $this->out("\tbakes all model files with associations and validation");
  827. $this->out();
  828. $this->_stop();
  829. }
  830. /**
  831. * Interact with FixtureTask to automatically bake fixtures when baking models.
  832. *
  833. * @param string $className Name of class to bake fixture for
  834. * @param string $useTable Optional table name for fixture to use.
  835. * @access public
  836. * @return void
  837. * @see FixtureTask::bake
  838. */
  839. function bakeFixture($className, $useTable = null) {
  840. $this->Fixture->connection = $this->connection;
  841. $this->Fixture->plugin = $this->plugin;
  842. $this->Fixture->bake($className, $useTable);
  843. }
  844. }
  845. ?>