1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25:
26: App::import('Model', 'ConnectionManager');
27: 28: 29: 30: 31: 32:
33: class ModelTask extends Shell {
34: 35: 36: 37: 38: 39:
40: var $plugin = null;
41: 42: 43: 44: 45: 46:
47: var $path = MODELS;
48: 49: 50: 51: 52: 53:
54: var $tasks = array('DbConfig');
55: 56: 57: 58: 59:
60: function execute() {
61: if (empty($this->args)) {
62: $this->__interactive();
63: }
64:
65: if (!empty($this->args[0])) {
66: $model = Inflector::camelize($this->args[0]);
67: $this->useDbConfig = 'default';
68: if ($this->bake($model)) {
69: if ($this->_checkUnitTest()) {
70: $this->bakeTest($model);
71: }
72: }
73: }
74: }
75: 76: 77: 78: 79:
80: function __interactive() {
81: $this->hr();
82: $this->out(sprintf("Bake Model\nPath: %s", $this->path));
83: $this->hr();
84: $this->interactive = true;
85:
86: $useTable = null;
87: $primaryKey = 'id';
88: $validate = array();
89: $associations = array('belongsTo'=> array(), 'hasOne'=> array(), 'hasMany' => array(), 'hasAndBelongsToMany'=> array());
90:
91: $useDbConfig = 'default';
92: $configs = get_class_vars('DATABASE_CONFIG');
93:
94: if (!is_array($configs)) {
95: return $this->DbConfig->execute();
96: }
97:
98: $connections = array_keys($configs);
99: if (count($connections) > 1) {
100: $useDbConfig = $this->in(__('Use Database Config', true) .':', $connections, 'default');
101: }
102: $this->useDbConfig = $useDbConfig;
103:
104: $currentModelName = $this->getName($useDbConfig);
105: $db =& ConnectionManager::getDataSource($useDbConfig);
106: $useTable = Inflector::tableize($currentModelName);
107: $fullTableName = $db->fullTableName($useTable, false);
108: $tableIsGood = false;
109:
110: if (array_search($useTable, $this->__tables) === false) {
111: $this->out('');
112: $this->out(sprintf(__("Given your model named '%s', Cake would expect a database table named %s", true), $currentModelName, $fullTableName));
113: $tableIsGood = $this->in(__('Do you want to use this table?', true), array('y','n'), 'y');
114: }
115:
116: if (strtolower($tableIsGood) == 'n' || strtolower($tableIsGood) == 'no') {
117: $useTable = $this->in(__('What is the name of the table (enter "null" to use NO table)?', true));
118: }
119:
120: while ($tableIsGood == false && strtolower($useTable) != 'null') {
121: if (is_array($this->__tables) && !in_array($useTable, $this->__tables)) {
122: $fullTableName = $db->fullTableName($useTable, false);
123: $this->out($fullTableName . ' does not exist.');
124: $useTable = $this->in(__('What is the name of the table (enter "null" to use NO table)?', true));
125: $tableIsGood = false;
126: } else {
127: $tableIsGood = true;
128: }
129: }
130:
131: $wannaDoValidation = $this->in(__('Would you like to supply validation criteria for the fields in your model?', true), array('y','n'), 'y');
132:
133: if (in_array($useTable, $this->__tables)) {
134: App::import('Model');
135: $tempModel = new Model(array('name' => $currentModelName, 'table' => $useTable, 'ds' => $useDbConfig));
136:
137: $fields = $tempModel->schema();
138: if (!array_key_exists('id', $fields)) {
139: foreach ($fields as $name => $field) {
140: if (isset($field['key']) && $field['key'] == 'primary') {
141: break;
142: }
143: }
144: $primaryKey = $this->in(__('What is the primaryKey?', true), null, $name);
145: }
146: }
147:
148: if (array_search($useTable, $this->__tables) !== false && (strtolower($wannaDoValidation) == 'y' || strtolower($wannaDoValidation) == 'yes')) {
149: $validate = $this->doValidation($tempModel);
150: }
151:
152: $wannaDoAssoc = $this->in(__('Would you like to define model associations (hasMany, hasOne, belongsTo, etc.)?', true), array('y','n'), 'y');
153: if ((strtolower($wannaDoAssoc) == 'y' || strtolower($wannaDoAssoc) == 'yes')) {
154: $associations = $this->doAssociations($tempModel);
155: }
156:
157: $this->out('');
158: $this->hr();
159: $this->out(__('The following Model will be created:', true));
160: $this->hr();
161: $this->out("Name: " . $currentModelName);
162:
163: if ($useDbConfig !== 'default') {
164: $this->out("DB Config: " . $useDbConfig);
165: }
166: if ($fullTableName !== Inflector::tableize($currentModelName)) {
167: $this->out("DB Table: " . $fullTableName);
168: }
169: if ($primaryKey != 'id') {
170: $this->out("Primary Key: " . $primaryKey);
171: }
172: if (!empty($validate)) {
173: $this->out("Validation: " . print_r($validate, true));
174: }
175: if (!empty($associations)) {
176: $this->out("Associations:");
177:
178: if (!empty($associations['belongsTo'])) {
179: for ($i = 0; $i < count($associations['belongsTo']); $i++) {
180: $this->out(" $currentModelName belongsTo {$associations['belongsTo'][$i]['alias']}");
181: }
182: }
183:
184: if (!empty($associations['hasOne'])) {
185: for ($i = 0; $i < count($associations['hasOne']); $i++) {
186: $this->out(" $currentModelName hasOne {$associations['hasOne'][$i]['alias']}");
187: }
188: }
189:
190: if (!empty($associations['hasMany'])) {
191: for ($i = 0; $i < count($associations['hasMany']); $i++) {
192: $this->out(" $currentModelName hasMany {$associations['hasMany'][$i]['alias']}");
193: }
194: }
195:
196: if (!empty($associations['hasAndBelongsToMany'])) {
197: for ($i = 0; $i < count($associations['hasAndBelongsToMany']); $i++) {
198: $this->out(" $currentModelName hasAndBelongsToMany {$associations['hasAndBelongsToMany'][$i]['alias']}");
199: }
200: }
201: }
202: $this->hr();
203: $looksGood = $this->in(__('Look okay?', true), array('y','n'), 'y');
204:
205: if (strtolower($looksGood) == 'y' || strtolower($looksGood) == 'yes') {
206: if ($this->bake($currentModelName, $associations, $validate, $primaryKey, $useTable, $useDbConfig)) {
207: if ($this->_checkUnitTest()) {
208: $this->bakeTest($currentModelName, $useTable, $associations);
209: }
210: }
211: } else {
212: return false;
213: }
214: }
215: 216: 217: 218: 219: 220: 221: 222:
223: function doValidation(&$model, $interactive = true) {
224: if (!is_object($model)) {
225: return false;
226: }
227: $fields = $model->schema();
228:
229: if (empty($fields)) {
230: return false;
231: }
232:
233: $validate = array();
234:
235: $options = array();
236:
237: if (class_exists('Validation')) {
238: $parent = get_class_methods(get_parent_class('Validation'));
239: $options = array_diff(get_class_methods('Validation'), $parent);
240: }
241:
242: foreach ($fields as $fieldName => $field) {
243: $prompt = 'Field: ' . $fieldName . "\n";
244: $prompt .= 'Type: ' . $field['type'] . "\n";
245: $prompt .= '---------------------------------------------------------------'."\n";
246: $prompt .= 'Please select one of the following validation options:'."\n";
247: $prompt .= '---------------------------------------------------------------'."\n";
248:
249: sort($options);
250:
251: $skip = 1;
252: foreach ($options as $key => $option) {
253: if ($option{0} != '_' && strtolower($option) != 'getinstance') {
254: $prompt .= "{$skip} - {$option}\n";
255: $choices[$skip] = strtolower($option);
256: $skip++;
257: }
258: }
259:
260: $methods = array_flip($choices);
261:
262: $prompt .= "{$skip} - Do not do any validation on this field.\n";
263: $prompt .= "... or enter in a valid regex validation string.\n";
264:
265: $guess = $skip;
266: if ($field['null'] != 1 && $fieldName != $model->primaryKey && !in_array($fieldName, array('created', 'modified', 'updated'))) {
267: if ($fieldName == 'email') {
268: $guess = $methods['email'];
269: } elseif ($field['type'] == 'string') {
270: $guess = $methods['notempty'];
271: } elseif ($field['type'] == 'integer') {
272: $guess = $methods['numeric'];
273: } elseif ($field['type'] == 'boolean') {
274: $guess = $methods['numeric'];
275: } elseif ($field['type'] == 'datetime') {
276: $guess = $methods['date'];
277: }
278: }
279:
280: if ($interactive === true) {
281: $this->out('');
282: $choice = $this->in($prompt, null, $guess);
283: } else {
284: $choice = $guess;
285: }
286: if ($choice != $skip) {
287: if (is_numeric($choice) && isset($choices[$choice])) {
288: $validate[$fieldName] = $choices[$choice];
289: } else {
290: $validate[$fieldName] = $choice;
291: }
292: }
293: }
294: return $validate;
295: }
296:
297: 298: 299: 300: 301: 302: 303: 304:
305: function doAssociations(&$model, $interactive = true) {
306:
307: if (!is_object($model)) {
308: return false;
309: }
310: $this->out(__('One moment while the associations are detected.', true));
311:
312: $fields = $model->schema();
313:
314: if (empty($fields)) {
315: return false;
316: }
317:
318: $primaryKey = $model->primaryKey;
319: $foreignKey = $this->_modelKey($model->name);
320:
321: $associations = array('belongsTo' => array(), 'hasMany' => array(), 'hasOne'=> array(), 'hasAndBelongsToMany' => array());
322: $possibleKeys = array();
323:
324:
325: $i = 0;
326: foreach ($fields as $fieldName => $field) {
327: $offset = strpos($fieldName, '_id');
328: if ($fieldName != $model->primaryKey && $offset !== false) {
329: $tmpModelName = $this->_modelNameFromKey($fieldName);
330: $associations['belongsTo'][$i]['alias'] = $tmpModelName;
331: $associations['belongsTo'][$i]['className'] = $tmpModelName;
332: $associations['belongsTo'][$i]['foreignKey'] = $fieldName;
333: $i++;
334: }
335: }
336:
337: $i = $j = 0;
338:
339: foreach ($this->__tables as $otherTable) {
340: App::import('Model');
341: $tmpModelName = $this->_modelName($otherTable);
342: $tempOtherModel = & new Model(array('name' => $tmpModelName, 'table' => $otherTable, 'ds' => $model->useDbConfig));
343: $modelFieldsTemp = $tempOtherModel->schema();
344:
345: $offset = strpos($otherTable, $model->table . '_');
346: $otherOffset = strpos($otherTable, '_' . $model->table);
347:
348: foreach ($modelFieldsTemp as $fieldName => $field) {
349: if ($field['type'] == 'integer' || $field['type'] == 'string') {
350: $possibleKeys[$otherTable][] = $fieldName;
351: }
352: if ($fieldName != $model->primaryKey && $fieldName == $foreignKey && $offset === false && $otherOffset === false) {
353: $associations['hasOne'][$j]['alias'] = $tempOtherModel->name;
354: $associations['hasOne'][$j]['className'] = $tempOtherModel->name;
355: $associations['hasOne'][$j]['foreignKey'] = $fieldName;
356:
357: $associations['hasMany'][$j]['alias'] = $tempOtherModel->name;
358: $associations['hasMany'][$j]['className'] = $tempOtherModel->name;
359: $associations['hasMany'][$j]['foreignKey'] = $fieldName;
360: $j++;
361: }
362: }
363:
364: if ($offset !== false) {
365: $offset = strlen($model->table . '_');
366: $tmpModelName = $this->_modelName(substr($otherTable, $offset));
367: $associations['hasAndBelongsToMany'][$i]['alias'] = $tmpModelName;
368: $associations['hasAndBelongsToMany'][$i]['className'] = $tmpModelName;
369: $associations['hasAndBelongsToMany'][$i]['foreignKey'] = $foreignKey;
370: $associations['hasAndBelongsToMany'][$i]['associationForeignKey'] = $this->_modelKey($tmpModelName);
371: $associations['hasAndBelongsToMany'][$i]['joinTable'] = $otherTable;
372: $i++;
373: }
374:
375: if ($otherOffset !== false) {
376: $tmpModelName = $this->_modelName(substr($otherTable, 0, $otherOffset));
377: $associations['hasAndBelongsToMany'][$i]['alias'] = $tmpModelName;
378: $associations['hasAndBelongsToMany'][$i]['className'] = $tmpModelName;
379: $associations['hasAndBelongsToMany'][$i]['foreignKey'] = $foreignKey;
380: $associations['hasAndBelongsToMany'][$i]['associationForeignKey'] = $this->_modelKey($tmpModelName);
381: $associations['hasAndBelongsToMany'][$i]['joinTable'] = $otherTable;
382: $i++;
383: }
384: }
385:
386: if ($interactive !== true) {
387: unset($associations['hasOne']);
388: }
389:
390: if ($interactive === true) {
391: $this->hr();
392: if (empty($associations)) {
393: $this->out(__('None found.', true));
394: } else {
395: $this->out(__('Please confirm the following associations:', true));
396: $this->hr();
397: foreach ($associations as $type => $settings) {
398: if (!empty($associations[$type])) {
399: $count = count($associations[$type]);
400: $response = 'y';
401: for ($i = 0; $i < $count; $i++) {
402: $prompt = "{$model->name} {$type} {$associations[$type][$i]['alias']}";
403: $response = $this->in("{$prompt}?", array('y','n'), 'y');
404:
405: if ('n' == strtolower($response) || 'no' == strtolower($response)) {
406: unset($associations[$type][$i]);
407: } else {
408: if ($model->name === $associations[$type][$i]['alias']) {
409: if ($type === 'belongsTo') {
410: $alias = 'Parent' . $associations[$type][$i]['alias'];
411: }
412: if ($type === 'hasOne' || $type === 'hasMany') {
413: $alias = 'Child' . $associations[$type][$i]['alias'];
414: }
415:
416: $alternateAlias = $this->in(sprintf(__('This is a self join. Use %s as the alias', true), $alias), array('y', 'n'), 'y');
417:
418: if ('n' == strtolower($alternateAlias) || 'no' == strtolower($alternateAlias)) {
419: $associations[$type][$i]['alias'] = $this->in(__('Specify an alternate alias.', true));
420: } else {
421: $associations[$type][$i]['alias'] = $alias;
422: }
423: }
424: }
425: }
426: $associations[$type] = array_merge($associations[$type]);
427: }
428: }
429: }
430:
431: $wannaDoMoreAssoc = $this->in(__('Would you like to define some additional model associations?', true), array('y','n'), 'n');
432:
433: while ((strtolower($wannaDoMoreAssoc) == 'y' || strtolower($wannaDoMoreAssoc) == 'yes')) {
434: $assocs = array(1 => 'belongsTo', 2 => 'hasOne', 3 => 'hasMany', 4 => 'hasAndBelongsToMany');
435: $bad = true;
436: while ($bad) {
437: $this->out(__('What is the association type?', true));
438: $prompt = "1. belongsTo\n";
439: $prompt .= "2. hasOne\n";
440: $prompt .= "3. hasMany\n";
441: $prompt .= "4. hasAndBelongsToMany\n";
442: $assocType = intval($this->in($prompt, null, __("Enter a number", true)));
443:
444: if (intval($assocType) < 1 || intval($assocType) > 4) {
445: $this->out(__('The selection you entered was invalid. Please enter a number between 1 and 4.', true));
446: } else {
447: $bad = false;
448: }
449: }
450: $this->out(__('For the following options be very careful to match your setup exactly. Any spelling mistakes will cause errors.', true));
451: $this->hr();
452: $alias = $this->in(__('What is the alias for this association?', true));
453: $className = $this->in(sprintf(__('What className will %s use?', true), $alias), null, $alias );
454: $suggestedForeignKey = null;
455: if ($assocType == '1') {
456: $showKeys = $possibleKeys[$model->table];
457: $suggestedForeignKey = $this->_modelKey($alias);
458: } else {
459: $otherTable = Inflector::tableize($className);
460: if (in_array($otherTable, $this->__tables)) {
461: if ($assocType < '4') {
462: $showKeys = $possibleKeys[$otherTable];
463: } else {
464: $showKeys = null;
465: }
466: } else {
467: $otherTable = $this->in(__('What is the table for this model?', true));
468: $showKeys = $possibleKeys[$otherTable];
469: }
470: $suggestedForeignKey = $this->_modelKey($model->name);
471: }
472: if (!empty($showKeys)) {
473: $this->out(__('A helpful List of possible keys', true));
474: for ($i = 0; $i < count($showKeys); $i++) {
475: $this->out($i + 1 . ". " . $showKeys[$i]);
476: }
477: $foreignKey = $this->in(__('What is the foreignKey?', true), null, __("Enter a number", true));
478: if (intval($foreignKey) > 0 && intval($foreignKey) <= $i ) {
479: $foreignKey = $showKeys[intval($foreignKey) - 1];
480: }
481: }
482: if (!isset($foreignKey)) {
483: $foreignKey = $this->in(__('What is the foreignKey? Specify your own.', true), null, $suggestedForeignKey);
484: }
485: if ($assocType == '4') {
486: $associationForeignKey = $this->in(__('What is the associationForeignKey?', true), null, $this->_modelKey($model->name));
487: $joinTable = $this->in(__('What is the joinTable?', true));
488: }
489: $associations[$assocs[$assocType]] = array_values((array)$associations[$assocs[$assocType]]);
490: $count = count($associations[$assocs[$assocType]]);
491: $i = ($count > 0) ? $count : 0;
492: $associations[$assocs[$assocType]][$i]['alias'] = $alias;
493: $associations[$assocs[$assocType]][$i]['className'] = $className;
494: $associations[$assocs[$assocType]][$i]['foreignKey'] = $foreignKey;
495: if ($assocType == '4') {
496: $associations[$assocs[$assocType]][$i]['associationForeignKey'] = $associationForeignKey;
497: $associations[$assocs[$assocType]][$i]['joinTable'] = $joinTable;
498: }
499: $wannaDoMoreAssoc = $this->in(__('Define another association?', true), array('y','n'), 'y');
500: }
501: }
502: return $associations;
503: }
504: 505: 506: 507: 508: 509: 510: 511: 512: 513: 514:
515: function bake($name, $associations = array(), $validate = array(), $primaryKey = 'id', $useTable = null, $useDbConfig = 'default') {
516:
517: if (is_object($name)) {
518: if (!is_array($associations)) {
519: $associations = $this->doAssociations($name, $associations);
520: $validate = $this->doValidation($name, $associations);
521: }
522: $primaryKey = $name->primaryKey;
523: $useTable = $name->table;
524: $useDbConfig = $name->useDbConfig;
525: $name = $name->name;
526: }
527:
528: $out = "<?php\n";
529: $out .= "class {$name} extends {$this->plugin}AppModel {\n\n";
530: $out .= "\tvar \$name = '{$name}';\n";
531:
532: if ($useDbConfig !== 'default') {
533: $out .= "\tvar \$useDbConfig = '$useDbConfig';\n";
534: }
535:
536: if (($useTable && $useTable !== Inflector::tableize($name)) || $useTable === false) {
537: $table = "'$useTable'";
538: if (!$useTable) {
539: $table = 'false';
540: }
541: $out .= "\tvar \$useTable = $table;\n";
542: }
543:
544: if ($primaryKey !== 'id') {
545: $out .= "\tvar \$primaryKey = '$primaryKey';\n";
546: }
547:
548: $validateCount = count($validate);
549: if (is_array($validate) && $validateCount > 0) {
550: $out .= "\tvar \$validate = array(\n";
551: $keys = array_keys($validate);
552: for ($i = 0; $i < $validateCount; $i++) {
553: $val = "'" . $validate[$keys[$i]] . "'";
554: $out .= "\t\t'" . $keys[$i] . "' => array({$val})";
555: if ($i + 1 < $validateCount) {
556: $out .= ",";
557: }
558: $out .= "\n";
559: }
560: $out .= "\t);\n";
561: }
562: $out .= "\n";
563:
564: if (!empty($associations)) {
565: if (!empty($associations['belongsTo']) || !empty($associations['hasOne']) || !empty($associations['hasMany']) || !empty($associations['hasAndBelongsToMany'])) {
566: $out.= "\t//The Associations below have been created with all possible keys, those that are not needed can be removed\n";
567: }
568:
569: if (!empty($associations['belongsTo'])) {
570: $out .= "\tvar \$belongsTo = array(\n";
571: $belongsToCount = count($associations['belongsTo']);
572:
573: for ($i = 0; $i < $belongsToCount; $i++) {
574: $out .= "\t\t'{$associations['belongsTo'][$i]['alias']}' => array(\n";
575: $out .= "\t\t\t'className' => '{$associations['belongsTo'][$i]['className']}',\n";
576: $out .= "\t\t\t'foreignKey' => '{$associations['belongsTo'][$i]['foreignKey']}',\n";
577: $out .= "\t\t\t'conditions' => '',\n";
578: $out .= "\t\t\t'fields' => '',\n";
579: $out .= "\t\t\t'order' => ''\n";
580: $out .= "\t\t)";
581: if ($i + 1 < $belongsToCount) {
582: $out .= ",";
583: }
584: $out .= "\n";
585:
586: }
587: $out .= "\t);\n\n";
588: }
589:
590: if (!empty($associations['hasOne'])) {
591: $out .= "\tvar \$hasOne = array(\n";
592: $hasOneCount = count($associations['hasOne']);
593:
594: for ($i = 0; $i < $hasOneCount; $i++) {
595: $out .= "\t\t'{$associations['hasOne'][$i]['alias']}' => array(\n";
596: $out .= "\t\t\t'className' => '{$associations['hasOne'][$i]['className']}',\n";
597: $out .= "\t\t\t'foreignKey' => '{$associations['hasOne'][$i]['foreignKey']}',\n";
598: $out .= "\t\t\t'dependent' => false,\n";
599: $out .= "\t\t\t'conditions' => '',\n";
600: $out .= "\t\t\t'fields' => '',\n";
601: $out .= "\t\t\t'order' => ''\n";
602: $out .= "\t\t)";
603: if ($i + 1 < $hasOneCount) {
604: $out .= ",";
605: }
606: $out .= "\n";
607:
608: }
609: $out .= "\t);\n\n";
610: }
611:
612: if (!empty($associations['hasMany'])) {
613: $out .= "\tvar \$hasMany = array(\n";
614: $hasManyCount = count($associations['hasMany']);
615:
616: for ($i = 0; $i < $hasManyCount; $i++) {
617: $out .= "\t\t'{$associations['hasMany'][$i]['alias']}' => array(\n";
618: $out .= "\t\t\t'className' => '{$associations['hasMany'][$i]['className']}',\n";
619: $out .= "\t\t\t'foreignKey' => '{$associations['hasMany'][$i]['foreignKey']}',\n";
620: $out .= "\t\t\t'dependent' => false,\n";
621: $out .= "\t\t\t'conditions' => '',\n";
622: $out .= "\t\t\t'fields' => '',\n";
623: $out .= "\t\t\t'order' => '',\n";
624: $out .= "\t\t\t'limit' => '',\n";
625: $out .= "\t\t\t'offset' => '',\n";
626: $out .= "\t\t\t'exclusive' => '',\n";
627: $out .= "\t\t\t'finderQuery' => '',\n";
628: $out .= "\t\t\t'counterQuery' => ''\n";
629: $out .= "\t\t)";
630: if ($i + 1 < $hasManyCount) {
631: $out .= ",";
632: }
633: $out .= "\n";
634: }
635: $out .= "\t);\n\n";
636: }
637:
638: if (!empty($associations['hasAndBelongsToMany'])) {
639: $out .= "\tvar \$hasAndBelongsToMany = array(\n";
640: $hasAndBelongsToManyCount = count($associations['hasAndBelongsToMany']);
641:
642: for ($i = 0; $i < $hasAndBelongsToManyCount; $i++) {
643: $out .= "\t\t'{$associations['hasAndBelongsToMany'][$i]['alias']}' => array(\n";
644: $out .= "\t\t\t'className' => '{$associations['hasAndBelongsToMany'][$i]['className']}',\n";
645: $out .= "\t\t\t'joinTable' => '{$associations['hasAndBelongsToMany'][$i]['joinTable']}',\n";
646: $out .= "\t\t\t'foreignKey' => '{$associations['hasAndBelongsToMany'][$i]['foreignKey']}',\n";
647: $out .= "\t\t\t'associationForeignKey' => '{$associations['hasAndBelongsToMany'][$i]['associationForeignKey']}',\n";
648: $out .= "\t\t\t'unique' => true,\n";
649: $out .= "\t\t\t'conditions' => '',\n";
650: $out .= "\t\t\t'fields' => '',\n";
651: $out .= "\t\t\t'order' => '',\n";
652: $out .= "\t\t\t'limit' => '',\n";
653: $out .= "\t\t\t'offset' => '',\n";
654: $out .= "\t\t\t'finderQuery' => '',\n";
655: $out .= "\t\t\t'deleteQuery' => '',\n";
656: $out .= "\t\t\t'insertQuery' => ''\n";
657: $out .= "\t\t)";
658: if ($i + 1 < $hasAndBelongsToManyCount) {
659: $out .= ",";
660: }
661: $out .= "\n";
662: }
663: $out .= "\t);\n\n";
664: }
665: }
666: $out .= "}\n";
667: $out .= "?>";
668: ClassRegistry::flush();
669: $filename = $this->path . Inflector::underscore($name) . '.php';
670: $this->out("\nBaking model class for $name...");
671: return $this->createFile($filename, $out);
672: }
673:
674: 675: 676: 677: 678: 679:
680: function bakeTest($className, $useTable = null, $associations = array()) {
681: $results = $this->fixture($className, $useTable);
682:
683: if ($results) {
684: $fixtureInc = 'app';
685: if ($this->plugin) {
686: $fixtureInc = 'plugin.'.Inflector::underscore($this->plugin);
687: }
688:
689: $fixture[] = "'{$fixtureInc}." . Inflector::underscore($className) ."'";
690:
691: if (!empty($associations)) {
692: $assoc[] = Set::extract($associations, 'belongsTo.{n}.className');
693: $assoc[] = Set::extract($associations, 'hasOne.{n}.className');
694: $assoc[] = Set::extract($associations, 'hasMany.{n}.className');
695: foreach ($assoc as $key => $value) {
696: if (is_array($value)) {
697: foreach ($value as $class) {
698: $fixture[] = "'{$fixtureInc}." . Inflector::underscore($class) ."'";
699: }
700: }
701: }
702: }
703: $fixture = implode(", ", $fixture);
704:
705: $import = $className;
706: if (isset($this->plugin)) {
707: $import = $this->plugin . '.' . $className;
708: }
709:
710: $out = "App::import('Model', '$import');\n\n";
711: $out .= "class {$className}TestCase extends CakeTestCase {\n";
712: $out .= "\tvar \${$className} = null;\n";
713: $out .= "\tvar \$fixtures = array($fixture);\n\n";
714: $out .= "\tfunction startTest() {\n";
715: $out .= "\t\t\$this->{$className} =& ClassRegistry::init('{$className}');\n";
716: $out .= "\t}\n\n";
717: $out .= "\tfunction test{$className}Instance() {\n";
718: $out .= "\t\t\$this->assertTrue(is_a(\$this->{$className}, '{$className}'));\n";
719: $out .= "\t}\n\n";
720: $out .= "\tfunction test{$className}Find() {\n";
721: $out .= "\t\t\$this->{$className}->recursive = -1;\n";
722: $out .= "\t\t\$results = \$this->{$className}->find('first');\n\t\t\$this->assertTrue(!empty(\$results));\n\n";
723: $out .= "\t\t\$expected = array('$className' => array(\n$results\n\t\t));\n";
724: $out .= "\t\t\$this->assertEqual(\$results, \$expected);\n";
725: $out .= "\t}\n";
726: $out .= "}\n";
727:
728: $path = MODEL_TESTS;
729: if (isset($this->plugin)) {
730: $pluginPath = 'plugins' . DS . Inflector::underscore($this->plugin) . DS;
731: $path = APP . $pluginPath . 'tests' . DS . 'cases' . DS . 'models' . DS;
732: }
733:
734: $filename = Inflector::underscore($className).'.test.php';
735: $this->out("\nBaking unit test for $className...");
736:
737: $header = '$Id';
738: $content = "<?php \n/* SVN FILE: $header$ */\n/* " . $className . " Test cases generated on: " . date('Y-m-d H:i:s') . " : " . time() . "*/\n{$out}?>";
739: return $this->createFile($path . $filename, $content);
740: }
741: return false;
742: }
743: 744: 745: 746: 747: 748:
749: function listAll($useDbConfig = 'default', $interactive = true) {
750: $db =& ConnectionManager::getDataSource($useDbConfig);
751: $usePrefix = empty($db->config['prefix']) ? '' : $db->config['prefix'];
752: if ($usePrefix) {
753: $tables = array();
754: foreach ($db->listSources() as $table) {
755: if (!strncmp($table, $usePrefix, strlen($usePrefix))) {
756: $tables[] = substr($table, strlen($usePrefix));
757: }
758: }
759: } else {
760: $tables = $db->listSources();
761: }
762: if (empty($tables)) {
763: $this->err(__('Your database does not have any tables.', true));
764: $this->_stop();
765: }
766:
767: $this->__tables = $tables;
768:
769: if ($interactive === true) {
770: $this->out(__('Possible Models based on your current database:', true));
771: $this->_modelNames = array();
772: $count = count($tables);
773: for ($i = 0; $i < $count; $i++) {
774: $this->_modelNames[] = $this->_modelName($tables[$i]);
775: $this->out($i + 1 . ". " . $this->_modelNames[$i]);
776: }
777: }
778: }
779: 780: 781: 782: 783: 784:
785: function getName($useDbConfig) {
786: $this->listAll($useDbConfig);
787:
788: $enteredModel = '';
789:
790: while ($enteredModel == '') {
791: $enteredModel = $this->in(__("Enter a number from the list above, type in the name of another model, or 'q' to exit", true), null, 'q');
792:
793: if ($enteredModel === 'q') {
794: $this->out(__("Exit", true));
795: $this->_stop();
796: }
797:
798: if ($enteredModel == '' || intval($enteredModel) > count($this->_modelNames)) {
799: $this->err(__("The model name you supplied was empty, or the number you selected was not an option. Please try again.", true));
800: $enteredModel = '';
801: }
802: }
803:
804: if (intval($enteredModel) > 0 && intval($enteredModel) <= count($this->_modelNames)) {
805: $currentModelName = $this->_modelNames[intval($enteredModel) - 1];
806: } else {
807: $currentModelName = $enteredModel;
808: }
809:
810: return $currentModelName;
811: }
812: 813: 814: 815: 816:
817: function help() {
818: $this->hr();
819: $this->out("Usage: cake bake model <arg1>");
820: $this->hr();
821: $this->out('Commands:');
822: $this->out("\n\tmodel\n\t\tbakes model in interactive mode.");
823: $this->out("\n\tmodel <name>\n\t\tbakes model file with no associations or validation");
824: $this->out("");
825: $this->_stop();
826: }
827: 828: 829: 830: 831: 832: 833: 834:
835: function fixture($model, $useTable = null) {
836: if (!class_exists('CakeSchema')) {
837: App::import('Model', 'Schema');
838: }
839: $out = "\nclass {$model}Fixture extends CakeTestFixture {\n";
840: $out .= "\tvar \$name = '$model';\n";
841:
842: if (!$useTable) {
843: $useTable = Inflector::tableize($model);
844: } else {
845: $out .= "\tvar \$table = '$useTable';\n";
846: }
847: $schema = new CakeSchema();
848: $data = $schema->read(array('models' => false, 'connection' => $this->useDbConfig));
849:
850: if (!isset($data['tables'][$useTable])) {
851: return false;
852: }
853: $tables[$model] = $data['tables'][$useTable];
854:
855: foreach ($tables as $table => $fields) {
856: if (!is_numeric($table) && $table !== 'missing') {
857: $out .= "\tvar \$fields = array(\n";
858: $records = array();
859: if (is_array($fields)) {
860: $cols = array();
861: foreach ($fields as $field => $value) {
862: if ($field != 'indexes') {
863: if (is_string($value)) {
864: $type = $value;
865: $value = array('type'=> $type);
866: }
867: $col = "\t\t'{$field}' => array('type'=>'" . $value['type'] . "', ";
868:
869: switch ($value['type']) {
870: case 'float':
871: case 'integer':
872: $insert = 1;
873: break;
874: case 'binary':
875: case 'string';
876: $insert = "Lorem ipsum dolor sit amet";
877: if (!empty($value['length'])) {
878: $insert = substr($insert, 0, (int)$value['length'] - 2);
879: }
880: $insert = "'$insert'";
881: break;
882: case 'datetime':
883: $ts = date('Y-m-d H:i:s');
884: $insert = "'$ts'";
885: break;
886: case 'date':
887: $ts = date('Y-m-d');
888: $insert = "'$ts'";
889: break;
890: case 'time':
891: $ts = date('H:i:s');
892: $insert = "'$ts'";
893: break;
894: case 'boolean':
895: $insert = 1;
896: break;
897: case 'text':
898: $insert =
899: "'Lorem ipsum dolor sit amet, aliquet feugiat. Convallis morbi fringilla gravida,";
900: $insert .= "phasellus feugiat dapibus velit nunc, pulvinar eget sollicitudin venenatis cum nullam,";
901: $insert .= "vivamus ut a sed, mollitia lectus. Nulla vestibulum massa neque ut et, id hendrerit sit,";
902: $insert .= "feugiat in taciti enim proin nibh, tempor dignissim, rhoncus duis vestibulum nunc mattis convallis.'";
903: break;
904: }
905: $records[] = "\t\t'$field' => $insert";
906: unset($value['type']);
907: $col .= implode(', ', $schema->__values($value));
908: } else {
909: $col = "\t\t'indexes' => array(";
910: $props = array();
911: foreach ((array)$value as $key => $index) {
912: $props[] = "'{$key}' => array(" . implode(', ', $schema->__values($index)) . ")";
913: }
914: $col .= implode(', ', $props);
915: }
916: $col .= ")";
917: $cols[] = $col;
918: }
919: $out .= implode(",\n", $cols);
920: }
921: $out .= "\n\t);\n";
922: }
923: }
924: $records = implode(",\n", $records);
925: $out .= "\tvar \$records = array(array(\n$records\n\t));\n";
926: $out .= "}\n";
927: $path = TESTS . DS . 'fixtures' . DS;
928: if (isset($this->plugin)) {
929: $pluginPath = 'plugins' . DS . Inflector::underscore($this->plugin) . DS;
930: $path = APP . $pluginPath . 'tests' . DS . 'fixtures' . DS;
931: }
932: $filename = Inflector::underscore($model) . '_fixture.php';
933: $header = '$Id';
934: $content = "<?php \n/* SVN FILE: $header$ */\n/* " . $model . " Fixture generated on: " . date('Y-m-d H:i:s') . " : " . time() . "*/\n{$out}?>";
935: $this->out("\nBaking test fixture for $model...");
936: if ($this->createFile($path . $filename, $content)) {
937: return str_replace("\t\t", "\t\t\t", $records);
938: }
939: return false;
940: }
941: }
942: ?>