diff --git a/app/webroot/test.php b/app/webroot/test.php index 1e9a044ac..b4ac38340 100644 --- a/app/webroot/test.php +++ b/app/webroot/test.php @@ -86,7 +86,7 @@ if (!include(CORE_PATH . 'cake' . DS . 'bootstrap.php')) { trigger_error("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR); } -$corePath = Configure::corePaths('cake'); +$corePath = App::core('cake'); if (isset($corePath[0])) { define('TEST_CAKE_CORE_INCLUDE_PATH', rtrim($corePath[0], DS) . DS); } else { diff --git a/cake/basics.php b/cake/basics.php index cae6e65bd..1c1dd61d1 100644 --- a/cake/basics.php +++ b/cake/basics.php @@ -362,8 +362,8 @@ if (!function_exists('array_combine')) { */ function env($key) { if ($key == 'HTTPS') { - if (isset($_SERVER) && !empty($_SERVER)) { - return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on'); + if (isset($_SERVER['HTTPS'])) { + return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'); } return (strpos(env('SCRIPT_URI'), 'https://') === 0); } @@ -624,9 +624,9 @@ if (!function_exists('file_put_contents')) { } if ($return === false) { - echo I18n::translate($singular, $plural, null, 5, $count); + echo I18n::translate($singular, $plural, null, 6, $count); } else { - return I18n::translate($singular, $plural, null, 5, $count); + return I18n::translate($singular, $plural, null, 6, $count); } } /** @@ -672,9 +672,9 @@ if (!function_exists('file_put_contents')) { } if ($return === false) { - echo I18n::translate($singular, $plural, $domain, 5, $count); + echo I18n::translate($singular, $plural, $domain, 6, $count); } else { - return I18n::translate($singular, $plural, $domain, 5, $count); + return I18n::translate($singular, $plural, $domain, 6, $count); } } /** @@ -723,13 +723,13 @@ if (!function_exists('file_put_contents')) { * Valid categories are: LC_CTYPE, LC_NUMERIC, LC_TIME, LC_COLLATE, LC_MONETARY, LC_MESSAGES and LC_ALL. * * Note that the category must be specified with a numeric value, instead of the constant name. The values are: - * LC_CTYPE 0 - * LC_NUMERIC 1 - * LC_TIME 2 - * LC_COLLATE 3 - * LC_MONETARY 4 - * LC_MESSAGES 5 - * LC_ALL 6 + * LC_ALL 0 + * LC_COLLATE 1 + * LC_CTYPE 2 + * LC_MONETARY 3 + * LC_NUMERIC 4 + * LC_TIME 5 + * LC_MESSAGES 6 * * @param string $domain Domain * @param string $singular Singular string to translate diff --git a/cake/console/cake.php b/cake/console/cake.php index 1c234e23a..f6f8ccf94 100644 --- a/cake/console/cake.php +++ b/cake/console/cake.php @@ -259,8 +259,8 @@ class ShellDispatcher { Configure::getInstance(file_exists(CONFIGS . 'bootstrap.php')); if (!file_exists(APP_PATH . 'config' . DS . 'core.php')) { - include_once CORE_PATH . 'cake' . DS . 'console' . DS . 'templates' . DS . 'skel' . DS . 'config' . DS . 'core.php'; - Configure::buildPaths(array()); + include_once CORE_PATH . 'cake' . DS . 'console' . DS . 'libs' . DS . 'templates' . DS . 'skel' . DS . 'config' . DS . 'core.php'; + App::build(); } Configure::write('debug', 1); diff --git a/cake/console/libs/api.php b/cake/console/libs/api.php index 7ff0d73e0..1ab770868 100644 --- a/cake/console/libs/api.php +++ b/cake/console/libs/api.php @@ -66,7 +66,7 @@ class ApiShell extends Shell { return $this->help(); } - $type = low($this->args[0]); + $type = strtolower($this->args[0]); if (isset($this->paths[$type])) { $path = $this->paths[$type]; @@ -202,7 +202,7 @@ class ApiShell extends Shell { if (strpos($method, '__') === false && $method[0] != '_') { $parsed[$method] = array( - 'comment' => r(array('/*', '*/', '*'), '', trim($result[1][$key])), + 'comment' => str_replace(array('/*', '*/', '*'), '', trim($result[1][$key])), 'method' => $method, 'parameters' => trim($result[3][$key]) ); diff --git a/cake/console/libs/bake.php b/cake/console/libs/bake.php index f891ca2a4..fc81eec16 100644 --- a/cake/console/libs/bake.php +++ b/cake/console/libs/bake.php @@ -1,5 +1,4 @@ command); if (isset($this->{$task}) && !in_array($task, array('Project', 'DbConfig'))) { - $path = Inflector::underscore(Inflector::pluralize($this->command)); - $this->{$task}->path = $this->params['working'] . DS . $path . DS; + if (empty($this->{$task}->path)) { + $path = Inflector::underscore(Inflector::pluralize($this->command)); + $this->{$task}->path = $this->params['working'] . DS . $path . DS; + } + if (isset($this->params['connection'])) { + $this->{$task}->connection = $this->params['connection']; + } + if (isset($this->params['plugin'])) { + $this->{$task}->plugin = $this->params['plugin']; + } if (!is_dir($this->{$task}->path)) { $this->err(sprintf(__("%s directory could not be found.\nBe sure you have created %s", true), $task, $this->{$task}->path)); $this->_stop(); @@ -82,6 +86,7 @@ class BakeShell extends Shell { $this->out('[V]iew'); $this->out('[C]ontroller'); $this->out('[P]roject'); + $this->out('[F]ixture'); $this->out('[Q]uit'); $classToBake = strtoupper($this->in(__('What would you like to Bake?', true), array('D', 'M', 'V', 'C', 'P', 'Q'))); @@ -101,6 +106,9 @@ class BakeShell extends Shell { case 'P': $this->Project->execute(); break; + case 'F': + $this->Fixture->execute(); + break; case 'Q': exit(0); break; @@ -116,22 +124,26 @@ class BakeShell extends Shell { * @access public */ function all() { - $ds = 'default'; $this->hr(); $this->out('Bake All'); $this->hr(); - if (isset($this->params['connection'])) { - $ds = $this->params['connection']; + if (!isset($this->params['connection']) && empty($this->connection)) { + $this->connection = $this->DbConfig->getConfig(); } if (empty($this->args)) { - $name = $this->Model->getName($ds); + $this->Model->interactive = true; + $name = $this->Model->getName($this->connection); + } + + foreach (array('Model', 'Controller', 'View') as $task) { + $this->{$task}->connection = $this->connection; + $this->{$task}->interactive = false; } if (!empty($this->args[0])) { $name = $this->args[0]; - $this->Model->listAll($ds, false); } $modelExists = false; @@ -141,7 +153,7 @@ class BakeShell extends Shell { $modelExists = true; } else { App::import('Model'); - $object = new Model(array('name' => $name, 'ds' => $ds)); + $object = new Model(array('name' => $name, 'ds' => $this->connection)); } $modelBaked = $this->Model->bake($object, false); @@ -149,6 +161,7 @@ class BakeShell extends Shell { if ($modelBaked && $modelExists === false) { $this->out(sprintf(__('%s Model was baked.', true), $model)); if ($this->_checkUnitTest()) { + $this->Model->bakeFixture($model); $this->Model->bakeTest($model); } $modelExists = true; @@ -165,16 +178,13 @@ class BakeShell extends Shell { if (App::import('Controller', $controller)) { $this->View->args = array($controller); $this->View->execute(); + $this->out(sprintf(__('%s Views were baked.', true), $name)); } - $this->out(__('Bake All complete')); + $this->out(__('Bake All complete', true)); array_shift($this->args); } else { $this->err(__('Bake All could not continue without a valid model', true)); } - - if (empty($this->args)) { - $this->all(); - } $this->_stop(); } diff --git a/cake/console/libs/console.php b/cake/console/libs/console.php index b9f2e6e96..89b2a26a2 100644 --- a/cake/console/libs/console.php +++ b/cake/console/libs/console.php @@ -269,8 +269,7 @@ class ConsoleShell extends Shell { if ($this->_isValidModel($modelToSave)) { // Extract the array of data we are trying to build list($foo, $data) = explode("->save", $command); - $badChars = array("(", ")"); - $data = str_replace($badChars, "", $data); + $data = preg_replace('/^\(*(array)?\(*(.+?)\)*$/i', '\\2', $data); $saveCommand = "\$this->{$modelToSave}->save(array('{$modelToSave}' => array({$data})));"; @eval($saveCommand); $this->out('Saved record for ' . $modelToSave); @@ -358,4 +357,4 @@ class ConsoleShell extends Shell { return true; } } -?> \ No newline at end of file +?> diff --git a/cake/console/libs/schema.php b/cake/console/libs/schema.php index 445ad1579..e9fdfb193 100644 --- a/cake/console/libs/schema.php +++ b/cake/console/libs/schema.php @@ -199,7 +199,7 @@ class SchemaShell extends Shell { } } $db =& ConnectionManager::getDataSource($this->Schema->connection); - $contents = "#". $Schema->name ." sql generated on: " . date('Y-m-d H:m:s') . " : ". time()."\n\n"; + $contents = "#". $Schema->name ." sql generated on: " . date('Y-m-d H:i:s') . " : ". time()."\n\n"; $contents .= $db->dropSchema($Schema) . "\n\n". $db->createSchema($Schema); if ($write) { if (strpos($write, '.sql') === false) { diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php index bac6c347b..847262823 100644 --- a/cake/console/libs/shell.php +++ b/cake/console/libs/shell.php @@ -149,10 +149,10 @@ class Shell extends Object { ClassRegistry::map($this->name, $this->alias); if (!PHP5 && isset($this->args[0])) { - if (strpos($this->name, low(Inflector::camelize($this->args[0]))) !== false) { + if (strpos($this->name, strtolower(Inflector::camelize($this->args[0]))) !== false) { $dispatch->shiftArgs(); } - if (low($this->command) == low(Inflector::variable($this->args[0])) && method_exists($this, $this->command)) { + if (strtolower($this->command) == strtolower(Inflector::variable($this->args[0])) && method_exists($this, $this->command)) { $dispatch->shiftArgs(); } } @@ -329,7 +329,7 @@ class Shell extends Object { } } if (is_array($options)) { - while ($in == '' || ($in && (!in_array(low($in), $options) && !in_array(up($in), $options)) && !in_array($in, $options))) { + while ($in == '' || ($in && (!in_array(strtolower($in), $options) && !in_array(strtoupper($in), $options)) && !in_array($in, $options))) { $in = $this->Dispatch->getInput($prompt, $options, $default); } } @@ -427,10 +427,10 @@ class Shell extends Object { $this->out("\n" . sprintf(__("Creating file %s", true), $path)); if (is_file($path) && $this->interactive === true) { $key = $this->in(__("File exists, overwrite?", true). " {$path}", array('y', 'n', 'q'), 'n'); - if (low($key) == 'q') { + if (strtolower($key) == 'q') { $this->out(__("Quitting.", true) ."\n"); exit; - } elseif (low($key) != 'y') { + } elseif (strtolower($key) != 'y') { $this->out(__("Skip", true) ." {$path}\n"); return false; } @@ -472,7 +472,7 @@ class Shell extends Object { return true; } $unitTest = $this->in('SimpleTest is not installed. Do you want to bake unit test files anyway?', array('y','n'), 'y'); - $result = low($unitTest) == 'y' || low($unitTest) == 'yes'; + $result = strtolower($unitTest) == 'y' || strtolower($unitTest) == 'yes'; if ($result) { $this->out("\nYou can download SimpleTest from http://simpletest.org", true); @@ -488,37 +488,8 @@ class Shell extends Object { */ function shortPath($file) { $shortPath = str_replace(ROOT, null, $file); - $shortPath = str_replace('..'.DS, '', $shortPath); - return r(DS.DS, DS, $shortPath); - } -/** - * Checks for Configure::read('Routing.admin') and forces user to input it if not enabled - * - * @return string Admin route to use - * @access public - */ - function getAdmin() { - $admin = ''; - $cakeAdmin = null; - $adminRoute = Configure::read('Routing.admin'); - if (!empty($adminRoute)) { - $cakeAdmin = $adminRoute . '_'; - } else { - $this->out('You need to enable Configure::write(\'Routing.admin\',\'admin\') in /app/config/core.php to use admin routing.'); - $this->out('What would you like the admin route to be?'); - $this->out('Example: www.example.com/admin/controller'); - while ($admin == '') { - $admin = $this->in("What would you like the admin route to be?", null, 'admin'); - } - if ($this->Project->cakeAdmin($admin) !== true) { - $this->out('Unable to write to /app/config/core.php.'); - $this->out('You need to enable Configure::write(\'Routing.admin\',\'admin\') in /app/config/core.php to use admin routing.'); - $this->_stop(); - } else { - $cakeAdmin = $admin . '_'; - } - } - return $cakeAdmin; + $shortPath = str_replace('..' . DS, '', $shortPath); + return str_replace(DS . DS, DS, $shortPath); } /** * Creates the proper controller path for the specified controller class name @@ -528,7 +499,7 @@ class Shell extends Object { * @access protected */ function _controllerPath($name) { - return low(Inflector::underscore($name)); + return strtolower(Inflector::underscore($name)); } /** * Creates the proper controller plural name for the specified controller class name @@ -611,5 +582,21 @@ class Shell extends Object { function _pluralHumanName($name) { return Inflector::humanize(Inflector::underscore(Inflector::pluralize($name))); } +/** + * Find the correct path for a plugin. Scans $pluginPaths for the plugin you want. + * + * @param string $pluginName Name of the plugin you want ie. DebugKit + * @return string $path path to the correct plugin. + **/ + function _pluginPath($pluginName) { + $pluginPaths = App::path('plugins'); + $pluginDirName = Inflector::underscore($pluginName); + foreach ($pluginPaths as $path) { + if (is_dir($path . $pluginDirName)) { + return $path . $pluginDirName . DS ; + } + } + return $pluginPaths[0] . $pluginDirName . DS; + } } ?> \ No newline at end of file diff --git a/cake/console/libs/tasks/controller.php b/cake/console/libs/tasks/controller.php index 81f4fafbe..46828a750 100644 --- a/cake/console/libs/tasks/controller.php +++ b/cake/console/libs/tasks/controller.php @@ -1,5 +1,4 @@ args[0])) { + if (!isset($this->connection)) { + $this->connection = 'default'; + } + if (strtolower($this->args[0]) == 'all') { + return $this->all(); + } $controller = Inflector::camelize($this->args[0]); $actions = null; if (isset($this->args[1]) && $this->args[1] == 'scaffold') { - $this->out('Baking scaffold for ' . $controller); + $this->out(__('Baking scaffold for ', true) . $controller); $actions = $this->bakeActions($controller); } else { $actions = 'scaffold'; } if ((isset($this->args[1]) && $this->args[1] == 'admin') || (isset($this->args[2]) && $this->args[2] == 'admin')) { - if ($admin = $this->getAdmin()) { + if ($admin = $this->Project->getAdmin()) { $this->out('Adding ' . Configure::read('Routing.admin') .' methods'); if ($actions == 'scaffold') { $actions = $this->bakeActions($controller, $admin); @@ -95,144 +97,164 @@ class ControllerTask extends Shell { } } } +/** + * Bake All the controllers at once. Will only bake controllers for models that exist. + * + * @access public + * @return void + **/ + function all() { + $this->interactive = false; + $this->listAll($this->connection, false); + ClassRegistry::config('Model', array('ds' => $this->connection)); + $unitTestExists = $this->_checkUnitTest(); + foreach ($this->__tables as $table) { + $model = $this->_modelName($table); + $controller = $this->_controllerName($model); + if (App::import('Model', $model)) { + $actions = $this->bakeActions($controller); + if ($this->bake($controller, $actions) && $unitTestExists) { + $this->bakeTest($controller); + } + } + } + } /** * Interactive * * @access private */ - function __interactive($controllerName = false) { - if (!$controllerName) { - $this->interactive = true; - $this->hr(); - $this->out(sprintf("Bake Controller\nPath: %s", $this->path)); - $this->hr(); - $actions = ''; - $uses = array(); - $helpers = array(); - $components = array(); - $wannaUseSession = 'y'; - $wannaDoAdmin = 'n'; - $wannaUseScaffold = 'n'; - $wannaDoScaffolding = 'y'; - $controllerName = $this->getName(); - } + function __interactive() { + $this->interactive = true; $this->hr(); - $this->out("Baking {$controllerName}Controller"); + $this->out(sprintf(__("Bake Controller\nPath: %s", true), $this->path)); $this->hr(); - $controllerFile = low(Inflector::underscore($controllerName)); + if (empty($this->connection)) { + $this->connection = $this->DbConfig->getConfig(); + } + + $controllerName = $this->getName(); + $this->hr(); + $this->out(sprintf(__('Baking %sController', true), $controllerName)); + $this->hr(); + + $helpers = $components = array(); + $actions = ''; + $wannaUseSession = 'y'; + $wannaBakeAdminCrud = 'n'; + $useDynamicScaffold = 'n'; + $wannaBakeCrud = 'y'; + + $controllerFile = strtolower(Inflector::underscore($controllerName)); $question[] = __("Would you like to build your controller interactively?", true); if (file_exists($this->path . $controllerFile .'_controller.php')) { $question[] = sprintf(__("Warning: Choosing no will overwrite the %sController.", true), $controllerName); } - $doItInteractive = $this->in(join("\n", $question), array('y','n'), 'y'); + $doItInteractive = $this->in(join("\n", $question), array('y', 'n'), 'y'); - if (low($doItInteractive) == 'y' || low($doItInteractive) == 'yes') { + if (strtolower($doItInteractive) == 'y') { $this->interactive = true; + $useDynamicScaffold = $this->in( + __("Would you like to use dynamic scaffolding?", true), array('y','n'), 'n' + ); - $wannaUseScaffold = $this->in(__("Would you like to use scaffolding?", true), array('y','n'), 'n'); - - if (low($wannaUseScaffold) == 'n' || low($wannaUseScaffold) == 'no') { - - $wannaDoScaffolding = $this->in(__("Would you like to include some basic class methods (index(), add(), view(), edit())?", true), array('y','n'), 'n'); - - if (low($wannaDoScaffolding) == 'y' || low($wannaDoScaffolding) == 'yes') { - $wannaDoAdmin = $this->in(__("Would you like to create the methods for admin routing?", true), array('y','n'), 'n'); - } - - $wannaDoHelpers = $this->in(__("Would you like this controller to use other helpers besides HtmlHelper and FormHelper?", true), array('y','n'), 'n'); - - if (low($wannaDoHelpers) == 'y' || low($wannaDoHelpers) == 'yes') { - $helpersList = $this->in(__("Please provide a comma separated list of the other helper names you'd like to use.\nExample: 'Ajax, Javascript, Time'", true)); - $helpersListTrimmed = str_replace(' ', '', $helpersList); - $helpers = explode(',', $helpersListTrimmed); - } - $wannaDoComponents = $this->in(__("Would you like this controller to use any components?", true), array('y','n'), 'n'); - - if (low($wannaDoComponents) == 'y' || low($wannaDoComponents) == 'yes') { - $componentsList = $this->in(__("Please provide a comma separated list of the component names you'd like to use.\nExample: 'Acl, Security, RequestHandler'", true)); - $componentsListTrimmed = str_replace(' ', '', $componentsList); - $components = explode(',', $componentsListTrimmed); - } - - $wannaUseSession = $this->in(__("Would you like to use Sessions?", true), array('y','n'), 'y'); + if (strtolower($useDynamicScaffold) == 'y') { + $wannaBakeCrud = 'n'; + $actions = 'scaffold'; } else { - $wannaDoScaffolding = 'n'; + list($wannaBakeCrud, $wannaBakeAdminCrud) = $this->_askAboutMethods(); + + $helpers = $this->doHelpers(); + $components = $this->doComponents(); + + $wannaUseSession = $this->in( + __("Would you like to use Session flash messages?", true), array('y','n'), 'y' + ); } } else { - $wannaDoScaffolding = $this->in(__("Would you like to include some basic class methods (index(), add(), view(), edit())?", true), array('y','n'), 'y'); - - if (low($wannaDoScaffolding) == 'y' || low($wannaDoScaffolding) == 'yes') { - $wannaDoAdmin = $this->in(__("Would you like to create the methods for admin routing?", true), array('y','n'), 'y'); - } - } - $admin = false; - - if ((low($wannaDoAdmin) == 'y' || low($wannaDoAdmin) == 'yes')) { - $admin = $this->getAdmin(); + list($wannaBakeCrud, $wannaBakeCrud) = $this->_askAboutMethods(); } - if (low($wannaDoScaffolding) == 'y' || low($wannaDoScaffolding) == 'yes') { - $actions = $this->bakeActions($controllerName, null, in_array(low($wannaUseSession), array('y', 'yes'))); - if ($admin) { - $actions .= $this->bakeActions($controllerName, $admin, in_array(low($wannaUseSession), array('y', 'yes'))); - } + if (strtolower($wannaBakeCrud) == 'y') { + $actions = $this->bakeActions($controllerName, null, strtolower($wannaUseSession) == 'y'); + } + if (strtolower($wannaBakeAdminCrud) == 'y') { + $admin = $this->Project->getAdmin(); + $actions .= $this->bakeActions($controllerName, $admin, strtolower($wannaUseSession) == 'y'); } if ($this->interactive === true) { - $this->out(''); - $this->hr(); - $this->out('The following controller will be created:'); - $this->hr(); - $this->out("Controller Name: $controllerName"); - - if (low($wannaUseScaffold) == 'y' || low($wannaUseScaffold) == 'yes') { - $this->out(" var \$scaffold;"); - $actions = 'scaffold'; - } - - if (count($helpers)) { - $this->out("Helpers: ", false); - - foreach ($helpers as $help) { - if ($help != $helpers[count($helpers) - 1]) { - $this->out(ucfirst($help) . ", ", false); - } else { - $this->out(ucfirst($help)); - } - } - } - - if (count($components)) { - $this->out("Components: ", false); - - foreach ($components as $comp) { - if ($comp != $components[count($components) - 1]) { - $this->out(ucfirst($comp) . ", ", false); - } else { - $this->out(ucfirst($comp)); - } - } - } - $this->hr(); + $this->confirmController($controllerName, $useDynamicScaffold, $helpers, $components); $looksGood = $this->in(__('Look okay?', true), array('y','n'), 'y'); - if (low($looksGood) == 'y' || low($looksGood) == 'yes') { - $baked = $this->bake($controllerName, $actions, $helpers, $components, $uses); + if (strtolower($looksGood) == 'y') { + $baked = $this->bake($controllerName, $actions, $helpers, $components); if ($baked && $this->_checkUnitTest()) { $this->bakeTest($controllerName); } - } else { - $this->__interactive($controllerName); } } else { - $baked = $this->bake($controllerName, $actions, $helpers, $components, $uses); + $baked = $this->bake($controllerName, $actions, $helpers, $components); if ($baked && $this->_checkUnitTest()) { $this->bakeTest($controllerName); } } } +/** + * Confirm a to be baked controller with the user + * + * @return void + **/ + function confirmController($controllerName, $useDynamicScaffold, $helpers, $components) { + $this->out(''); + $this->hr(); + $this->out(__('The following controller will be created:', true)); + $this->hr(); + $this->out(sprintf(__("Controller Name:\n\t%s", true), $controllerName)); + + if (strtolower($useDynamicScaffold) == 'y') { + $this->out("var \$scaffold;"); + } + + $properties = array( + 'helpers' => __("Helpers:", true), + 'components' => __('Components:', true), + ); + + foreach ($properties as $var => $title) { + if (count($$var)) { + $output = ''; + $length = count($$var); + foreach ($$var as $i => $propElement) { + if ($i != $length -1) { + $output .= ucfirst($propElement) . ', '; + } else { + $output .= ucfirst($propElement); + } + } + $this->out($title . "\n\t" . $output); + } + } + $this->hr(); + } +/** + * Interact with the user and ask about which methods (admin or regular they want to bake) + * + * @return array Array containing (bakeRegular, bakeAdmin) answers + **/ + function _askAboutMethods() { + $wannaBakeCrud = $this->in( + __("Would you like to create some basic class methods \n(index(), add(), view(), edit())?", true), + array('y','n'), 'n' + ); + $wannaBakeAdminCrud = $this->in( + __("Would you like to create the basic class methods for admin routing?", true), + array('y','n'), 'n' + ); + return array($wannaBakeCrud, $wannaBakeAdminCrud); + } /** * Bake scaffold actions * @@ -247,151 +269,23 @@ class ControllerTask extends Shell { if ($this->plugin) { $modelImport = $this->plugin . '.' . $modelImport; } - if (!App::import('Model', $modelImport)) { - $this->err(__('You must have a model for this class to build scaffold methods. Please try again.', true)); - exit; + if (!App::import('Model', $currentModelName)) { + $this->err(__('You must have a model for this class to build basic methods. Please try again.', true)); + $this->_stop(); } - $actions = null; - $modelObj =& new $currentModelName(); + + $modelObj =& ClassRegistry::init($currentModelName); $controllerPath = $this->_controllerPath($controllerName); $pluralName = $this->_pluralName($currentModelName); $singularName = Inflector::variable($currentModelName); $singularHumanName = Inflector::humanize($currentModelName); $pluralHumanName = Inflector::humanize($controllerName); - $actions .= "\n"; - $actions .= "\tfunction {$admin}index() {\n"; - $actions .= "\t\t\$this->{$currentModelName}->recursive = 0;\n"; - $actions .= "\t\t\$this->set('{$pluralName}', \$this->paginate());\n"; - $actions .= "\t}\n"; - $actions .= "\n"; - $actions .= "\tfunction {$admin}view(\$id = null) {\n"; - $actions .= "\t\tif (!\$id) {\n"; - if ($wannaUseSession) { - $actions .= "\t\t\t\$this->Session->setFlash(__('Invalid {$singularHumanName}.', true));\n"; - $actions .= "\t\t\t\$this->redirect(array('action'=>'index'));\n"; - } else { - $actions .= "\t\t\t\$this->flash(__('Invalid {$singularHumanName}', true), array('action'=>'index'));\n"; - } - $actions .= "\t\t}\n"; - $actions .= "\t\t\$this->set('".$singularName."', \$this->{$currentModelName}->read(null, \$id));\n"; - $actions .= "\t}\n"; - $actions .= "\n"; - /* ADD ACTION */ - $compact = array(); - $actions .= "\tfunction {$admin}add() {\n"; - $actions .= "\t\tif (!empty(\$this->data)) {\n"; - $actions .= "\t\t\t\$this->{$currentModelName}->create();\n"; - $actions .= "\t\t\tif (\$this->{$currentModelName}->save(\$this->data)) {\n"; - if ($wannaUseSession) { - $actions .= "\t\t\t\t\$this->Session->setFlash(__('The ".$singularHumanName." has been saved', true));\n"; - $actions .= "\t\t\t\t\$this->redirect(array('action'=>'index'));\n"; - } else { - $actions .= "\t\t\t\t\$this->flash(__('{$currentModelName} saved.', true), array('action'=>'index'));\n"; - } - $actions .= "\t\t\t} else {\n"; - if ($wannaUseSession) { - $actions .= "\t\t\t\t\$this->Session->setFlash(__('The {$singularHumanName} could not be saved. Please, try again.', true));\n"; - } - $actions .= "\t\t\t}\n"; - $actions .= "\t\t}\n"; - foreach ($modelObj->hasAndBelongsToMany as $associationName => $relation) { - if (!empty($associationName)) { - $habtmModelName = $this->_modelName($associationName); - $habtmSingularName = $this->_singularName($associationName); - $habtmPluralName = $this->_pluralName($associationName); - $actions .= "\t\t\${$habtmPluralName} = \$this->{$currentModelName}->{$habtmModelName}->find('list');\n"; - $compact[] = "'{$habtmPluralName}'"; - } - } - foreach ($modelObj->belongsTo as $associationName => $relation) { - if (!empty($associationName)) { - $belongsToModelName = $this->_modelName($associationName); - $belongsToPluralName = $this->_pluralName($associationName); - $actions .= "\t\t\${$belongsToPluralName} = \$this->{$currentModelName}->{$belongsToModelName}->find('list');\n"; - $compact[] = "'{$belongsToPluralName}'"; - } - } - if (!empty($compact)) { - $actions .= "\t\t\$this->set(compact(".join(', ', $compact)."));\n"; - } - $actions .= "\t}\n"; - $actions .= "\n"; - - /* EDIT ACTION */ - $compact = array(); - $actions .= "\tfunction {$admin}edit(\$id = null) {\n"; - $actions .= "\t\tif (!\$id && empty(\$this->data)) {\n"; - if ($wannaUseSession) { - $actions .= "\t\t\t\$this->Session->setFlash(__('Invalid {$singularHumanName}', true));\n"; - $actions .= "\t\t\t\$this->redirect(array('action'=>'index'));\n"; - } else { - $actions .= "\t\t\t\$this->flash(__('Invalid {$singularHumanName}', true), array('action'=>'index'));\n"; - } - $actions .= "\t\t}\n"; - $actions .= "\t\tif (!empty(\$this->data)) {\n"; - $actions .= "\t\t\tif (\$this->{$currentModelName}->save(\$this->data)) {\n"; - if ($wannaUseSession) { - $actions .= "\t\t\t\t\$this->Session->setFlash(__('The ".$singularHumanName." has been saved', true));\n"; - $actions .= "\t\t\t\t\$this->redirect(array('action'=>'index'));\n"; - } else { - $actions .= "\t\t\t\t\$this->flash(__('The ".$singularHumanName." has been saved.', true), array('action'=>'index'));\n"; - } - $actions .= "\t\t\t} else {\n"; - if ($wannaUseSession) { - $actions .= "\t\t\t\t\$this->Session->setFlash(__('The {$singularHumanName} could not be saved. Please, try again.', true));\n"; - } - $actions .= "\t\t\t}\n"; - $actions .= "\t\t}\n"; - $actions .= "\t\tif (empty(\$this->data)) {\n"; - $actions .= "\t\t\t\$this->data = \$this->{$currentModelName}->read(null, \$id);\n"; - $actions .= "\t\t}\n"; - - foreach ($modelObj->hasAndBelongsToMany as $associationName => $relation) { - if (!empty($associationName)) { - $habtmModelName = $this->_modelName($associationName); - $habtmSingularName = $this->_singularName($associationName); - $habtmPluralName = $this->_pluralName($associationName); - $actions .= "\t\t\${$habtmPluralName} = \$this->{$currentModelName}->{$habtmModelName}->find('list');\n"; - $compact[] = "'{$habtmPluralName}'"; - } - } - foreach ($modelObj->belongsTo as $associationName => $relation) { - if (!empty($associationName)) { - $belongsToModelName = $this->_modelName($associationName); - $belongsToPluralName = $this->_pluralName($associationName); - $actions .= "\t\t\${$belongsToPluralName} = \$this->{$currentModelName}->{$belongsToModelName}->find('list');\n"; - $compact[] = "'{$belongsToPluralName}'"; - } - } - if (!empty($compact)) { - $actions .= "\t\t\$this->set(compact(".join(',', $compact)."));\n"; - } - $actions .= "\t}\n"; - $actions .= "\n"; - $actions .= "\tfunction {$admin}delete(\$id = null) {\n"; - $actions .= "\t\tif (!\$id) {\n"; - if ($wannaUseSession) { - $actions .= "\t\t\t\$this->Session->setFlash(__('Invalid id for {$singularHumanName}', true));\n"; - $actions .= "\t\t\t\$this->redirect(array('action'=>'index'));\n"; - } else { - $actions .= "\t\t\t\$this->flash(__('Invalid {$singularHumanName}', true), array('action'=>'index'));\n"; - } - $actions .= "\t\t}\n"; - $actions .= "\t\tif (\$this->{$currentModelName}->delete(\$id)) {\n"; - if ($wannaUseSession) { - $actions .= "\t\t\t\$this->Session->setFlash(__('{$singularHumanName} deleted', true));\n"; - $actions .= "\t\t\t\$this->redirect(array('action'=>'index'));\n"; - } else { - $actions .= "\t\t\t\$this->flash(__('{$singularHumanName} deleted', true), array('action'=>'index'));\n"; - } - $actions .= "\t\t}\n"; - $actions .= "\t}\n"; - $actions .= "\n"; + $this->Template->set(compact('admin', 'controllerPath', 'pluralName', 'singularName', 'singularHumanName', + 'pluralHumanName', 'modelObj', 'wannaUseSession', 'currentModelName')); + $actions = $this->Template->generate('actions', 'controller_actions'); return $actions; } - - /** * Assembles and writes a Controller file * @@ -403,53 +297,22 @@ class ControllerTask extends Shell { * @return string Baked controller * @access private */ - function bake($controllerName, $actions = '', $helpers = null, $components = null, $uses = null) { - $out = "plugin}AppController {\n\n"; - $out .= "\tvar \$name = '$controllerName';\n"; + function bake($controllerName, $actions = '', $helpers = null, $components = null) { + $isScaffold = ($actions === 'scaffold') ? true : false; - if (low($actions) == 'scaffold') { - $out .= "\tvar \$scaffold;\n"; - } else { - if (count($uses)) { - $out .= "\tvar \$uses = array('" . $this->_modelName($controllerName) . "', "; + $this->Template->set('plugin', Inflector::camelize($this->plugin)); + $this->Template->set(compact('controllerName', 'actions', 'helpers', 'components', 'isScaffold')); + $contents = $this->Template->generate('classes', 'controller'); - foreach ($uses as $use) { - if ($use != $uses[count($uses) - 1]) { - $out .= "'" . $this->_modelName($use) . "', "; - } else { - $out .= "'" . $this->_modelName($use) . "'"; - } - } - $out .= ");\n"; - } - - $out .= "\tvar \$helpers = array('Html', 'Form'"; - if (count($helpers)) { - foreach ($helpers as $help) { - $out .= ", '" . Inflector::camelize($help) . "'"; - } - } - $out .= ");\n"; - - if (count($components)) { - $out .= "\tvar \$components = array("; - - foreach ($components as $comp) { - if ($comp != $components[count($components) - 1]) { - $out .= "'" . Inflector::camelize($comp) . "', "; - } else { - $out .= "'" . Inflector::camelize($comp) . "'"; - } - } - $out .= ");\n"; - } - $out .= $actions; + $path = $this->path; + if (isset($this->plugin)) { + $path = $this->_pluginPath($this->plugin) . 'controllers' . DS; } - $out .= "}\n"; - $out .= "?>"; - $filename = $this->path . $this->_controllerPath($controllerName) . '_controller.php'; - return $this->createFile($filename, $out); + $filename = $path . $this->_controllerPath($controllerName) . '_controller.php'; + if ($this->createFile($filename, $contents)) { + return $contents; + } + return false; } /** * Assembles and writes a unit test file @@ -459,84 +322,89 @@ class ControllerTask extends Shell { * @access private */ function bakeTest($className) { - $import = $className; - if ($this->plugin) { - $import = $this->plugin . '.' . $className; - } - $out = "App::import('Controller', '$import');\n\n"; - $out .= "class Test{$className} extends {$className}Controller {\n"; - $out .= "\tvar \$autoRender = false;\n}\n\n"; - $out .= "class {$className}ControllerTest extends CakeTestCase {\n"; - $out .= "\tvar \${$className} = null;\n\n"; - $out .= "\tfunction startTest() {\n\t\t\$this->{$className} = new Test{$className}();"; - $out .= "\n\t\t\$this->{$className}->constructClasses();\n\t}\n\n"; - $out .= "\tfunction test{$className}ControllerInstance() {\n"; - $out .= "\t\t\$this->assertTrue(is_a(\$this->{$className}, '{$className}Controller'));\n\t}\n\n"; - $out .= "\tfunction endTest() {\n\t\tunset(\$this->{$className});\n\t}\n}\n"; - - $path = CONTROLLER_TESTS; - if (isset($this->plugin)) { - $pluginPath = 'plugins' . DS . Inflector::underscore($this->plugin) . DS; - $path = APP . $pluginPath . 'tests' . DS . 'cases' . DS . 'controllers' . DS; - } - - $filename = Inflector::underscore($className).'_controller.test.php'; - $this->out("\nBaking unit test for $className..."); - - $header = '$Id'; - $content = ""; - return $this->createFile($path . $filename, $content); + $this->Test->plugin = $this->plugin; + $this->Test->connection = $this->connection; + return $this->Test->bake('Controller', $className); } /** - * Outputs and gets the list of possible models or controllers from database + * Interact with the user and get a list of additional helpers + * + * @return array Helpers that the user wants to use. + **/ + function doHelpers() { + return $this->_doPropertyChoices( + __("Would you like this controller to use other helpers\nbesides HtmlHelper and FormHelper?", true), + __("Please provide a comma separated list of the other\nhelper names you'd like to use.\nExample: 'Ajax, Javascript, Time'", true) + ); + } + +/** + * Interact with the user and get a list of additional components + * + * @return array Components the user wants to use. + **/ + function doComponents() { + return $this->_doPropertyChoices( + __("Would you like this controller to use any components?", true), + __("Please provide a comma separated list of the component names you'd like to use.\nExample: 'Acl, Security, RequestHandler'", true) + ); + } +/** + * Common code for property choice handling. + * + * @param string $prompt A yes/no question to precede the list + * @param sting $example A question for a comma separated list, with examples. + * @return array Array of values for property. + **/ + function _doPropertyChoices($prompt, $example) { + $proceed = $this->in($prompt, array('y','n'), 'n'); + $property = array(); + if (strtolower($proceed) == 'y') { + $propertyList = $this->in($example); + $propertyListTrimmed = str_replace(' ', '', $propertyList); + $property = explode(',', $propertyListTrimmed); + } + return array_filter($property); + } +/** + * Outputs and gets the list of possible controllers from database * * @param string $useDbConfig Database configuration name + * @param boolean $interactive Whether you are using listAll interactively and want options output. * @return array Set of controllers * @access public */ - function listAll($useDbConfig = 'default') { - $db =& ConnectionManager::getDataSource($useDbConfig); - $usePrefix = empty($db->config['prefix']) ? '' : $db->config['prefix']; - if ($usePrefix) { - $tables = array(); - foreach ($db->listSources() as $table) { - if (!strncmp($table, $usePrefix, strlen($usePrefix))) { - $tables[] = substr($table, strlen($usePrefix)); - } + function listAll($useDbConfig = null) { + if (is_null($useDbConfig)) { + $useDbConfig = $this->connection; + } + $this->__tables = $this->Model->getAllTables($useDbConfig); + + if ($this->interactive == true) { + $this->out(__('Possible Controllers based on your current database:', true)); + $this->_controllerNames = array(); + $count = count($this->__tables); + for ($i = 0; $i < $count; $i++) { + $this->_controllerNames[] = $this->_controllerName($this->_modelName($this->__tables[$i])); + $this->out($i + 1 . ". " . $this->_controllerNames[$i]); } - } else { - $tables = $db->listSources(); + return $this->_controllerNames; } - - if (empty($tables)) { - $this->err(__('Your database does not have any tables.', true)); - $this->_stop(); - } - - $this->__tables = $tables; - $this->out('Possible Controllers based on your current database:'); - $this->_controllerNames = array(); - $count = count($tables); - for ($i = 0; $i < $count; $i++) { - $this->_controllerNames[] = $this->_controllerName($this->_modelName($tables[$i])); - $this->out($i + 1 . ". " . $this->_controllerNames[$i]); - } - return $this->_controllerNames; + return $this->__tables; } - /** * Forces the user to specify the controller he wants to bake, and returns the selected controller name. * + * @param string $useDbConfig Connection name to get a controller name for. * @return string Controller name * @access public */ - function getName() { - $useDbConfig = 'default'; - $controllers = $this->listAll($useDbConfig, 'Controllers'); + function getName($useDbConfig = null) { + $controllers = $this->listAll($useDbConfig); $enteredController = ''; while ($enteredController == '') { - $enteredController = $this->in(__("Enter a number from the list above, type in the name of another controller, or 'q' to exit", true), null, 'q'); + $enteredController = $this->in(__("Enter a number from the list above,\ntype in the name of another controller, or 'q' to exit", true), null, 'q'); if ($enteredController === 'q') { $this->out(__("Exit", true)); @@ -544,8 +412,7 @@ class ControllerTask extends Shell { } if ($enteredController == '' || intval($enteredController) > count($controllers)) { - $this->out(__('Error:', true)); - $this->out(__("The Controller name you supplied was empty, or the number \nyou selected was not an option. Please try again.", true)); + $this->err(__("The Controller name you supplied was empty,\nor the number you selected was not an option. Please try again.", true)); $enteredController = ''; } } @@ -555,7 +422,6 @@ class ControllerTask extends Shell { } else { $controllerName = Inflector::camelize($enteredController); } - return $controllerName; } /** @@ -568,12 +434,26 @@ class ControllerTask extends Shell { $this->out("Usage: cake bake controller ..."); $this->hr(); $this->out('Commands:'); - $this->out("\n\tcontroller \n\t\tbakes controller with var \$scaffold"); - $this->out("\n\tcontroller scaffold\n\t\tbakes controller with scaffold actions.\n\t\t(index, view, add, edit, delete)"); - $this->out("\n\tcontroller scaffold admin\n\t\tbakes a controller with scaffold actions for both public and Configure::read('Routing.admin')"); - $this->out("\n\tcontroller admin\n\t\tbakes a controller with scaffold actions only for Configure::read('Routing.admin')"); + $this->out(''); + $this->out("controller "); + $this->out("\tbakes controller with var \$scaffold"); + $this->out(''); + $this->out("controller scaffold"); + $this->out("\tbakes controller with scaffold actions."); + $this->out("\t(index, view, add, edit, delete)"); + $this->out(''); + $this->out("controller scaffold admin"); + $this->out("\tbakes a controller with scaffold actions for both public"); + $this->out("\tand Configure::read('Routing.admin')"); + $this->out(''); + $this->out("controller admin"); + $this->out("\tbakes a controller with scaffold actions only for"); + $this->out("\tConfigure::read('Routing.admin')"); + $this->out(''); + $this->out("controller all"); + $this->out("\tbakes all controllers with CRUD methods."); $this->out(""); $this->_stop(); } } -?> +?> \ No newline at end of file diff --git a/cake/console/libs/tasks/db_config.php b/cake/console/libs/tasks/db_config.php index fcd71052e..42c0a42af 100644 --- a/cake/console/libs/tasks/db_config.php +++ b/cake/console/libs/tasks/db_config.php @@ -1,5 +1,4 @@ 'root', 'password'=> 'password', 'database'=> 'project_name', 'schema'=> null, 'prefix'=> null, 'encoding' => null, 'port' => null ); +/** + * String name of the database config class name. + * Used for testing. + * + * @var string + **/ + var $databaseClassName = 'DATABASE_CONFIG'; /** * initialization callback * @@ -92,35 +92,27 @@ class DbConfigTask extends Shell { if (preg_match('/[^a-z0-9_]/i', $name)) { $name = ''; $this->out('The name may only contain unaccented latin characters, numbers or underscores'); - } - else if (preg_match('/^[^a-z_]/i', $name)) { + } else if (preg_match('/^[^a-z_]/i', $name)) { $name = ''; $this->out('The name must start with an unaccented latin character or an underscore'); } } - $driver = ''; - while ($driver == '') { - $driver = $this->in('Driver:', array('db2', 'firebird', 'mssql', 'mysql', 'mysqli', 'odbc', 'oracle', 'postgres', 'sqlite', 'sybase'), 'mysql'); - } - $persistent = ''; - - while ($persistent == '') { - $persistent = $this->in('Persistent Connection?', array('y', 'n'), 'n'); - } + $driver = $this->in('Driver:', array('db2', 'firebird', 'mssql', 'mysql', 'mysqli', 'odbc', 'oracle', 'postgres', 'sqlite', 'sybase'), 'mysql'); + $persistent = $this->in('Persistent Connection?', array('y', 'n'), 'n'); if (low($persistent) == 'n') { $persistent = 'false'; } else { $persistent = 'true'; } - $host = ''; + $host = ''; while ($host == '') { $host = $this->in('Database Host:', null, 'localhost'); } - $port = ''; + $port = ''; while ($port == '') { $port = $this->in('Port?', null, 'n'); } @@ -128,8 +120,8 @@ class DbConfigTask extends Shell { if (low($port) == 'n') { $port = null; } - $login = ''; + $login = ''; while ($login == '') { $login = $this->in('User:', null, 'root'); } @@ -141,43 +133,39 @@ class DbConfigTask extends Shell { if ($password == '') { $blank = $this->in('The password you supplied was empty. Use an empty password?', array('y', 'n'), 'n'); - if ($blank == 'y') - { + if ($blank == 'y') { $blankPassword = true; } } } - $database = ''; + $database = ''; while ($database == '') { $database = $this->in('Database Name:', null, 'cake'); } - $prefix = ''; + $prefix = ''; while ($prefix == '') { $prefix = $this->in('Table Prefix?', null, 'n'); } - if (low($prefix) == 'n') { $prefix = null; } - $encoding = ''; + $encoding = ''; while ($encoding == '') { $encoding = $this->in('Table encoding?', null, 'n'); } - if (low($encoding) == 'n') { $encoding = null; } - $schema = ''; + $schema = ''; if ($driver == 'postgres') { while ($schema == '') { $schema = $this->in('Table schema?', null, 'n'); } } - if (low($schema) == 'n') { $schema = null; } @@ -240,7 +228,7 @@ class DbConfigTask extends Shell { $this->hr(); $looksGood = $this->in('Look okay?', array('y', 'n'), 'y'); - if (low($looksGood) == 'y' || low($looksGood) == 'yes') { + if (strtolower($looksGood) == 'y') { return $config; } return false; @@ -262,7 +250,7 @@ class DbConfigTask extends Shell { $oldConfigs = array(); if (file_exists($filename)) { - $db = new DATABASE_CONFIG; + $db = new $this->databaseClassName; $temp = get_class_vars(get_class($db)); foreach ($temp as $configName => $info) { @@ -346,8 +334,27 @@ class DbConfigTask extends Shell { $out .= "}\n"; $out .= "?>"; - $filename = $this->path.'database.php'; + $filename = $this->path . 'database.php'; return $this->createFile($filename, $out); } +/** + * Get a user specified Connection name + * + * @return void + **/ + function getConfig() { + $useDbConfig = 'default'; + $configs = get_class_vars($this->databaseClassName); + + if (!is_array($configs)) { + return $this->execute(); + } + + $connections = array_keys($configs); + if (count($connections) > 1) { + $useDbConfig = $this->in(__('Use Database Config', true) .':', $connections, 'default'); + } + return $useDbConfig; + } } ?> \ No newline at end of file diff --git a/cake/console/libs/tasks/extract.php b/cake/console/libs/tasks/extract.php index 7918e8cd7..552eb8497 100644 --- a/cake/console/libs/tasks/extract.php +++ b/cake/console/libs/tasks/extract.php @@ -1,5 +1,4 @@ path = $this->params['working'] . DS . 'tests' . DS . 'fixtures' . DS; + if (!class_exists('CakeSchema')) { + App::import('Model', 'CakeSchema'); + } + } +/** + * Execution method always used for tasks + * Handles dispatching to interactive, named, or all processess. + * + * @access public + */ + function execute() { + if (empty($this->args)) { + $this->__interactive(); + } + + if (isset($this->args[0])) { + if (!isset($this->connection)) { + $this->connection = 'default'; + } + if (strtolower($this->args[0]) == 'all') { + return $this->all(); + } + $model = Inflector::camelize($this->args[0]); + $this->bake($model); + } + } +/** + * Bake All the Fixtures at once. Will only bake fixtures for models that exist. + * + * @access public + * @return void + **/ + function all() { + $this->interactive = false; + $tables = $this->Model->listAll($this->connection, false); + foreach ($tables as $table) { + $model = $this->_modelName($table); + $this->bake($model); + } + } +/** + * Interactive baking function + * + * @access private + */ + function __interactive() { + $this->interactive = true; + $this->hr(); + $this->out(sprintf("Bake Fixture\nPath: %s", $this->path)); + $this->hr(); + + $useDbConfig = $this->connection; + if (!isset($this->connection)) { + $this->connection = $this->DbConfig->getConfig(); + } + $modelName = $this->Model->getName($this->connection); + $useTable = $this->Model->getTable($modelName, $this->connection); + $importOptions = $this->importOptions($modelName); + $this->bake($modelName, $useTable, $importOptions); + } +/** + * Interacts with the User to setup an array of import options. For a fixture. + * + * @param string $modelName Name of model you are dealing with. + * @return array Array of import options. + **/ + function importOptions($modelName) { + $options = array(); + $doSchema = $this->in(__('Would you like to import schema for this fixture?', true), array('y', 'n'), 'n'); + if ($doSchema == 'y') { + $options['schema'] = $modelName; + } + $doRecords = $this->in(__('Would you like to use record importing for this fixture?', true), array('y', 'n'), 'n'); + if ($doRecords == 'y') { + $options['records'] = true; + } + if ($doRecords == 'n') { + $prompt = sprintf(__("Would you like to build this fixture with data from %s's table?", true), $modelName); + $fromTable = $this->in($prompt, array('y', 'n'), 'n'); + if (strtolower($fromTable) == 'y') { + $options['fromTable'] = true; + } + } + return $options; + } +/** + * Assembles and writes a Fixture file + * + * @param string $model Name of model to bake. + * @param string $useTable Name of table to use. + * @param array $importOptions Options for var $import + * @return string Baked fixture + * @access private + */ + function bake($model, $useTable = false, $importOptions = array()) { + $table = $schema = $records = $import = $modelImport = $recordImport = null; + if (!$useTable) { + $useTable = Inflector::tableize($model); + } elseif ($useTable != Inflector::tableize($model)) { + $table = $useTable; + } + + if (!empty($importOptions)) { + if (isset($importOptions['schema'])) { + $modelImport = "'model' => '{$importOptions['schema']}'"; + } + if (isset($importOptions['records'])) { + $recordImport = "'records' => true"; + } + if ($modelImport && $recordImport) { + $modelImport .= ', '; + } + if (!empty($modelImport) || !empty($recordImport)) { + $import = sprintf("array(%s%s)", $modelImport, $recordImport); + } + } + + $this->_Schema = new CakeSchema(); + $data = $this->_Schema->read(array('models' => false, 'connection' => $this->connection)); + + if (!isset($data['tables'][$useTable])) { + $this->err('Could not find your selected table ' . $useTable); + return false; + } + + $tableInfo = $data['tables'][$useTable]; + if (is_null($modelImport)) { + $schema = $this->_generateSchema($tableInfo); + } + + if (!isset($importOptions['records']) && !isset($importOptions['fromTable'])) { + $recordCount = 1; + if (isset($this->params['count'])) { + $recordCount = $this->params['count']; + } + $records = $this->_makeRecordString($this->_generateRecords($tableInfo, $recordCount)); + } + if (isset($importOptions['fromTable'])) { + $records = $this->_makeRecordString($this->_getRecordsFromTable($model, $useTable)); + } + $out = $this->generateFixtureFile($model, compact('records', 'table', 'schema', 'import', 'fields')); + return $out; + } +/** + * Generate the fixture file, and write to disk + * + * @param string $model name of the model being generated + * @param string $fixture Contents of the fixture file. + * @access public + * @return void + **/ + function generateFixtureFile($model, $otherVars) { + $defaults = array('table' => null, 'schema' => null, 'records' => null, 'import' => null, 'fields' => null); + $vars = array_merge($defaults, $otherVars); + + $path = $this->path; + if (isset($this->plugin)) { + $path = $this->_pluginPath($this->plugin) . 'tests' . DS . 'fixtures' . DS; + } + $filename = Inflector::underscore($model) . '_fixture.php'; + + $this->Template->set('model', $model); + $this->Template->set($vars); + $content = $this->Template->generate('classes', 'fixture'); + + $this->out("\nBaking test fixture for $model..."); + $this->createFile($path . $filename, $content); + return $content; + } +/** + * Generates a string representation of a schema. + * + * @param array $table Table schema array + * @return string fields definitions + **/ + function _generateSchema($tableInfo) { + $cols = array(); + $out = "array(\n"; + foreach ($tableInfo as $field => $fieldInfo) { + if (is_array($fieldInfo)) { + if ($field != 'indexes') { + $col = "\t\t'{$field}' => array('type'=>'" . $fieldInfo['type'] . "', "; + $col .= join(', ', $this->_Schema->__values($fieldInfo)); + } else { + $col = "\t\t'indexes' => array("; + $props = array(); + foreach ((array)$fieldInfo as $key => $index) { + $props[] = "'{$key}' => array(".join(', ', $this->_Schema->__values($index)).")"; + } + $col .= join(', ', $props); + } + $col .= ")"; + $cols[] = $col; + } + } + $out .= join(",\n", $cols); + $out .= "\n\t)"; + return $out; + } +/** + * Generate String representation of Records + * + * @param array $table Table schema array + * @return array Array of records to use in the fixture. + **/ + function _generateRecords($tableInfo, $recordCount = 1) { + $records = array(); + for ($i = 0; $i < $recordCount; $i++) { + $record = array(); + foreach ($tableInfo as $field => $fieldInfo) { + if (empty($fieldInfo['type'])) { + continue; + } + switch ($fieldInfo['type']) { + case 'integer': + $insert = $i + 1; + break; + case 'string'; + $isPrimaryUuid = ( + isset($fieldInfo['key']) && strtolower($fieldInfo['key']) == 'primary' && + isset($fieldInfo['length']) && $fieldInfo['length'] == 36 + ); + if ($isPrimaryUuid) { + $insert = String::uuid(); + } else { + $insert = "Lorem ipsum dolor sit amet"; + if (!empty($fieldInfo['length'])) { + $insert = substr($insert, 0, (int)$fieldInfo['length'] - 2); + } + } + $insert = "'$insert'"; + break; + case 'timestamp': + $ts = time(); + $insert = "'$ts'"; + break; + case 'datetime': + $ts = date('Y-m-d H:i:s'); + $insert = "'$ts'"; + break; + case 'date': + $ts = date('Y-m-d'); + $insert = "'$ts'"; + break; + case 'time': + $ts = date('H:i:s'); + $insert = "'$ts'"; + break; + case 'boolean': + $insert = 1; + break; + case 'text': + $insert = "'Lorem ipsum dolor sit amet, aliquet feugiat."; + $insert .= " Convallis morbi fringilla gravida,"; + $insert .= " phasellus feugiat dapibus velit nunc, pulvinar eget sollicitudin"; + $insert .= " venenatis cum nullam, vivamus ut a sed, mollitia lectus. Nulla"; + $insert .= " vestibulum massa neque ut et, id hendrerit sit,"; + $insert .= " feugiat in taciti enim proin nibh, tempor dignissim, rhoncus"; + $insert .= " duis vestibulum nunc mattis convallis.'"; + break; + } + $record[$field] = $insert; + } + $records[] = $record; + } + return $records; + } +/** + * Convert a $records array into a a string. + * + * @param array $records Array of records to be converted to string + * @return string A string value of the $records array. + **/ + function _makeRecordString($records) { + $out = "array(\n"; + foreach ($records as $record) { + $values = array(); + foreach ($record as $field => $value) { + $values[] = "\t\t\t'$field' => $value"; + } + $out .= "\t\tarray(\n"; + $out .= implode(",\n", $values); + $out .= "\n\t\t),\n"; + } + $out .= "\t)"; + return $out; + } +/** + * Interact with the user to get a custom SQL condition and use that to extract data + * to build a fixture. + * + * @param string $modelName name of the model to take records from. + * @param string $useTable Name of table to use. + * @return array Array of records. + **/ + function _getRecordsFromTable($modelName, $useTable = null) { + $condition = null; + $prompt = __("Please provide a SQL fragment to use as conditions\nExample: WHERE 1=1 LIMIT 10", true); + while (!$condition) { + $condition = $this->in($prompt, null, 'WHERE 1=1 LIMIT 10'); + } + App::import('Core', 'Model'); + $modelObject =& new Model(array('name' => $modelName, 'table' => $useTable, 'ds' => $this->connection)); + $records = $modelObject->find('all', array( + 'conditions' => $condition, + 'recursive' => -1 + )); + $db =& $modelObject->getDataSource(); + $schema = $modelObject->schema(); + $out = array(); + foreach ($records as $record) { + $row = array(); + foreach ($record[$modelObject->alias] as $field => $value) { + $row[$field] = $db->value($value, $schema[$field]['type']); + } + $out[] = $row; + } + return $out; + } +/** + * Displays help contents + * + * @access public + */ + function help() { + $this->hr(); + $this->out("Usage: cake bake fixture "); + $this->hr(); + $this->out('Commands:'); + $this->out("\nfixture \n\tbakes fixture with specified name."); + $this->out("\nfixture all\n\tbakes all fixtures."); + $this->out(""); + $this->out('Parameters:'); + $this->out("\t-count When using generated data, the number of records to include in the fixture(s)."); + $this->out("\t-connection Which database configuration to use for baking."); + $this->out("\t-plugin lowercased_underscored name of plugin to bake fixtures for."); + $this->out(""); + $this->_stop(); + } +} +?> \ No newline at end of file diff --git a/cake/console/libs/tasks/model.php b/cake/console/libs/tasks/model.php index b4ae6feed..d436748d9 100644 --- a/cake/console/libs/tasks/model.php +++ b/cake/console/libs/tasks/model.php @@ -1,5 +1,4 @@ args[0])) { + $this->interactive = false; + if (!isset($this->connection)) { + $this->connection = 'default'; + } + if (strtolower($this->args[0]) == 'all') { + return $this->all(); + } $model = Inflector::camelize($this->args[0]); - if ($this->bake($model)) { + $object = $this->_getModelObject($model); + if ($this->bake($object, false)) { if ($this->_checkUnitTest()) { + $this->bakeFixture($model); $this->bakeTest($model); } } } } +/** + * Bake all models at once. + * + * @return void + **/ + function all() { + $this->listAll($this->connection, false); + $unitTestExists = $this->_checkUnitTest(); + foreach ($this->__tables as $table) { + $modelClass = Inflector::classify($table); + $this->out(sprintf(__('Baking %s', true), $modelClass)); + $object = $this->_getModelObject($modelClass); + if ($this->bake($object, false) && $unitTestExists) { + $this->bakeFixture($modelClass); + $this->bakeTest($modelClass); + } + } + } +/** + * Get a model object for a class name. + * + * @param string $className Name of class you want model to be. + * @return object Model instance + **/ + function &_getModelObject($className) { + $object = new Model(array('name' => $className, 'ds' => $this->connection)); + return $object; + } +/** + * Generate a key value list of options and a prompt. + * + * @param array $options Array of options to use for the selections. indexes must start at 0 + * @param string $prompt Prompt to use for options list. + * @param integer $default The default option for the given prompt. + * @return result of user choice. + **/ + function inOptions($options, $prompt = null, $default = null) { + $valid = false; + $max = count($options); + while (!$valid) { + foreach ($options as $i => $option) { + $this->out($i + 1 .'. ' . $option); + } + if (empty($prompt)) { + $prompt = __('Make a selection from the choices above', true); + } + $choice = $this->in($prompt, null, $default); + if (intval($choice) > 0 && intval($choice) <= $max) { + $valid = true; + } + } + return $choice - 1; + } /** * Handles interactive baking * @@ -83,73 +169,42 @@ class ModelTask extends Shell { $this->hr(); $this->interactive = true; - $useTable = null; $primaryKey = 'id'; - $validate = array(); - $associations = array('belongsTo'=> array(), 'hasOne'=> array(), 'hasMany' => array(), 'hasAndBelongsToMany'=> array()); + $validate = $associations = array(); - $useDbConfig = 'default'; - $configs = get_class_vars('DATABASE_CONFIG'); - - if (!is_array($configs)) { - return $this->DbConfig->execute(); + if (empty($this->connection)) { + $this->connection = $this->DbConfig->getConfig(); } - - $connections = array_keys($configs); - if (count($connections) > 1) { - $useDbConfig = $this->in(__('Use Database Config', true) .':', $connections, 'default'); - } - - $currentModelName = $this->getName($useDbConfig); - $db =& ConnectionManager::getDataSource($useDbConfig); - $useTable = Inflector::tableize($currentModelName); - $fullTableName = $db->fullTableName($useTable, false); - $tableIsGood = false; - - if (array_search($useTable, $this->__tables) === false) { - $this->out(''); - $this->out(sprintf(__("Given your model named '%s', Cake would expect a database table named %s", true), $currentModelName, $fullTableName)); - $tableIsGood = $this->in(__('Do you want to use this table?', true), array('y','n'), 'y'); - } - - if (low($tableIsGood) == 'n' || low($tableIsGood) == 'no') { - $useTable = $this->in(__('What is the name of the table (enter "null" to use NO table)?', true)); - } - - while ($tableIsGood == false && low($useTable) != 'null') { - if (is_array($this->__tables) && !in_array($useTable, $this->__tables)) { - $fullTableName = $db->fullTableName($useTable, false); - $this->out($fullTableName . ' does not exist.'); - $useTable = $this->in(__('What is the name of the table (enter "null" to use NO table)?', true)); - $tableIsGood = false; - } else { - $tableIsGood = true; - } - } - - $wannaDoValidation = $this->in(__('Would you like to supply validation criteria for the fields in your model?', true), array('y','n'), 'y'); + $currentModelName = $this->getName(); + $useTable = $this->getTable($currentModelName); + $db =& ConnectionManager::getDataSource($this->connection); + $fullTableName = $db->fullTableName($useTable); if (in_array($useTable, $this->__tables)) { - App::import('Model'); - $tempModel = new Model(array('name' => $currentModelName, 'table' => $useTable, 'ds' => $useDbConfig)); - + $tempModel = new Model(array('name' => $currentModelName, 'table' => $useTable, 'ds' => $this->connection)); $fields = $tempModel->schema(); if (!array_key_exists('id', $fields)) { - foreach ($fields as $name => $field) { - if (isset($field['key']) && $field['key'] == 'primary') { - break; - } - } - $primaryKey = $this->in(__('What is the primaryKey?', true), null, $name); + $primaryKey = $this->findPrimaryKey($fields); } + } else { + $this->err(sprintf(__('Table %s does not exist, cannot bake a model without a table.', true), $useTable)); + $this->_stop(); + return false; + } + $displayField = $tempModel->hasField(array('name', 'title')); + if (!$displayField) { + $displayField = $this->findDisplayField($tempModel->schema()); } - if (array_search($useTable, $this->__tables) !== false && (low($wannaDoValidation) == 'y' || low($wannaDoValidation) == 'yes')) { + $prompt = __("Would you like to supply validation criteria \nfor the fields in your model?", true); + $wannaDoValidation = $this->in($prompt, array('y','n'), 'y'); + if (array_search($useTable, $this->__tables) !== false && strtolower($wannaDoValidation) == 'y') { $validate = $this->doValidation($tempModel); } - $wannaDoAssoc = $this->in(__('Would you like to define model associations (hasMany, hasOne, belongsTo, etc.)?', true), array('y','n'), 'y'); - if ((low($wannaDoAssoc) == 'y' || low($wannaDoAssoc) == 'yes')) { + $prompt = __("Would you like to define model associations\n(hasMany, hasOne, belongsTo, etc.)?", true); + $wannaDoAssoc = $this->in($prompt, array('y','n'), 'y'); + if (strtolower($wannaDoAssoc) == 'y') { $associations = $this->doAssociations($tempModel); } @@ -159,51 +214,35 @@ class ModelTask extends Shell { $this->hr(); $this->out("Name: " . $currentModelName); - if ($useDbConfig !== 'default') { - $this->out("DB Config: " . $useDbConfig); + if ($this->connection !== 'default') { + $this->out(sprintf(__("DB Config: %s", true), $this->connection)); } if ($fullTableName !== Inflector::tableize($currentModelName)) { - $this->out("DB Table: " . $fullTableName); + $this->out(sprintf(__("DB Table: %s", true), $fullTableName)); } if ($primaryKey != 'id') { - $this->out("Primary Key: " . $primaryKey); + $this->out(sprintf(__("Primary Key: %s", true), $primaryKey)); } if (!empty($validate)) { - $this->out("Validation: " . print_r($validate, true)); + $this->out(sprintf(__("Validation: %s", true), print_r($validate, true))); } if (!empty($associations)) { - $this->out("Associations:"); - - if (!empty($associations['belongsTo'])) { - for ($i = 0; $i < count($associations['belongsTo']); $i++) { - $this->out(" $currentModelName belongsTo {$associations['belongsTo'][$i]['alias']}"); - } - } - - if (!empty($associations['hasOne'])) { - for ($i = 0; $i < count($associations['hasOne']); $i++) { - $this->out(" $currentModelName hasOne {$associations['hasOne'][$i]['alias']}"); - } - } - - if (!empty($associations['hasMany'])) { - for ($i = 0; $i < count($associations['hasMany']); $i++) { - $this->out(" $currentModelName hasMany {$associations['hasMany'][$i]['alias']}"); - } - } - - if (!empty($associations['hasAndBelongsToMany'])) { - for ($i = 0; $i < count($associations['hasAndBelongsToMany']); $i++) { - $this->out(" $currentModelName hasAndBelongsToMany {$associations['hasAndBelongsToMany'][$i]['alias']}"); - } + $this->out(__("Associations:", true)); + $assocKeys = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany'); + foreach ($assocKeys as $assocKey) { + $this->_printAssociation($currentModelName, $assocKey, $associations); } } + $this->hr(); $looksGood = $this->in(__('Look okay?', true), array('y','n'), 'y'); - if (low($looksGood) == 'y' || low($looksGood) == 'yes') { - if ($this->bake($currentModelName, $associations, $validate, $primaryKey, $useTable, $useDbConfig)) { + if (strtolower($looksGood) == 'y') { + $vars = compact('associations', 'validate', 'primaryKey', 'useTable', 'displayField'); + $vars['useDbConfig'] = $this->connection; + if ($this->bake($currentModelName, $vars)) { if ($this->_checkUnitTest()) { + $this->bakeFixture($currentModelName, $useTable); $this->bakeTest($currentModelName, $useTable, $associations); } } @@ -212,14 +251,62 @@ class ModelTask extends Shell { } } /** - * Handles associations + * Print out all the associations of a particular type * - * @param object $model - * @param boolean $interactive - * @return array $validate + * @param string $modelName Name of the model relations belong to. + * @param string $type Name of association you want to see. i.e. 'belongsTo' + * @param string $associations Collection of associations. + * @access protected + * @return void + **/ + function _printAssociation($modelName, $type, $associations) { + if (!empty($associations[$type])) { + for ($i = 0; $i < count($associations[$type]); $i++) { + $out = "\t" . $modelName . ' ' . $type . ' ' . $associations[$type][$i]['alias']; + $this->out($out); + } + } + } +/** + * Finds a primary Key in a list of fields. + * + * @param array $fields Array of fields that might have a primary key. + * @return string Name of field that is a primary key. + * @access public + **/ + function findPrimaryKey($fields) { + foreach ($fields as $name => $field) { + if (isset($field['key']) && $field['key'] == 'primary') { + break; + } + } + return $this->in(__('What is the primaryKey?', true), null, $name); + } +/** + * interact with the user to find the displayField value for a model. + * + * @param array $fields Array of fields to look for and choose as a displayField + * @return mixed Name of field to use for displayField or false if the user declines to choose + **/ + function findDisplayField($fields) { + $fieldNames = array_keys($fields); + $prompt = __("A displayField could not be automatically detected\nwould you like to choose one?", true); + $continue = $this->in($prompt, array('y', 'n')); + if (strtolower($continue) == 'n') { + return false; + } + $prompt = __('Choose a field from the options above:', true); + $choice = $this->inOptions($fieldNames, $prompt); + return $fieldNames[$choice]; + } +/** + * Handles Generation and user interaction for creating validation. + * + * @param object $model Model to have validations generated for. + * @return array $validate Array of user selected validations. * @access public */ - function doValidation(&$model, $interactive = true) { + function doValidation(&$model) { if (!is_object($model)) { return false; } @@ -228,515 +315,425 @@ class ModelTask extends Shell { if (empty($fields)) { return false; } - $validate = array(); - - $options = array(); - - if (class_exists('Validation')) { - $parent = get_class_methods(get_parent_class('Validation')); - $options = array_diff(get_class_methods('Validation'), $parent); - } - + $this->initValidations(); foreach ($fields as $fieldName => $field) { - $prompt = 'Field: ' . $fieldName . "\n"; - $prompt .= 'Type: ' . $field['type'] . "\n"; - $prompt .= '---------------------------------------------------------------'."\n"; - $prompt .= 'Please select one of the following validation options:'."\n"; - $prompt .= '---------------------------------------------------------------'."\n"; - - sort($options); - - $skip = 1; - foreach ($options as $key => $option) { - if ($option{0} != '_' && strtolower($option) != 'getinstance') { - $prompt .= "{$skip} - {$option}\n"; - $choices[$skip] = strtolower($option); - $skip++; - } - } - - $methods = array_flip($choices); - - $prompt .= "{$skip} - Do not do any validation on this field.\n"; - $prompt .= "... or enter in a valid regex validation string.\n"; - - $guess = $skip; - if ($field['null'] != 1 && $fieldName != $model->primaryKey && !in_array($fieldName, array('created', 'modified', 'updated'))) { - if ($fieldName == 'email') { - $guess = $methods['email']; - } elseif ($field['type'] == 'string') { - $guess = $methods['notempty']; - } elseif ($field['type'] == 'integer') { - $guess = $methods['numeric']; - } elseif ($field['type'] == 'boolean') { - $guess = $methods['numeric']; - } elseif ($field['type'] == 'datetime') { - $guess = $methods['date']; - } - } - - if ($interactive === true) { - $this->out(''); - $choice = $this->in($prompt, null, $guess); - } else { - $choice = $guess; - } - if ($choice != $skip) { - if (is_numeric($choice) && isset($choices[$choice])) { - $validate[$fieldName] = $choices[$choice]; - } else { - $validate[$fieldName] = $choice; - } + $validation = $this->fieldValidation($fieldName, $field, $model->primaryKey); + if (!empty($validation)) { + $validate[$fieldName] = $validation; } } return $validate; } +/** + * Populate the __validations array + * + * @return void + **/ + function initValidations() { + $options = $choices = array(); + if (class_exists('Validation')) { + $parent = get_class_methods(get_parent_class('Validation')); + $options = array_diff(get_class_methods('Validation'), $parent); + } + sort($options); + $default = 1; + foreach ($options as $key => $option) { + if ($option{0} != '_' && strtolower($option) != 'getinstance') { + $choices[$default] = strtolower($option); + $default++; + } + } + $this->__validations = $choices; + return $choices; + } +/** + * Does individual field validation handling. + * + * @param string $fieldName Name of field to be validated. + * @param array $metaData metadata for field + * @return array Array of validation for the field. + **/ + function fieldValidation($fieldName, $metaData, $primaryKey = 'id') { + $defaultChoice = count($this->__validations); + $validate = $alreadyChosen = array(); + + $anotherValidator = 'y'; + while ($anotherValidator == 'y') { + if ($this->interactive) { + $this->out(''); + $this->out(sprintf(__('Field: %s', true), $fieldName)); + $this->out(sprintf(__('Type: %s', true), $metaData['type'])); + $this->hr(); + $this->out(__('Please select one of the following validation options:', true)); + $this->hr(); + } + + $prompt = ''; + for ($i = 1; $i < $defaultChoice; $i++) { + $prompt .= $i . ' - ' . $this->__validations[$i] . "\n"; + } + $prompt .= sprintf(__("%s - Do not do any validation on this field.\n", true), $defaultChoice); + $prompt .= __("... or enter in a valid regex validation string.\n", true); + $methods = array_flip($this->__validations); + $guess = $defaultChoice; + if ($metaData['null'] != 1 && !in_array($fieldName, array($primaryKey, 'created', 'modified', 'updated'))) { + if ($fieldName == 'email') { + $guess = $methods['email']; + } elseif ($metaData['type'] == 'string') { + $guess = $methods['notempty']; + } elseif ($metaData['type'] == 'integer') { + $guess = $methods['numeric']; + } elseif ($metaData['type'] == 'boolean') { + $guess = $methods['numeric']; + } elseif ($metaData['type'] == 'datetime' || $metaData['type'] == 'date') { + $guess = $methods['date']; + } elseif ($metaData['type'] == 'time') { + $guess = $methods['time']; + } + } + + if ($this->interactive === true) { + $choice = $this->in($prompt, null, $guess); + if (in_array($choice, $alreadyChosen)) { + $this->out(__("You have already chosen that validation rule,\nplease choose again", true)); + continue; + } + $alreadyChosen[] = $choice; + } else { + $choice = $guess; + } + $validatorName = $this->__validations[$choice]; + if ($choice != $defaultChoice) { + if (is_numeric($choice) && isset($this->__validations[$choice])) { + $validate[$validatorName] = $this->__validations[$choice]; + } else { + $validate[$validatorName] = $choice; + } + } + if ($this->interactive == true && $choice != $defaultChoice) { + $anotherValidator = $this->in(__('Would you like to add another validation rule?', true), array('y', 'n'), 'n'); + } else { + $anotherValidator = 'n'; + } + } + return $validate; + } /** * Handles associations * * @param object $model - * @param boolean $interactive * @return array $assocaitons * @access public */ - function doAssociations(&$model, $interactive = true) { - + function doAssociations(&$model) { if (!is_object($model)) { return false; } - $this->out(__('One moment while the associations are detected.', true)); + if ($this->interactive === true) { + $this->out(__('One moment while the associations are detected.', true)); + } $fields = $model->schema(); - if (empty($fields)) { return false; } - $primaryKey = $model->primaryKey; - $foreignKey = $this->_modelKey($model->name); - - $associations = array('belongsTo' => array(), 'hasMany' => array(), 'hasOne'=> array(), 'hasAndBelongsToMany' => array()); + $associations = array( + 'belongsTo' => array(), 'hasMany' => array(), 'hasOne'=> array(), 'hasAndBelongsToMany' => array() + ); $possibleKeys = array(); - //Look for belongsTo - $i = 0; - foreach ($fields as $fieldName => $field) { - $offset = strpos($fieldName, '_id'); - if ($fieldName != $model->primaryKey && $offset !== false) { - $tmpModelName = $this->_modelNameFromKey($fieldName); - $associations['belongsTo'][$i]['alias'] = $tmpModelName; - $associations['belongsTo'][$i]['className'] = $tmpModelName; - $associations['belongsTo'][$i]['foreignKey'] = $fieldName; - $i++; - } - } - //Look for hasOne and hasMany and hasAndBelongsToMany - $i = $j = 0; + $associations = $this->findBelongsTo($model, $associations); + $associations = $this->findHasOneAndMany($model, $associations); + $associations = $this->findHasAndBelongsToMany($model, $associations); - foreach ($this->__tables as $otherTable) { - App::import('Model'); - $tmpModelName = $this->_modelName($otherTable); - $tempOtherModel = & new Model(array('name' => $tmpModelName, 'table' => $otherTable, 'ds' => $model->useDbConfig)); - $modelFieldsTemp = $tempOtherModel->schema(); - - $offset = strpos($otherTable, $model->table . '_'); - $otherOffset = strpos($otherTable, '_' . $model->table); - - foreach ($modelFieldsTemp as $fieldName => $field) { - if ($field['type'] == 'integer' || $field['type'] == 'string') { - $possibleKeys[$otherTable][] = $fieldName; - } - if ($fieldName != $model->primaryKey && $fieldName == $foreignKey && $offset === false && $otherOffset === false) { - $associations['hasOne'][$j]['alias'] = $tempOtherModel->name; - $associations['hasOne'][$j]['className'] = $tempOtherModel->name; - $associations['hasOne'][$j]['foreignKey'] = $fieldName; - - $associations['hasMany'][$j]['alias'] = $tempOtherModel->name; - $associations['hasMany'][$j]['className'] = $tempOtherModel->name; - $associations['hasMany'][$j]['foreignKey'] = $fieldName; - $j++; - } - } - - if ($offset !== false) { - $offset = strlen($model->table . '_'); - $tmpModelName = $this->_modelName(substr($otherTable, $offset)); - $associations['hasAndBelongsToMany'][$i]['alias'] = $tmpModelName; - $associations['hasAndBelongsToMany'][$i]['className'] = $tmpModelName; - $associations['hasAndBelongsToMany'][$i]['foreignKey'] = $foreignKey; - $associations['hasAndBelongsToMany'][$i]['associationForeignKey'] = $this->_modelKey($tmpModelName); - $associations['hasAndBelongsToMany'][$i]['joinTable'] = $otherTable; - $i++; - } - - if ($otherOffset !== false) { - $tmpModelName = $this->_modelName(substr($otherTable, 0, $otherOffset)); - $associations['hasAndBelongsToMany'][$i]['alias'] = $tmpModelName; - $associations['hasAndBelongsToMany'][$i]['className'] = $tmpModelName; - $associations['hasAndBelongsToMany'][$i]['foreignKey'] = $foreignKey; - $associations['hasAndBelongsToMany'][$i]['associationForeignKey'] = $this->_modelKey($tmpModelName); - $associations['hasAndBelongsToMany'][$i]['joinTable'] = $otherTable; - $i++; - } - } - - if ($interactive !== true) { + if ($this->interactive !== true) { unset($associations['hasOne']); } - if ($interactive === true) { + if ($this->interactive === true) { $this->hr(); if (empty($associations)) { $this->out(__('None found.', true)); } else { $this->out(__('Please confirm the following associations:', true)); $this->hr(); - foreach ($associations as $type => $settings) { - if (!empty($associations[$type])) { - $count = count($associations[$type]); - $response = 'y'; - for ($i = 0; $i < $count; $i++) { - $prompt = "{$model->name} {$type} {$associations[$type][$i]['alias']}"; - $response = $this->in("{$prompt}?", array('y','n'), 'y'); - - if ('n' == low($response) || 'no' == low($response)) { - unset($associations[$type][$i]); - } else { - if ($model->name === $associations[$type][$i]['alias']) { - if ($type === 'belongsTo') { - $alias = 'Parent' . $associations[$type][$i]['alias']; - } - if ($type === 'hasOne' || $type === 'hasMany') { - $alias = 'Child' . $associations[$type][$i]['alias']; - } - - $alternateAlias = $this->in(sprintf(__('This is a self join. Use %s as the alias', true), $alias), array('y', 'n'), 'y'); - - if ('n' == low($alternateAlias) || 'no' == low($alternateAlias)) { - $associations[$type][$i]['alias'] = $this->in(__('Specify an alternate alias.', true)); - } else { - $associations[$type][$i]['alias'] = $alias; - } - } - } - } - $associations[$type] = array_merge($associations[$type]); - } - } + $associations = $this->confirmAssociations($model, $associations); } - - $wannaDoMoreAssoc = $this->in(__('Would you like to define some additional model associations?', true), array('y','n'), 'n'); - - while ((low($wannaDoMoreAssoc) == 'y' || low($wannaDoMoreAssoc) == 'yes')) { - $assocs = array(1 => 'belongsTo', 2 => 'hasOne', 3 => 'hasMany', 4 => 'hasAndBelongsToMany'); - $bad = true; - while ($bad) { - $this->out(__('What is the association type?', true)); - $prompt = "1. belongsTo\n"; - $prompt .= "2. hasOne\n"; - $prompt .= "3. hasMany\n"; - $prompt .= "4. hasAndBelongsToMany\n"; - $assocType = intval($this->in($prompt, null, __("Enter a number", true))); - - if (intval($assocType) < 1 || intval($assocType) > 4) { - $this->out(__('The selection you entered was invalid. Please enter a number between 1 and 4.', true)); - } else { - $bad = false; - } - } - $this->out(__('For the following options be very careful to match your setup exactly. Any spelling mistakes will cause errors.', true)); - $this->hr(); - $alias = $this->in(__('What is the alias for this association?', true)); - $className = $this->in(sprintf(__('What className will %s use?', true), $alias), null, $alias ); - $suggestedForeignKey = null; - if ($assocType == '1') { - $showKeys = $possibleKeys[$model->table]; - $suggestedForeignKey = $this->_modelKey($alias); - } else { - $otherTable = Inflector::tableize($className); - if (in_array($otherTable, $this->__tables)) { - if ($assocType < '4') { - $showKeys = $possibleKeys[$otherTable]; - } else { - $showKeys = null; - } - } else { - $otherTable = $this->in(__('What is the table for this model?', true)); - $showKeys = $possibleKeys[$otherTable]; - } - $suggestedForeignKey = $this->_modelKey($model->name); - } - if (!empty($showKeys)) { - $this->out(__('A helpful List of possible keys', true)); - for ($i = 0; $i < count($showKeys); $i++) { - $this->out($i + 1 . ". " . $showKeys[$i]); - } - $foreignKey = $this->in(__('What is the foreignKey?', true), null, __("Enter a number", true)); - if (intval($foreignKey) > 0 && intval($foreignKey) <= $i ) { - $foreignKey = $showKeys[intval($foreignKey) - 1]; - } - } - if (!isset($foreignKey)) { - $foreignKey = $this->in(__('What is the foreignKey? Specify your own.', true), null, $suggestedForeignKey); - } - if ($assocType == '4') { - $associationForeignKey = $this->in(__('What is the associationForeignKey?', true), null, $this->_modelKey($model->name)); - $joinTable = $this->in(__('What is the joinTable?', true)); - } - $associations[$assocs[$assocType]] = array_values((array)$associations[$assocs[$assocType]]); - $count = count($associations[$assocs[$assocType]]); - $i = ($count > 0) ? $count : 0; - $associations[$assocs[$assocType]][$i]['alias'] = $alias; - $associations[$assocs[$assocType]][$i]['className'] = $className; - $associations[$assocs[$assocType]][$i]['foreignKey'] = $foreignKey; - if ($assocType == '4') { - $associations[$assocs[$assocType]][$i]['associationForeignKey'] = $associationForeignKey; - $associations[$assocs[$assocType]][$i]['joinTable'] = $joinTable; - } - $wannaDoMoreAssoc = $this->in(__('Define another association?', true), array('y','n'), 'y'); + $associations = $this->doMoreAssociations($model, $associations); + } + return $associations; + } +/** + * Find belongsTo relations and add them to the associations list. + * + * @param object $model Model instance of model being generated. + * @param array $associations Array of inprogress associations + * @return array $associations with belongsTo added in. + **/ + function findBelongsTo(&$model, $associations) { + $fields = $model->schema(); + foreach ($fields as $fieldName => $field) { + $offset = strpos($fieldName, '_id'); + if ($fieldName != $model->primaryKey && $fieldName != 'parent_id' && $offset !== false) { + $tmpModelName = $this->_modelNameFromKey($fieldName); + $associations['belongsTo'][] = array( + 'alias' => $tmpModelName, + 'className' => $tmpModelName, + 'foreignKey' => $fieldName, + ); + } elseif ($fieldName == 'parent_id') { + $associations['belongsTo'][] = array( + 'alias' => 'Parent' . $model->name, + 'className' => $model->name, + 'foreignKey' => $fieldName, + ); } } return $associations; } +/** + * Find the hasOne and HasMany relations and add them to associations list + * + * @param object $model Model instance being generated + * @param array $associations Array of inprogress associations + * @return array $associations with hasOne and hasMany added in. + **/ + function findHasOneAndMany(&$model, $associations) { + $foreignKey = $this->_modelKey($model->name); + foreach ($this->__tables as $otherTable) { + $tempOtherModel = $this->_getModelObject($this->_modelName($otherTable)); + $modelFieldsTemp = $tempOtherModel->schema(); + + $pattern = '/_' . preg_quote($model->table, '/') . '|' . preg_quote($model->table, '/') . '_/'; + $possibleJoinTable = preg_match($pattern , $otherTable); + if ($possibleJoinTable == true) { + continue; + } + foreach ($modelFieldsTemp as $fieldName => $field) { + $assoc = false; + if ($fieldName != $model->primaryKey && $fieldName == $foreignKey) { + $assoc = array( + 'alias' => $tempOtherModel->name, + 'className' => $tempOtherModel->name, + 'foreignKey' => $fieldName + ); + } elseif ($otherTable == $model->table && $fieldName == 'parent_id') { + $assoc = array( + 'alias' => 'Child' . $model->name, + 'className' => $model->name, + 'foreignKey' => $fieldName + ); + } + if ($assoc) { + $associations['hasOne'][] = $assoc; + $associations['hasMany'][] = $assoc; + } + + } + } + return $associations; + } +/** + * Find the hasAndBelongsToMany relations and add them to associations list + * + * @param object $model Model instance being generated + * @param array $associations Array of inprogress associations + * @return array $associations with hasAndBelongsToMany added in. + **/ + function findHasAndBelongsToMany(&$model, $associations) { + $foreignKey = $this->_modelKey($model->name); + foreach ($this->__tables as $otherTable) { + $tempOtherModel = $this->_getModelObject($this->_modelName($otherTable)); + $modelFieldsTemp = $tempOtherModel->schema(); + + $offset = strpos($otherTable, $model->table . '_'); + $otherOffset = strpos($otherTable, '_' . $model->table); + + if ($offset !== false) { + $offset = strlen($model->table . '_'); + $habtmName = $this->_modelName(substr($otherTable, $offset)); + $associations['hasAndBelongsToMany'][] = array( + 'alias' => $habtmName, + 'className' => $habtmName, + 'foreignKey' => $foreignKey, + 'associationForeignKey' => $this->_modelKey($habtmName), + 'joinTable' => $otherTable + ); + } elseif ($otherOffset !== false) { + $habtmName = $this->_modelName(substr($otherTable, 0, $otherOffset)); + $associations['hasAndBelongsToMany'][] = array( + 'alias' => $habtmName, + 'className' => $habtmName, + 'foreignKey' => $foreignKey, + 'associationForeignKey' => $this->_modelKey($habtmName), + 'joinTable' => $otherTable + ); + } + } + return $associations; + } +/** + * Interact with the user and confirm associations. + * + * @param array $model Temporary Model instance. + * @param array $associations Array of associations to be confirmed. + * @return array Array of confirmed associations + **/ + function confirmAssociations(&$model, $associations) { + foreach ($associations as $type => $settings) { + if (!empty($associations[$type])) { + $count = count($associations[$type]); + $response = 'y'; + for ($i = 0; $i < $count; $i++) { + $prompt = "{$model->name} {$type} {$associations[$type][$i]['alias']}"; + $response = $this->in("{$prompt}?", array('y','n'), 'y'); + + if ('n' == low($response)) { + unset($associations[$type][$i]); + } elseif ($type == 'hasMany') { + unset($associations['hasOne'][$i]); + } + } + $associations[$type] = array_merge($associations[$type]); + } + } + return $associations; + } +/** + * Interact with the user and generate additional non-conventional associations + * + * @param object $model Temporary model instance + * @param array $associations Array of associations. + * @return array Array of associations. + **/ + function doMoreAssociations($model, $associations) { + $prompt = __('Would you like to define some additional model associations?', true); + $wannaDoMoreAssoc = $this->in($prompt, array('y','n'), 'n'); + $possibleKeys = $this->_generatePossibleKeys(); + while (low($wannaDoMoreAssoc) == 'y') { + $assocs = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany'); + $this->out(__('What is the association type?', true)); + $assocType = intval($this->inOptions($assocs, __('Enter a number',true))); + + $this->out(__("For the following options be very careful to match your setup exactly.\nAny spelling mistakes will cause errors.", true)); + $this->hr(); + + $alias = $this->in(__('What is the alias for this association?', true)); + $className = $this->in(sprintf(__('What className will %s use?', true), $alias), null, $alias ); + $suggestedForeignKey = null; + + if ($assocType == 0) { + $showKeys = $possibleKeys[$model->table]; + $suggestedForeignKey = $this->_modelKey($alias); + } else { + $otherTable = Inflector::tableize($className); + if (in_array($otherTable, $this->__tables)) { + if ($assocType < 3) { + $showKeys = $possibleKeys[$otherTable]; + } else { + $showKeys = null; + } + } else { + $otherTable = $this->in(__('What is the table for this model?', true)); + $showKeys = $possibleKeys[$otherTable]; + } + $suggestedForeignKey = $this->_modelKey($model->name); + } + if (!empty($showKeys)) { + $this->out(__('A helpful List of possible keys', true)); + $foreignKey = $this->inOptions($showKeys, __('What is the foreignKey?', true)); + $foreignKey = $showKeys[intval($foreignKey)]; + } + if (!isset($foreignKey)) { + $foreignKey = $this->in(__('What is the foreignKey? Specify your own.', true), null, $suggestedForeignKey); + } + if ($assocType == 3) { + $associationForeignKey = $this->in(__('What is the associationForeignKey?', true), null, $this->_modelKey($model->name)); + $joinTable = $this->in(__('What is the joinTable?', true)); + } + $associations[$assocs[$assocType]] = array_values((array)$associations[$assocs[$assocType]]); + $count = count($associations[$assocs[$assocType]]); + $i = ($count > 0) ? $count : 0; + $associations[$assocs[$assocType]][$i]['alias'] = $alias; + $associations[$assocs[$assocType]][$i]['className'] = $className; + $associations[$assocs[$assocType]][$i]['foreignKey'] = $foreignKey; + if ($assocType == 3) { + $associations[$assocs[$assocType]][$i]['associationForeignKey'] = $associationForeignKey; + $associations[$assocs[$assocType]][$i]['joinTable'] = $joinTable; + } + $wannaDoMoreAssoc = $this->in(__('Define another association?', true), array('y','n'), 'y'); + } + return $associations; + } +/** + * Finds all possible keys to use on custom associations. + * + * @return array array of tables and possible keys + **/ + function _generatePossibleKeys() { + $possible = array(); + foreach ($this->__tables as $otherTable) { + $tempOtherModel = & new Model(array('table' => $otherTable, 'ds' => $this->connection)); + $modelFieldsTemp = $tempOtherModel->schema(); + foreach ($modelFieldsTemp as $fieldName => $field) { + if ($field['type'] == 'integer' || $field['type'] == 'string') { + $possible[$otherTable][] = $fieldName; + } + } + } + return $possible; + } /** * Assembles and writes a Model file. * * @param mixed $name Model name or object - * @param mixed $associations if array and $name is not an object assume Model associations array otherwise boolean interactive - * @param array $validate Validation rules - * @param string $primaryKey Primary key to use - * @param string $useTable Table to use - * @param string $useDbConfig Database configuration setting to use + * @param mixed $data if array and $name is not an object assume bake data, otherwise boolean. * @access private */ - function bake($name, $associations = array(), $validate = array(), $primaryKey = 'id', $useTable = null, $useDbConfig = 'default') { - + function bake($name, $data = array()) { if (is_object($name)) { - if (!is_array($associations)) { - $associations = $this->doAssociations($name, $associations); - $validate = $this->doValidation($name, $associations); + if ($data == false) { + $data = $associations = array(); + $data['associations'] = $this->doAssociations($name, $associations); + $data['validate'] = $this->doValidation($name); } - $primaryKey = $name->primaryKey; - $useTable = $name->table; - $useDbConfig = $name->useDbConfig; - $name = $name->name; + $data['primaryKey'] = $name->primaryKey; + $data['useTable'] = $name->table; + $data['useDbConfig'] = $name->useDbConfig; + $data['name'] = $name = $name->name; + } else { + $data['name'] = $name; } + $defaults = array('associations' => array(), 'validate' => array(), 'primaryKey' => 'id', + 'useTable' => null, 'useDbConfig' => 'default', 'displayField' => null); + $data = array_merge($defaults, $data); - $out = "plugin}AppModel {\n\n"; - $out .= "\tvar \$name = '{$name}';\n"; + $this->Template->set($data); + $this->Template->set('plugin', Inflector::camelize($this->plugin)); + $out = $this->Template->generate('classes', 'model'); - if ($useDbConfig !== 'default') { - $out .= "\tvar \$useDbConfig = '$useDbConfig';\n"; + $path = $this->path; + if (isset($this->plugin)) { + $path = $this->_pluginPath($this->plugin) . 'models' . DS; } - - if (($useTable && $useTable !== Inflector::tableize($name)) || $useTable === false) { - $table = "'$useTable'"; - if (!$useTable) { - $table = 'false'; - } - $out .= "\tvar \$useTable = $table;\n"; - } - - if ($primaryKey !== 'id') { - $out .= "\tvar \$primaryKey = '$primaryKey';\n"; - } - - $validateCount = count($validate); - if (is_array($validate) && $validateCount > 0) { - $out .= "\tvar \$validate = array(\n"; - $keys = array_keys($validate); - for ($i = 0; $i < $validateCount; $i++) { - $val = "'" . $validate[$keys[$i]] . "'"; - $out .= "\t\t'" . $keys[$i] . "' => array({$val})"; - if ($i + 1 < $validateCount) { - $out .= ","; - } - $out .= "\n"; - } - $out .= "\t);\n"; - } - $out .= "\n"; - - if (!empty($associations)) { - if (!empty($associations['belongsTo']) || !empty($associations['hasOne']) || !empty($associations['hasMany']) || !empty($associations['hasAndBelongsToMany'])) { - $out.= "\t//The Associations below have been created with all possible keys, those that are not needed can be removed\n"; - } - - if (!empty($associations['belongsTo'])) { - $out .= "\tvar \$belongsTo = array(\n"; - $belongsToCount = count($associations['belongsTo']); - - for ($i = 0; $i < $belongsToCount; $i++) { - $out .= "\t\t'{$associations['belongsTo'][$i]['alias']}' => array(\n"; - $out .= "\t\t\t'className' => '{$associations['belongsTo'][$i]['className']}',\n"; - $out .= "\t\t\t'foreignKey' => '{$associations['belongsTo'][$i]['foreignKey']}',\n"; - $out .= "\t\t\t'conditions' => '',\n"; - $out .= "\t\t\t'fields' => '',\n"; - $out .= "\t\t\t'order' => ''\n"; - $out .= "\t\t)"; - if ($i + 1 < $belongsToCount) { - $out .= ","; - } - $out .= "\n"; - - } - $out .= "\t);\n\n"; - } - - if (!empty($associations['hasOne'])) { - $out .= "\tvar \$hasOne = array(\n"; - $hasOneCount = count($associations['hasOne']); - - for ($i = 0; $i < $hasOneCount; $i++) { - $out .= "\t\t'{$associations['hasOne'][$i]['alias']}' => array(\n"; - $out .= "\t\t\t'className' => '{$associations['hasOne'][$i]['className']}',\n"; - $out .= "\t\t\t'foreignKey' => '{$associations['hasOne'][$i]['foreignKey']}',\n"; - $out .= "\t\t\t'dependent' => false,\n"; - $out .= "\t\t\t'conditions' => '',\n"; - $out .= "\t\t\t'fields' => '',\n"; - $out .= "\t\t\t'order' => ''\n"; - $out .= "\t\t)"; - if ($i + 1 < $hasOneCount) { - $out .= ","; - } - $out .= "\n"; - - } - $out .= "\t);\n\n"; - } - - if (!empty($associations['hasMany'])) { - $out .= "\tvar \$hasMany = array(\n"; - $hasManyCount = count($associations['hasMany']); - - for ($i = 0; $i < $hasManyCount; $i++) { - $out .= "\t\t'{$associations['hasMany'][$i]['alias']}' => array(\n"; - $out .= "\t\t\t'className' => '{$associations['hasMany'][$i]['className']}',\n"; - $out .= "\t\t\t'foreignKey' => '{$associations['hasMany'][$i]['foreignKey']}',\n"; - $out .= "\t\t\t'dependent' => false,\n"; - $out .= "\t\t\t'conditions' => '',\n"; - $out .= "\t\t\t'fields' => '',\n"; - $out .= "\t\t\t'order' => '',\n"; - $out .= "\t\t\t'limit' => '',\n"; - $out .= "\t\t\t'offset' => '',\n"; - $out .= "\t\t\t'exclusive' => '',\n"; - $out .= "\t\t\t'finderQuery' => '',\n"; - $out .= "\t\t\t'counterQuery' => ''\n"; - $out .= "\t\t)"; - if ($i + 1 < $hasManyCount) { - $out .= ","; - } - $out .= "\n"; - } - $out .= "\t);\n\n"; - } - - if (!empty($associations['hasAndBelongsToMany'])) { - $out .= "\tvar \$hasAndBelongsToMany = array(\n"; - $hasAndBelongsToManyCount = count($associations['hasAndBelongsToMany']); - - for ($i = 0; $i < $hasAndBelongsToManyCount; $i++) { - $out .= "\t\t'{$associations['hasAndBelongsToMany'][$i]['alias']}' => array(\n"; - $out .= "\t\t\t'className' => '{$associations['hasAndBelongsToMany'][$i]['className']}',\n"; - $out .= "\t\t\t'joinTable' => '{$associations['hasAndBelongsToMany'][$i]['joinTable']}',\n"; - $out .= "\t\t\t'foreignKey' => '{$associations['hasAndBelongsToMany'][$i]['foreignKey']}',\n"; - $out .= "\t\t\t'associationForeignKey' => '{$associations['hasAndBelongsToMany'][$i]['associationForeignKey']}',\n"; - $out .= "\t\t\t'unique' => true,\n"; - $out .= "\t\t\t'conditions' => '',\n"; - $out .= "\t\t\t'fields' => '',\n"; - $out .= "\t\t\t'order' => '',\n"; - $out .= "\t\t\t'limit' => '',\n"; - $out .= "\t\t\t'offset' => '',\n"; - $out .= "\t\t\t'finderQuery' => '',\n"; - $out .= "\t\t\t'deleteQuery' => '',\n"; - $out .= "\t\t\t'insertQuery' => ''\n"; - $out .= "\t\t)"; - if ($i + 1 < $hasAndBelongsToManyCount) { - $out .= ","; - } - $out .= "\n"; - } - $out .= "\t);\n\n"; - } - } - $out .= "}\n"; - $out .= "?>"; - $filename = $this->path . Inflector::underscore($name) . '.php'; + $filename = $path . Inflector::underscore($name) . '.php'; $this->out("\nBaking model class for $name..."); - return $this->createFile($filename, $out); + $this->createFile($filename, $out); + return $out; } - /** * Assembles and writes a unit test file * * @param string $className Model class name * @access private */ - function bakeTest($className, $useTable = null, $associations = array()) { - $results = $this->fixture($className, $useTable); - - if ($results) { - $fixtureInc = 'app'; - if ($this->plugin) { - $fixtureInc = 'plugin.'.Inflector::underscore($this->plugin); - } - - $fixture[] = "'{$fixtureInc}." . Inflector::underscore($className) ."'"; - - if (!empty($associations)) { - $assoc[] = Set::extract($associations, 'belongsTo.{n}.className'); - $assoc[] = Set::extract($associations, 'hasOne.{n}.className'); - $assoc[] = Set::extract($associations, 'hasMany.{n}.className'); - foreach ($assoc as $key => $value) { - if (is_array($value)) { - foreach ($value as $class) { - $fixture[] = "'{$fixtureInc}." . Inflector::underscore($class) ."'"; - } - } - } - } - $fixture = join(", ", $fixture); - - $import = $className; - if (isset($this->plugin)) { - $import = $this->plugin . '.' . $className; - } - - $out = "App::import('Model', '$import');\n\n"; - $out .= "class {$className}TestCase extends CakeTestCase {\n"; - $out .= "\tvar \${$className} = null;\n"; - $out .= "\tvar \$fixtures = array($fixture);\n\n"; - $out .= "\tfunction startTest() {\n"; - $out .= "\t\t\$this->{$className} =& ClassRegistry::init('{$className}');\n"; - $out .= "\t}\n\n"; - $out .= "\tfunction test{$className}Instance() {\n"; - $out .= "\t\t\$this->assertTrue(is_a(\$this->{$className}, '{$className}'));\n"; - $out .= "\t}\n\n"; - $out .= "\tfunction test{$className}Find() {\n"; - $out .= "\t\t\$this->{$className}->recursive = -1;\n"; - $out .= "\t\t\$results = \$this->{$className}->find('first');\n\t\t\$this->assertTrue(!empty(\$results));\n\n"; - $out .= "\t\t\$expected = array('$className' => array(\n$results\n\t\t));\n"; - $out .= "\t\t\$this->assertEqual(\$results, \$expected);\n"; - $out .= "\t}\n"; - $out .= "}\n"; - - $path = MODEL_TESTS; - if (isset($this->plugin)) { - $pluginPath = 'plugins' . DS . Inflector::underscore($this->plugin) . DS; - $path = APP . $pluginPath . 'tests' . DS . 'cases' . DS . 'models' . DS; - } - - $filename = Inflector::underscore($className).'.test.php'; - $this->out("\nBaking unit test for $className..."); - - $header = '$Id'; - $content = ""; - return $this->createFile($path . $filename, $content); - } - return false; + function bakeTest($className) { + $this->Test->plugin = $this->plugin; + $this->Test->connection = $this->connection; + return $this->Test->bake('Model', $className); } /** * outputs the a list of possible models or controllers from database @@ -744,11 +741,61 @@ class ModelTask extends Shell { * @param string $useDbConfig Database configuration name * @access public */ - function listAll($useDbConfig = 'default', $interactive = true) { + function listAll($useDbConfig = null) { + $this->__tables = $this->getAllTables($useDbConfig); + + if ($this->interactive === true) { + $this->out(__('Possible Models based on your current database:', true)); + $this->_modelNames = array(); + $count = count($this->__tables); + for ($i = 0; $i < $count; $i++) { + $this->_modelNames[] = $this->_modelName($this->__tables[$i]); + $this->out($i + 1 . ". " . $this->_modelNames[$i]); + } + } + return $this->__tables; + } +/** + * Interact with the user to determine the table name of a particular model + * + * @param string $modelName Name of the model you want a table for. + * @param string $useDbConfig Name of the database config you want to get tables from. + * @return void + **/ + function getTable($modelName, $useDbConfig = null) { + if (!isset($useDbConfig)) { + $useDbConfig = $this->connection; + } + $db =& ConnectionManager::getDataSource($useDbConfig); + $useTable = Inflector::tableize($modelName); + $fullTableName = $db->fullTableName($useTable, false); + $tableIsGood = false; + + if (array_search($useTable, $this->__tables) === false) { + $this->out(''); + $this->out(sprintf(__("Given your model named '%s',\nCake would expect a database table named '%s'", true), $modelName, $fullTableName)); + $tableIsGood = $this->in(__('Do you want to use this table?', true), array('y','n'), 'y'); + } + if (strtolower($tableIsGood) == 'n') { + $useTable = $this->in(__('What is the name of the table?', true)); + } + return $useTable; + } +/** + * Get an Array of all the tables in the supplied connection + * will halt the script if no tables are found. + * + * @param string $useDbConfig Connection name to scan. + * @return array Array of tables in the database. + **/ + function getAllTables($useDbConfig = null) { + if (!isset($useDbConfig)) { + $useDbConfig = $this->connection; + } + $tables = array(); $db =& ConnectionManager::getDataSource($useDbConfig); $usePrefix = empty($db->config['prefix']) ? '' : $db->config['prefix']; if ($usePrefix) { - $tables = array(); foreach ($db->listSources() as $table) { if (!strncmp($table, $usePrefix, strlen($usePrefix))) { $tables[] = substr($table, strlen($usePrefix)); @@ -761,18 +808,7 @@ class ModelTask extends Shell { $this->err(__('Your database does not have any tables.', true)); $this->_stop(); } - - $this->__tables = $tables; - - if ($interactive === true) { - $this->out(__('Possible Models based on your current database:', true)); - $this->_modelNames = array(); - $count = count($tables); - for ($i = 0; $i < $count; $i++) { - $this->_modelNames[] = $this->_modelName($tables[$i]); - $this->out($i + 1 . ". " . $this->_modelNames[$i]); - } - } + return $tables; } /** * Forces the user to specify the model he wants to bake, and returns the selected model name. @@ -780,13 +816,13 @@ class ModelTask extends Shell { * @return string the model name * @access public */ - function getName($useDbConfig) { + function getName($useDbConfig = null) { $this->listAll($useDbConfig); $enteredModel = ''; while ($enteredModel == '') { - $enteredModel = $this->in(__("Enter a number from the list above, type in the name of another model, or 'q' to exit", true), null, 'q'); + $enteredModel = $this->in(__("Enter a number from the list above,\ntype in the name of another model, or 'q' to exit", true), null, 'q'); if ($enteredModel === 'q') { $this->out(__("Exit", true)); @@ -794,17 +830,15 @@ class ModelTask extends Shell { } if ($enteredModel == '' || intval($enteredModel) > count($this->_modelNames)) { - $this->err(__("The model name you supplied was empty, or the number you selected was not an option. Please try again.", true)); + $this->err(__("The model name you supplied was empty,\nor the number you selected was not an option. Please try again.", true)); $enteredModel = ''; } } - if (intval($enteredModel) > 0 && intval($enteredModel) <= count($this->_modelNames)) { $currentModelName = $this->_modelNames[intval($enteredModel) - 1]; } else { $currentModelName = $enteredModel; } - return $currentModelName; } /** @@ -817,122 +851,31 @@ class ModelTask extends Shell { $this->out("Usage: cake bake model "); $this->hr(); $this->out('Commands:'); - $this->out("\n\tmodel\n\t\tbakes model in interactive mode."); - $this->out("\n\tmodel \n\t\tbakes model file with no associations or validation"); + $this->out(''); + $this->out("model"); + $this->out("\tbakes model in interactive mode."); + $this->out(''); + $this->out("model "); + $this->out("\tbakes model file with no associations or validation"); + $this->out(''); + $this->out("model all"); + $this->out("\tbakes all model files with associations and validation"); $this->out(""); $this->_stop(); } /** - * Builds the tests fixtures for the model and create the file + * Interact with FixtureTask to automatically bake fixtures when baking models. * - * @param string $model the name of the model - * @param string $useTable table name - * @return array $records, used in ModelTask::bakeTest() to create $expected - * @todo move this to a task - */ - function fixture($model, $useTable = null) { - if (!class_exists('CakeSchema')) { - App::import('Model', 'CakeSchema'); - } - $out = "\nclass {$model}Fixture extends CakeTestFixture {\n"; - $out .= "\tvar \$name = '$model';\n"; - - if (!$useTable) { - $useTable = Inflector::tableize($model); - } else { - $out .= "\tvar \$table = '$useTable';\n"; - } - $schema = new CakeSchema(); - $data = $schema->read(array('models' => false)); - - if (!isset($data['tables'][$useTable])) { - return false; - } - $tables[$model] = $data['tables'][$useTable]; - - foreach ($tables as $table => $fields) { - if (!is_numeric($table) && $table !== 'missing') { - $out .= "\tvar \$fields = array(\n"; - $records = array(); - if (is_array($fields)) { - $cols = array(); - foreach ($fields as $field => $value) { - if ($field != 'indexes') { - if (is_string($value)) { - $type = $value; - $value = array('type'=> $type); - } - $col = "\t\t'{$field}' => array('type'=>'" . $value['type'] . "', "; - - switch ($value['type']) { - case 'integer': - $insert = 1; - break; - case 'string'; - $insert = "Lorem ipsum dolor sit amet"; - if (!empty($value['length'])) { - $insert = substr($insert, 0, (int)$value['length'] - 2); - } - $insert = "'$insert'"; - break; - case 'datetime': - $ts = date('Y-m-d H:i:s'); - $insert = "'$ts'"; - break; - case 'date': - $ts = date('Y-m-d'); - $insert = "'$ts'"; - break; - case 'time': - $ts = date('H:i:s'); - $insert = "'$ts'"; - break; - case 'boolean': - $insert = 1; - break; - case 'text': - $insert = - "'Lorem ipsum dolor sit amet, aliquet feugiat. Convallis morbi fringilla gravida,"; - $insert .= "phasellus feugiat dapibus velit nunc, pulvinar eget sollicitudin venenatis cum nullam,"; - $insert .= "vivamus ut a sed, mollitia lectus. Nulla vestibulum massa neque ut et, id hendrerit sit,"; - $insert .= "feugiat in taciti enim proin nibh, tempor dignissim, rhoncus duis vestibulum nunc mattis convallis.'"; - break; - } - $records[] = "\t\t'$field' => $insert"; - unset($value['type']); - $col .= join(', ', $schema->__values($value)); - } else { - $col = "\t\t'indexes' => array("; - $props = array(); - foreach ((array)$value as $key => $index) { - $props[] = "'{$key}' => array(".join(', ', $schema->__values($index)).")"; - } - $col .= join(', ', $props); - } - $col .= ")"; - $cols[] = $col; - } - $out .= join(",\n", $cols); - } - $out .= "\n\t);\n"; - } - } - $records = join(",\n", $records); - $out .= "\tvar \$records = array(array(\n$records\n\t));\n"; - $out .= "}\n"; - $path = TESTS . DS . 'fixtures' . DS; - if (isset($this->plugin)) { - $pluginPath = 'plugins' . DS . Inflector::underscore($this->plugin) . DS; - $path = APP . $pluginPath . 'tests' . DS . 'fixtures' . DS; - } - $filename = Inflector::underscore($model).'_fixture.php'; - $header = '$Id'; - $content = ""; - $this->out("\nBaking test fixture for $model..."); - if ($this->createFile($path . $filename, $content)) { - return str_replace("\t\t", "\t\t\t", $records); - } - return false; + * @param string $className Name of class to bake fixture for + * @param string $useTable Optional table name for fixture to use. + * @access public + * @return void + * @see FixtureTask::bake + **/ + function bakeFixture($className, $useTable = null) { + $this->Fixture->connection = $this->connection; + $this->Fixture->plugin = $this->plugin; + $this->Fixture->bake($className, $useTable); } } ?> \ No newline at end of file diff --git a/cake/console/libs/tasks/plugin.php b/cake/console/libs/tasks/plugin.php index 1fc81374b..80afdcafc 100644 --- a/cake/console/libs/tasks/plugin.php +++ b/cake/console/libs/tasks/plugin.php @@ -1,5 +1,4 @@ params['skel'])) { $this->params['skel'] = ''; - if (is_dir(CAKE_CORE_INCLUDE_PATH.DS.'cake'.DS.'console'.DS.'libs'.DS.'templates'.DS.'skel') === true) { - $this->params['skel'] = CAKE_CORE_INCLUDE_PATH.DS.'cake'.DS.'console'.DS.'libs'.DS.'templates'.DS.'skel'; + if (is_dir(CAKE_CORE_INCLUDE_PATH . DS . CONSOLE_LIBS . 'templates' . DS . 'skel') === true) { + $this->params['skel'] = CAKE_CORE_INCLUDE_PATH . DS . CONSOLE_LIBS . 'templates' . DS . 'skel'; } } - $plugin = null; if (isset($this->args[0])) { $plugin = Inflector::camelize($this->args[0]); - $pluginPath = Inflector::underscore($plugin) . DS; + $pluginPath = $this->_pluginPath($plugin); $this->Dispatch->shiftArgs(); - if (is_dir($this->path . $pluginPath)) { - $this->out(sprintf('Plugin: %s', $plugin)); - $this->out(sprintf('Path: %s', $this->path . $pluginPath)); - $this->hr(); + if (is_dir($pluginPath)) { + $this->out(sprintf(__('Plugin: %s', true), $plugin)); + $this->out(sprintf(__('Path: %s', true), $pluginPath)); } elseif (isset($this->args[0])) { - $this->err(sprintf('%s in path %s not found.', $plugin, $this->path . $pluginPath)); + $this->err(sprintf(__('%s in path %s not found.', true), $plugin, $pluginPath)); $this->_stop(); } else { $this->__interactive($plugin); } + } else { + return $this->__interactive(); } if (isset($this->args[0])) { @@ -90,17 +84,16 @@ class PluginTask extends Shell { $this->Dispatch->shiftArgs(); if (in_array($task, $this->tasks)) { $this->{$task}->plugin = $plugin; - $this->{$task}->path = $this->path . $pluginPath . Inflector::underscore(Inflector::pluralize($task)) . DS; + $this->{$task}->path = $pluginPath . Inflector::underscore(Inflector::pluralize($task)) . DS; if (!is_dir($this->{$task}->path)) { $this->err(sprintf(__("%s directory could not be found.\nBe sure you have created %s", true), $task, $this->{$task}->path)); } $this->{$task}->loadTasks(); - $this->{$task}->execute(); + return $this->{$task}->execute(); } } } - /** * Interactive interface * @@ -116,7 +109,6 @@ class PluginTask extends Shell { $this->err(sprintf(__("An error occured trying to bake: %s in %s", true), $plugin, $this->path . $pluginPath)); } } - /** * Bake the plugin, create directories and files * @@ -125,28 +117,49 @@ class PluginTask extends Shell { * @return bool */ function bake($plugin) { - $pluginPath = Inflector::underscore($plugin); + $pathOptions = App::path('plugins'); + if (count($pathOptions) > 1) { + $this->findPath($pathOptions); + } + $this->hr(); - $this->out("Plugin Name: $plugin"); - $this->out("Plugin Directory: {$this->path}{$pluginPath}"); + $this->out(sprintf(__("Plugin Name: %s", true), $plugin)); + $this->out(sprintf(__("Plugin Directory: %s", true), $this->path . $pluginPath)); $this->hr(); + $looksGood = $this->in(__('Look okay?', true), array('y', 'n', 'q'), 'y'); - $looksGood = $this->in('Look okay?', array('y', 'n', 'q'), 'y'); - - if (low($looksGood) == 'y' || low($looksGood) == 'yes') { + if (strtolower($looksGood) == 'y') { $verbose = $this->in(__('Do you want verbose output?', true), array('y', 'n'), 'n'); - $Folder = new Folder($this->path . $pluginPath); - $directories = array('models' . DS . 'behaviors', 'controllers' . DS . 'components', 'views' . DS . 'helpers'); + $Folder =& new Folder($this->path . $pluginPath); + $directories = array( + 'config' . DS . 'sql', + 'models' . DS . 'behaviors', + 'controllers' . DS . 'components', + 'views' . DS . 'helpers', + 'tests' . DS . 'cases' . DS . 'components', + 'tests' . DS . 'cases' . DS . 'helpers', + 'tests' . DS . 'cases' . DS . 'behaviors', + 'tests' . DS . 'cases' . DS . 'controllers', + 'tests' . DS . 'cases' . DS . 'models', + 'tests' . DS . 'groups', + 'tests' . DS . 'fixtures', + 'vendors' . DS . 'img', + 'vendors' . DS . 'js', + 'vendors' . DS . 'css', + 'vendors' . DS . 'shells' + ); foreach ($directories as $directory) { - $Folder->create($this->path . $pluginPath . DS . $directory); + $dirPath = $this->path . $pluginPath . DS . $directory; + $Folder->create($dirPath); + $File =& new File($dirPath . DS . 'empty', true); } - if (low($verbose) == 'y' || low($verbose) == 'yes') { + if (strtolower($verbose) == 'y') { foreach ($Folder->messages() as $message) { $this->out($message); } @@ -180,6 +193,26 @@ class PluginTask extends Shell { return true; } +/** + * find and change $this->path to the user selection + * + * @return void + **/ + function findPath($pathOptions) { + $valid = false; + $max = count($pathOptions); + while (!$valid) { + foreach ($pathOptions as $i => $option) { + $this->out($i + 1 .'. ' . $option); + } + $prompt = __('Choose a plugin path from the paths above.', true); + $choice = $this->in($prompt); + if (intval($choice) > 0 && intval($choice) <= $max) { + $valid = true; + } + } + $this->path = $pathOptions[$choice - 1]; + } /** * Help * @@ -191,10 +224,18 @@ class PluginTask extends Shell { $this->out("Usage: cake bake plugin ..."); $this->hr(); $this->out('Commands:'); - $this->out("\n\tplugin \n\t\tbakes plugin directory structure"); - $this->out("\n\tplugin model\n\t\tbakes model. Run 'cake bake model help' for more info."); - $this->out("\n\tplugin controller\n\t\tbakes controller. Run 'cake bake controller help' for more info."); - $this->out("\n\tplugin view\n\t\tbakes view. Run 'cake bake view help' for more info."); + $this->out(''); + $this->out("plugin "); + $this->out("\tbakes plugin directory structure"); + $this->out(''); + $this->out("plugin model"); + $this->out("\tbakes model. Run 'cake bake model help' for more info."); + $this->out(''); + $this->out("plugin controller"); + $this->out("\tbakes controller. Run 'cake bake controller help' for more info."); + $this->out(''); + $this->out("plugin view"); + $this->out("\tbakes view. Run 'cake bake view help' for more info."); $this->out(""); $this->_stop(); } diff --git a/cake/console/libs/tasks/project.php b/cake/console/libs/tasks/project.php index 0502f8429..fda6a7cf7 100644 --- a/cake/console/libs/tasks/project.php +++ b/cake/console/libs/tasks/project.php @@ -1,32 +1,24 @@ in('A project already exists in this location: '.$project.' Overwrite?', array('y','n'), 'n'); + $response = $this->in('A project already exists in this location: ' . $project . ' Overwrite?', array('y','n'), 'n'); if (strtolower($response) === 'n') { $response = $project = false; } @@ -144,7 +142,7 @@ class ProjectTask extends Shell { $looksGood = $this->in('Look okay?', array('y', 'n', 'q'), 'y'); - if (low($looksGood) == 'y' || low($looksGood) == 'yes') { + if (strtolower($looksGood) == 'y') { $verbose = $this->in(__('Do you want verbose output?', true), array('y', 'n'), 'n'); $Folder = new Folder($skel); @@ -157,14 +155,14 @@ class ProjectTask extends Shell { return false; } - if (low($verbose) == 'y' || low($verbose) == 'yes') { + if (strtolower($verbose) == 'y') { foreach ($Folder->messages() as $message) { $this->out($message); } } return true; - } elseif (low($looksGood) == 'q' || low($looksGood) == 'quit') { + } elseif (strtolower($looksGood) == 'q') { $this->out('Bake Aborted.'); } else { $this->execute(false); @@ -181,7 +179,7 @@ class ProjectTask extends Shell { function createHome($dir) { $app = basename($dir); $path = $dir . 'views' . DS . 'pages' . DS; - include(CAKE_CORE_INCLUDE_PATH.DS.'cake'.DS.'console'.DS.'libs'.DS.'templates'.DS.'views'.DS.'home.ctp'); + include(CAKE_CORE_INCLUDE_PATH.DS.'cake'.DS.'console'.DS.'libs'.DS.'templates'.DS.'default'.DS.'views'.DS.'home.ctp'); return $this->createFile($path.'home.ctp', $output); } /** @@ -248,7 +246,8 @@ class ProjectTask extends Shell { * @access public */ function cakeAdmin($name) { - $File =& new File(CONFIGS . 'core.php'); + $path = (empty($this->configPath)) ? CONFIGS : $this->configPath; + $File =& new File($path . 'core.php'); $contents = $File->read(); if (preg_match('%([/\\t\\x20]*Configure::write\(\'Routing.admin\',[\\t\\x20\'a-z]*\\);)%', $contents, $match)) { $result = str_replace($match[0], "\t" . 'Configure::write(\'Routing.admin\', \''.$name.'\');', $contents); @@ -262,6 +261,32 @@ class ProjectTask extends Shell { return false; } } +/** + * Checks for Configure::read('Routing.admin') and forces user to input it if not enabled + * + * @return string Admin route to use + * @access public + */ + function getAdmin() { + $admin = ''; + $cakeAdmin = null; + $adminRoute = Configure::read('Routing.admin'); + if (!empty($adminRoute)) { + return $adminRoute . '_'; + } + $this->out('You need to enable Configure::write(\'Routing.admin\',\'admin\') in /app/config/core.php to use admin routing.'); + $this->out('What would you like the admin route to be?'); + $this->out('Example: www.example.com/admin/controller'); + while ($admin == '') { + $admin = $this->in("What would you like the admin route to be?", null, 'admin'); + } + if ($this->cakeAdmin($admin) !== true) { + $this->out('Unable to write to /app/config/core.php.'); + $this->out('You need to enable Configure::write(\'Routing.admin\',\'admin\') in /app/config/core.php to use admin routing.'); + $this->_stop(); + } + return $admin . '_'; + } /** * Help * @@ -273,7 +298,10 @@ class ProjectTask extends Shell { $this->out("Usage: cake bake project "); $this->hr(); $this->out('Commands:'); - $this->out("\n\tproject \n\t\tbakes app directory structure.\n\t\tif begins with '/' path is absolute."); + $this->out(''); + $this->out("project "); + $this->out("\tbakes app directory structure."); + $this->out("\tif begins with '/' path is absolute."); $this->out(""); $this->_stop(); } diff --git a/cake/console/libs/tasks/template.php b/cake/console/libs/tasks/template.php new file mode 100644 index 000000000..4b682e70f --- /dev/null +++ b/cake/console/libs/tasks/template.php @@ -0,0 +1,183 @@ + $path + * + * @var array + **/ + var $templatePaths = array(); +/** + * Initialize callback. Setup paths for the template task. + * + * @access public + * @return void + **/ + function initialize() { + $this->templatePaths = $this->_findThemes(); + } +/** + * Find the paths to all the installed shell themes in the app. + * + * Bake themes are directories not named `skel` inside a `vendors/shells/templates` path. + * + * @return array Array of bake themes that are installed. + **/ + function _findThemes() { + $paths = $this->Dispatch->shellPaths; + $themes = array(); + foreach ($paths as $path) { + $Folder =& new Folder($path . 'templates', false); + $contents = $Folder->read(); + $subDirs = $contents[0]; + foreach ($subDirs as $dir) { + if (empty($dir) || $dir == 'skel') { + continue; + } + $templateDir = $path . 'templates' . DS . $dir . DS; + $themes[$dir] = $templateDir; + } + } + return $themes; + } +/** + * Set variable values to the template scope + * + * @param mixed $one A string or an array of data. + * @param mixed $two Value in case $one is a string (which then works as the key). + * Unused if $one is an associative array, otherwise serves as the values to $one's keys. + * @return void + */ + function set($one, $two = null) { + $data = null; + if (is_array($one)) { + if (is_array($two)) { + $data = array_combine($one, $two); + } else { + $data = $one; + } + } else { + $data = array($one => $two); + } + + if ($data == null) { + return false; + } + + foreach ($data as $name => $value) { + $this->templateVars[$name] = $value; + } + } +/** + * Runs the template + * + * @param string $directory directory / type of thing you want + * @param string $filename template name + * @param string $vars Additional vars to set to template scope. + * @access public + * @return contents of generated code template + **/ + function generate($directory, $filename, $vars = null) { + if ($vars !== null) { + $this->set($vars); + } + if (empty($this->templatePaths)) { + $this->initialize(); + } + $themePath = $this->getThemePath(); + $templateFile = $this->_findTemplate($themePath, $directory, $filename); + if ($templateFile) { + extract($this->templateVars); + ob_start(); + ob_implicit_flush(0); + include($templateFile); + $content = ob_get_clean(); + return $content; + } + return ''; + } +/** + * Find the theme name for the current operation. + * If there is only one theme in $templatePaths it will be used. + * If there is a -theme param in the cli args, it will be used. + * If there is more than one installed theme user interaction will happen + * + * @return string returns the path to the selected theme. + **/ + function getThemePath() { + if (count($this->templatePaths) == 1) { + $paths = array_values($this->templatePaths); + return $paths[0]; + } + if (!empty($this->params['theme']) && isset($this->templatePaths[$this->params['theme']])) { + return $this->templatePaths[$this->params['theme']]; + } + + $this->hr(); + $this->out(__('You have more than one set of templates installed.', true)); + $this->out(__('Please choose the template set you wish to use:', true)); + $this->hr(); + + $i = 1; + $indexedPaths = array(); + foreach ($this->templatePaths as $key => $path) { + $this->out($i . '. ' . $key); + $indexedPaths[$i] = $path; + $i++; + } + $index = $this->in(__('Which bake theme would you like to use?', true), range(1, $i - 1), 1); + $themeNames = array_keys($this->templatePaths); + $this->Dispatch->params['theme'] = $themeNames[$index - 1]; + return $indexedPaths[$index]; + } +/** + * Find a template inside a directory inside a path. + * Will scan all other theme dirs if the template is not found in the first directory. + * + * @param string $path The initial path to look for the file on. If it is not found fallbacks will be used. + * @param string $directory Subdirectory to look for ie. 'views', 'objects' + * @param string $filename lower_case_underscored filename you want. + * @access public + * @return string filename will exit program if template is not found. + **/ + function _findTemplate($path, $directory, $filename) { + $themeFile = $path . $directory . DS . $filename . '.ctp'; + if (file_exists($themeFile)) { + return $themeFile; + } + foreach ($this->templatePaths as $path) { + $templatePath = $path . $directory . DS . $filename . '.ctp'; + if (file_exists($templatePath)) { + return $templatePath; + } + } + $this->err(sprintf(__('Could not find template for %s', true), $filename)); + $this->_stop(); + return false; + } +} +?> \ No newline at end of file diff --git a/cake/console/libs/tasks/test.php b/cake/console/libs/tasks/test.php index 45b853448..54668a843 100644 --- a/cake/console/libs/tasks/test.php +++ b/cake/console/libs/tasks/test.php @@ -1,27 +1,21 @@ args) > 1) { - $class = Inflector::underscore($this->args[0]); - if ($this->bake($class, $this->args[1])) { + $type = Inflector::underscore($this->args[0]); + if ($this->bake($type, $this->args[1])) { $this->out('done'); } } @@ -71,133 +90,304 @@ class TestTask extends Shell { * * @access private */ - function __interactive($class = null) { + function __interactive($type = null) { + $this->interactive = true; $this->hr(); - $this->out(sprintf("Bake Tests\nPath: %s", $this->path)); + $this->out(__('Bake Tests', true)); + $this->out(sprintf(__("Path: %s", true), $this->path)); $this->hr(); - $key = null; - $options = array('Behavior', 'Helper', 'Component', 'Model', 'Controller'); - - if ($class !== null) { - $class = Inflector::camelize($class); - if (in_array($class, $options)) { - $key = array_search($class); + $selection = null; + if ($type) { + $type = Inflector::camelize($type); + if (!in_array($type, $this->classTypes)) { + unset($type); } } - - while ($class == null) { - $cases = array(); - $this->hr(); - $this->out("Select a class:"); - $this->hr(); - - $keys = array(); - foreach ($options as $key => $option) { - $this->out(++$key . '. ' . $option); - $keys[] = $key; - } - $keys[] = 'q'; - - $key = $this->in(__("Enter the class to test or (q)uit", true), $keys, 'q'); - - if ($key != 'q') { - if (isset($options[--$key])) { - $class = $options[$key]; - } - - if ($class) { - $name = $this->in(__("Enter the name for the test or (q)uit", true), null, 'q'); - if ($name !== 'q') { - $case = null; - while ($case !== 'q') { - $case = $this->in(__("Enter a test case or (q)uit", true), null, 'q'); - if ($case !== 'q') { - $cases[] = $case; - } - } - if ($this->bake($class, $name, $cases)) { - $this->out(__("Test baked\n", true)); - $type = null; - } - $class = null; - } - } - } else { - $this->_stop(); - } + if (!$type) { + $type = $this->getObjectType(); } + $className = $this->getClassName($type); + return $this->bake($type, $className); } /** - * Writes File + * Completes final steps for generating data to create test case. * + * @param string $type Type of object to bake test case for ie. Model, Controller + * @param string $className the 'cake name' for the class ie. Posts for the PostsController * @access public */ - function bake($class, $name = null, $cases = array()) { - if (!$name) { - return false; + function bake($type, $className) { + if ($this->typeCanDetectFixtures($type) && $this->isLoadableClass($type, $className)) { + $this->out(__('Bake is detecting possible fixtures..', true)); + $testSubject =& $this->buildTestSubject($type, $className); + $this->generateFixtureList($testSubject); + } elseif ($this->interactive) { + $this->getUserFixtures(); + } + $fullClassName = $this->getRealClassName($type, $className); + + $methods = array(); + if (class_exists($fullClassName)) { + $methods = $this->getTestableMethods($fullClassName); + } + $mock = $this->hasMockClass($type, $fullClassName); + $construction = $this->generateConstructor($type, $fullClassName); + + $plugin = null; + if ($this->plugin) { + $plugin = $this->plugin . '.'; } - if (!is_array($cases)) { - $cases = array($cases); - } + $this->Template->set('fixtures', $this->_fixtures); + $this->Template->set('plugin', $plugin); + $this->Template->set(compact('className', 'methods', 'type', 'fullClassName', 'mock', 'construction')); + $out = $this->Template->generate('classes', 'test'); - if (strpos($this->path, $class) === false) { - $this->filePath = $this->path . 'cases' . DS . Inflector::tableize($class) . DS; + $filename = $this->testCaseFileName($type, $className); + $made = $this->createFile($filename, $out); + if ($made) { + return $out; } - - $class = Inflector::classify($class); - $name = Inflector::classify($name); - - $import = $name; - if (isset($this->plugin)) { - $import = $this->plugin . '.' . $name; - } - $extras = $this->__extras($class); - $out = "App::import('$class', '$import');\n"; - if ($class == 'Model') { - $class = null; - } - $out .= "class Test{$name} extends {$name}{$class} {\n"; - $out .= "{$extras}"; - $out .= "}\n\n"; - $out .= "class {$name}{$class}Test extends CakeTestCase {\n"; - $out .= "\n\tfunction startTest() {"; - $out .= "\n\t\t\$this->{$name} = new Test{$name}();"; - $out .= "\n\t}\n"; - $out .= "\n\tfunction test{$name}Instance() {\n"; - $out .= "\t\t\$this->assertTrue(is_a(\$this->{$name}, '{$name}{$class}'));\n\t}\n"; - foreach ($cases as $case) { - $case = Inflector::classify($case); - $out .= "\n\tfunction test{$case}() {\n\n\t}\n"; - } - $out .= "}\n"; - - $this->out("Baking unit test for $name..."); - $this->out($out); - $ok = $this->in(__('Is this correct?', true), array('y', 'n'), 'y'); - if ($ok == 'n') { - return false; - } - - $header = '$Id'; - $content = ""; - return $this->createFile($this->filePath . Inflector::underscore($name) . '.test.php', $content); + return false; } /** - * Handles the extra stuff needed + * Interact with the user and get their chosen type. Can exit the script. * - * @access private - */ - function __extras($class) { - $extras = null; - switch ($class) { - case 'Model': - $extras = "\n\tvar \$cacheSources = false;"; - $extras .= "\n\tvar \$useDbConfig = 'test_suite';\n"; - break; + * @return string Users chosen type. + **/ + function getObjectType() { + $this->hr(); + $this->out(__("Select an object type:", true)); + $this->hr(); + + $keys = array(); + foreach ($this->classTypes as $key => $option) { + $this->out(++$key . '. ' . $option); + $keys[] = $key; } - return $extras; + $keys[] = 'q'; + $selection = $this->in(__("Enter the type of object to bake a test for or (q)uit", true), $keys, 'q'); + if ($selection == 'q') { + return $this->_stop(); + } + return $this->classTypes[$selection - 1]; + } +/** + * Get the user chosen Class name for the chosen type + * + * @param string $objectType Type of object to list classes for i.e. Model, Controller. + * @return string Class name the user chose. + **/ + function getClassName($objectType) { + $options = Configure::listObjects(strtolower($objectType)); + $this->out(sprintf(__('Choose a %s class', true), $objectType)); + $keys = array(); + foreach ($options as $key => $option) { + $this->out(++$key . '. ' . $option); + $keys[] = $key; + } + $selection = $this->in(__('Choose an existing class, or enter the name of a class that does not exist', true)); + if (isset($options[$selection - 1])) { + return $options[$selection - 1]; + } + return $selection; + } +/** + * Checks whether the chosen type can find its own fixtures. + * Currently only model, and controller are supported + * + * @return boolean + **/ + function typeCanDetectFixtures($type) { + $type = strtolower($type); + return ($type == 'controller' || $type == 'model'); + } +/** + * Check if a class with the given type is loaded or can be loaded. + * + * @return boolean + **/ + function isLoadableClass($type, $class) { + return App::import($type, $class); + } +/** + * Construct an instance of the class to be tested. + * So that fixtures can be detected + * + * @return object + **/ + function &buildTestSubject($type, $class) { + ClassRegistry::flush(); + App::import($type, $class); + $class = $this->getRealClassName($type, $class); + if (strtolower($type) == 'model') { + $instance =& ClassRegistry::init($class); + } else { + $instance =& new $class(); + } + return $instance; + } +/** + * Gets the real class name from the cake short form. + * + * @return string Real classname + **/ + function getRealClassName($type, $class) { + if (strtolower($type) == 'model') { + return $class; + } + return $class . $type; + } +/** + * Get methods declared in the class given. + * No parent methods will be returned + * + * @param string $className Name of class to look at. + * @return array Array of method names. + **/ + function getTestableMethods($className) { + $classMethods = get_class_methods($className); + $parentMethods = get_class_methods(get_parent_class($className)); + $thisMethods = array_diff($classMethods, $parentMethods); + $out = array(); + foreach ($thisMethods as $method) { + if (substr($method, 0, 1) != '_') { + $out[] = $method; + } + } + return $out; + } +/** + * Generate the list of fixtures that will be required to run this test based on + * loaded models. + * + * @param object The object you want to generate fixtures for. + * @return array Array of fixtures to be included in the test. + **/ + function generateFixtureList(&$subject) { + $this->_fixtures = array(); + if (is_a($subject, 'Model')) { + $this->_processModel($subject); + } elseif (is_a($subject, 'Controller')) { + $this->_processController($subject); + } + return array_values($this->_fixtures); + } +/** + * Process a model recursively and pull out all the + * model names converting them to fixture names. + * + * @return void + * @access protected + **/ + function _processModel(&$subject) { + $this->_addFixture($subject->name); + $associated = $subject->getAssociated(); + foreach ($associated as $alias => $type) { + $className = $subject->{$alias}->name; + if (!isset($this->_fixtures[$className])) { + $this->_processModel($subject->{$alias}); + } + if ($type == 'hasAndBelongsToMany') { + $joinModel = Inflector::classify($subject->hasAndBelongsToMany[$alias]['joinTable']); + if (!isset($this->_fixtures[$joinModel])) { + $this->_processModel($subject->{$joinModel}); + } + } + } + } +/** + * Process all the models attached to a controller + * and generate a fixture list. + * + * @return void + * @access protected + **/ + function _processController(&$subject) { + $subject->constructClasses(); + $models = array(Inflector::classify($subject->name)); + if (!empty($subject->uses)) { + $models = $subject->uses; + } + foreach ($models as $model) { + $this->_processModel($subject->{$model}); + } + } +/** + * Add classname to the fixture list. + * Sets the app. or plugin.plugin_name. prefix. + * + * @return void + * @access protected + **/ + function _addFixture($name) { + $parent = get_parent_class($name); + $prefix = 'app.'; + if (strtolower($parent) != 'appmodel' && strtolower(substr($parent, -8)) == 'appmodel') { + $pluginName = substr($parent, 0, strlen($parent) -8); + $prefix = 'plugin.' . Inflector::underscore($pluginName) . '.'; + } + $fixture = $prefix . Inflector::underscore($name); + $this->_fixtures[$name] = $fixture; + } +/** + * Interact with the user to get additional fixtures they want to use. + * + * @return void + **/ + function getUserFixtures() { + $proceed = $this->in(__('Bake could not detect fixtures, would you like to add some?', true), array('y','n'), 'n'); + $fixtures = array(); + if (strtolower($proceed) == 'y') { + $fixtureList = $this->in(__("Please provide a comma separated list of the fixtures names you'd like to use.\nExample: 'app.comment, app.post, plugin.forums.post'", true)); + $fixtureListTrimmed = str_replace(' ', '', $fixtureList); + $fixtures = explode(',', $fixtureListTrimmed); + } + $this->_fixtures = array_merge($this->_fixtures, $fixtures); + return $fixtures; + } +/** + * Is a mock class required for this type of test? + * Controllers require a mock class. + * + * @return boolean + **/ + function hasMockClass($type) { + $type = strtolower($type); + return $type == 'controller'; + } +/** + * Generate a constructor code snippet for the type and classname + * + * @return string Constructor snippet for the thing you are building. + **/ + function generateConstructor($type, $fullClassName) { + $type = strtolower($type); + if ($type == 'model') { + return "ClassRegistry::init('$fullClassName');\n"; + } + if ($type == 'controller') { + return "new Test$fullClassName();\n\t\t\$this->{$fullClassName}->constructClasses();\n"; + } + return "new $fullClassName()\n"; + } +/** + * make the filename for the test case. resolve the suffixes for controllers + * and get the plugin path if needed. + * + * @return string filename the test should be created on + **/ + function testCaseFileName($type, $className) { + $path = $this->path; + if (isset($this->plugin)) { + $path = $this->_pluginPath($this->plugin) . 'tests' . DS; + } + $path .= 'cases' . DS . Inflector::tableize($type) . DS; + if (strtolower($type) == 'controller') { + $className = $this->getRealClassName($type, $className); + } + return $path . Inflector::underscore($className) . '.test.php'; } } ?> \ No newline at end of file diff --git a/cake/console/libs/tasks/view.php b/cake/console/libs/tasks/view.php index 4dfdc5089..ba5f1c37e 100644 --- a/cake/console/libs/tasks/view.php +++ b/cake/console/libs/tasks/view.php @@ -1,5 +1,4 @@ args[0])) { + if (!isset($this->connection)) { + $this->connection = 'default'; + } $controller = $action = $alias = null; $this->controllerName = Inflector::camelize($this->args[0]); $this->controllerPath = Inflector::underscore($this->controllerName); @@ -115,34 +114,86 @@ class ViewTask extends Shell { $action = $this->template; } + if (strtolower($this->args[0]) == 'all') { + return $this->all(); + } + if (in_array($action, $this->scaffoldActions)) { $this->bake($action, true); } elseif ($action) { $this->bake($action, true); } else { $vars = $this->__loadController(); - if ($vars) { - - $methods = array_diff( - array_map('strtolower', get_class_methods($this->controllerName . 'Controller')), - array_map('strtolower', get_class_methods('appcontroller')) - ); - if (empty($methods)) { - $methods = $this->scaffoldActions; - } - $adminDelete = null; - - $adminRoute = Configure::read('Routing.admin'); - if (!empty($adminRoute)) { - $adminDelete = $adminRoute.'_delete'; - } - foreach ($methods as $method) { - if ($method{0} != '_' && !in_array($method, array('delete', $adminDelete))) { - $content = $this->getContent($method, $vars); - $this->bake($method, $content); + $methods = $this->_methodsToBake(); + $methods = array_diff( + array_map('strtolower', get_class_methods($this->controllerName . 'Controller')), + array_map('strtolower', get_class_methods('appcontroller')) + ); + if (empty($methods)) { + $methods = $this->scaffoldActions; + } + $adminRoute = Configure::read('Routing.admin'); + if ($adminRoute && isset($this->params['admin'])) { + foreach ($methods as $i => $method) { + if (strpos($method, $adminRoute . '_') === false) { + unset($methods[$i]); } } } + $adminDelete = null; + if (!empty($adminRoute)) { + $adminDelete = $adminRoute . '_delete'; + } + foreach ($methods as $method) { + if ($method{0} != '_' && !in_array($method, array('delete', $adminDelete))) { + $content = $this->getContent($method, $vars); + $this->bake($method, $content); + } + } + } + } + } +/** + * Get a list of actions that can / should have views baked for them. + * + * @return array Array of action names that should be baked + **/ + function _methodsToBake() { + $methods = array_diff( + array_map('strtolower', get_class_methods($this->controllerName . 'Controller')), + array_map('strtolower', get_class_methods('appcontroller')) + ); + if (empty($methods)) { + $methods = $this->scaffoldActions; + } + $adminRoute = Configure::read('Routing.admin'); + foreach ($methods as $i => $method) { + if ($method == 'delete' || $method = $adminRoute . '_delete' || $method{0} == '_') { + unset($methods[$i]); + } + if ($adminRoute && isset($this->params['admin']) && strpos($method, $adminRoute . '_') === false) { + unset($methods[$i]); + } + } + return $methods; + } +/** + * Bake All views for All controllers. + * + * @return void + **/ + function all() { + $actions = $this->scaffoldActions; + $this->Controller->interactive = false; + $tables = $this->Controller->listAll($this->connection, false); + $this->interactive = false; + foreach ($tables as $table) { + $model = $this->_modelName($table); + $this->controllerName = $this->_controllerName($model); + $this->controllerPath = Inflector::underscore($this->controllerName); + if (App::import('Model', $model)) { + $vars = $this->__loadController(); + $this->bakeActions($actions, $vars); } } } @@ -155,70 +206,48 @@ class ViewTask extends Shell { $this->hr(); $this->out(sprintf("Bake View\nPath: %s", $this->path)); $this->hr(); - $wannaDoAdmin = 'n'; - $wannaDoScaffold = 'y'; - $this->interactive = false; + if (empty($this->connection)) { + $this->connection = $this->DbConfig->getConfig(); + } + + $this->Controller->connection = $this->connection; $this->controllerName = $this->Controller->getName(); - $this->controllerPath = low(Inflector::underscore($this->controllerName)); + $this->controllerPath = strtolower(Inflector::underscore($this->controllerName)); - $interactive = $this->in("Would you like bake to build your views interactively?\nWarning: Choosing no will overwrite {$this->controllerName} views if it exist.", array('y','n'), 'y'); + $prompt = sprintf(__("Would you like bake to build your views interactively?\nWarning: Choosing no will overwrite %s views if it exist.", true), $this->controllerName); + $interactive = $this->in($prompt, array('y', 'n'), 'n'); - if (low($interactive) == 'y' || low($interactive) == 'yes') { - $this->interactive = true; - $wannaDoScaffold = $this->in("Would you like to create some scaffolded views (index, add, view, edit) for this controller?\nNOTE: Before doing so, you'll need to create your controller and model classes (including associated models).", array('y','n'), 'n'); + if (strtolower($interactive) == 'n') { + $this->interactive = false; } - if (low($wannaDoScaffold) == 'y' || low($wannaDoScaffold) == 'yes') { - $wannaDoAdmin = $this->in("Would you like to create the views for admin routing?", array('y','n'), 'y'); - } - $admin = false; + $prompt = __("Would you like to create some CRUD views\n(index, add, view, edit) for this controller?\nNOTE: Before doing so, you'll need to create your controller\nand model classes (including associated models).", true); + $wannaDoScaffold = $this->in($prompt, array('y','n'), 'y'); - if ((low($wannaDoAdmin) == 'y' || low($wannaDoAdmin) == 'yes')) { - $admin = $this->getAdmin(); - } + $wannaDoAdmin = $this->in(__("Would you like to create the views for admin routing?", true), array('y','n'), 'n'); - if (low($wannaDoScaffold) == 'y' || low($wannaDoScaffold) == 'yes') { - $actions = $this->scaffoldActions; - if ($admin) { - foreach ($actions as $action) { - $actions[] = $admin . $action; - } - } + if (strtolower($wannaDoScaffold) == 'y' || strtolower($wannaDoAdmin) == 'y') { $vars = $this->__loadController(); - if ($vars) { - foreach ($actions as $action) { - $content = $this->getContent($action, $vars); - $this->bake($action, $content); + if (strtolower($wannaDoScaffold) == 'y') { + $actions = $this->scaffoldActions; + $this->bakeActions($actions, $vars); + } + if (strtolower($wannaDoAdmin) == 'y') { + $admin = $this->Project->getAdmin(); + $regularActions = $this->scaffoldActions; + $adminActions = array(); + foreach ($regularActions as $action) { + $adminActions[] = $admin . $action; } + $this->bakeActions($adminActions, $vars); } $this->hr(); $this->out(''); - $this->out('View Scaffolding Complete.'."\n"); + $this->out(__("View Scaffolding Complete.\n", true)); } else { - $action = ''; - while ($action == '') { - $action = $this->in('Action Name? (use camelCased function name)'); - if ($action == '') { - $this->out('The action name you supplied was empty. Please try again.'); - } - } - $this->out(''); - $this->hr(); - $this->out('The following view will be created:'); - $this->hr(); - $this->out("Controller Name: {$this->controllerName}"); - $this->out("Action Name: {$action}"); - $this->out("Path: ".$this->params['app'] . DS . $this->controllerPath . DS . Inflector::underscore($action) . ".ctp"); - $this->hr(); - $looksGood = $this->in('Look okay?', array('y','n'), 'y'); - if (low($looksGood) == 'y' || low($looksGood) == 'yes') { - $this->bake($action); - $this->_stop(); - } else { - $this->out('Bake Aborted.'); - } + $this->customAction(); } } /** @@ -247,7 +276,7 @@ class ViewTask extends Shell { $this->_stop(); } $controllerClassName = $this->controllerName . 'Controller'; - $controllerObj = & new $controllerClassName(); + $controllerObj =& new $controllerClassName(); $controllerObj->constructClasses(); $modelClass = $controllerObj->modelClass; $modelObj =& ClassRegistry::getObject($controllerObj->modelKey); @@ -277,6 +306,47 @@ class ViewTask extends Shell { return compact('modelClass', 'schema', 'primaryKey', 'displayField', 'singularVar', 'pluralVar', 'singularHumanName', 'pluralHumanName', 'fields','associations'); } +/** + * Bake a view file for each of the supplied actions + * + * @param array $actions Array of actions to make files for. + * @return void + **/ + function bakeActions($actions, $vars) { + foreach ($actions as $action) { + $content = $this->getContent($action, $vars); + $this->bake($action, $content); + } + } +/** + * handle creation of baking a custom action view file + * + * @return void + **/ + function customAction() { + $action = ''; + while ($action == '') { + $action = $this->in(__('Action Name? (use lowercase_underscored function name)', true)); + if ($action == '') { + $this->out(__('The action name you supplied was empty. Please try again.', true)); + } + } + $this->out(''); + $this->hr(); + $this->out(__('The following view will be created:', true)); + $this->hr(); + $this->out(sprintf(__('Controller Name: %s', true), $this->controllerName)); + $this->out(sprintf(__('Action Name: %s', true), $action)); + $this->out(sprintf(__('Path: %s', true), $this->params['app'] . DS . $this->controllerPath . DS . Inflector::underscore($action) . ".ctp")); + $this->hr(); + $looksGood = $this->in(__('Look okay?', true), array('y','n'), 'y'); + if (strtolower($looksGood) == 'y') { + $this->bake($action); + $this->_stop(); + } else { + $this->out(__('Bake Aborted.', true)); + } + } /** * Assembles and writes bakes the view file. * @@ -287,20 +357,14 @@ class ViewTask extends Shell { */ function bake($action, $content = '') { if ($content === true) { - $content = $this->getContent(); + $content = $this->getContent($action); } - $filename = $this->path . $this->controllerPath . DS . Inflector::underscore($action) . '.ctp'; - $Folder =& new Folder($this->path . $this->controllerPath, true); - $errors = $Folder->errors(); - if (empty($errors)) { - $path = $Folder->slashTerm($Folder->pwd()); - return $this->createFile($filename, $content); - } else { - foreach ($errors as $error) { - $this->err($error); - } + $path = $this->path; + if (isset($this->plugin)) { + $path = $this->_pluginPath($this->plugin) . 'views' . DS; } - return false; + $filename = $path . $this->controllerPath . DS . Inflector::underscore($action) . '.ctp'; + return $this->createFile($filename, $content); } /** * Builds content from template and variables @@ -318,30 +382,23 @@ class ViewTask extends Shell { $adminRoute = Configure::read('Routing.admin'); if (!empty($adminRoute) && strpos($template, $adminRoute) !== false) { - $template = str_replace($adminRoute.'_', '', $template); + $template = str_replace($adminRoute . '_', '', $template); } if (in_array($template, array('add', 'edit'))) { $action = $template; $template = 'form'; } - $loaded = false; - foreach ($this->Dispatch->shellPaths as $path) { - $templatePath = $path . 'templates' . DS . 'views' . DS .Inflector::underscore($template).'.ctp'; - if (file_exists($templatePath) && is_file($templatePath)) { - $loaded = true; - break; - } - } if (!$vars) { $vars = $this->__loadController(); } - if ($loaded) { - extract($vars); - ob_start(); - ob_implicit_flush(0); - include($templatePath); - $content = ob_get_clean(); - return $content; + + $this->Template->set('action', $action); + $this->Template->set('plugin', $this->plugin); + $this->Template->set($vars); + $output = $this->Template->generate('views', $template); + + if (!empty($output)) { + return $output; } $this->hr(); $this->err(sprintf(__('Template for %s could not be found', true), $template)); @@ -357,10 +414,25 @@ class ViewTask extends Shell { $this->out("Usage: cake bake view ..."); $this->hr(); $this->out('Commands:'); - $this->out("\n\tview \n\t\twill read the given controller for methods\n\t\tand bake corresponding views.\n\t\tIf var scaffold is found it will bake the scaffolded actions\n\t\t(index,view,add,edit)"); - $this->out("\n\tview \n\t\twill bake a template. core templates: (index, add, edit, view)"); - $this->out("\n\tview