ControllerTask.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. <?php
  2. /**
  3. * The ControllerTask handles creating and updating controller files.
  4. *
  5. * PHP 5
  6. *
  7. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  8. * Copyright 2005-2011, Cake Software Foundation, Inc.
  9. *
  10. * Licensed under The MIT License
  11. * Redistributions of files must retain the above copyright notice.
  12. *
  13. * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
  14. * @link http://cakephp.org CakePHP(tm) Project
  15. * @since CakePHP(tm) v 1.2
  16. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  17. */
  18. App::uses('BakeTask', 'Console/Command/Task');
  19. App::uses('AppModel', 'Model');
  20. /**
  21. * Task class for creating and updating controller files.
  22. *
  23. * @package Cake.Console.Command.Task
  24. */
  25. class ControllerTask extends BakeTask {
  26. /**
  27. * Tasks to be loaded by this Task
  28. *
  29. * @var array
  30. */
  31. public $tasks = array('Model', 'Test', 'Template', 'DbConfig', 'Project');
  32. /**
  33. * path to Controller directory
  34. *
  35. * @var array
  36. */
  37. public $path = null;
  38. /**
  39. * Override initialize
  40. *
  41. */
  42. public function initialize() {
  43. $this->path = current(App::path('Controller'));
  44. }
  45. /**
  46. * Execution method always used for tasks
  47. *
  48. */
  49. public function execute() {
  50. parent::execute();
  51. if (empty($this->args)) {
  52. return $this->_interactive();
  53. }
  54. if (isset($this->args[0])) {
  55. if (!isset($this->connection)) {
  56. $this->connection = 'default';
  57. }
  58. if (strtolower($this->args[0]) == 'all') {
  59. return $this->all();
  60. }
  61. $controller = $this->_controllerName($this->args[0]);
  62. $actions = '';
  63. if (!empty($this->params['public'])) {
  64. $this->out(__d('cake_console', 'Baking basic crud methods for ') . $controller);
  65. $actions .= $this->bakeActions($controller);
  66. }
  67. if (!empty($this->params['admin'])) {
  68. $admin = $this->Project->getPrefix();
  69. if ($admin) {
  70. $this->out(__d('cake_console', 'Adding %s methods', $admin));
  71. $actions .= "\n" . $this->bakeActions($controller, $admin);
  72. }
  73. }
  74. if (empty($actions)) {
  75. $actions = 'scaffold';
  76. }
  77. if ($this->bake($controller, $actions)) {
  78. if ($this->_checkUnitTest()) {
  79. $this->bakeTest($controller);
  80. }
  81. }
  82. }
  83. }
  84. /**
  85. * Bake All the controllers at once. Will only bake controllers for models that exist.
  86. *
  87. * @return void
  88. */
  89. public function all() {
  90. $this->interactive = false;
  91. $this->listAll($this->connection, false);
  92. ClassRegistry::config('Model', array('ds' => $this->connection));
  93. $unitTestExists = $this->_checkUnitTest();
  94. foreach ($this->__tables as $table) {
  95. $model = $this->_modelName($table);
  96. $controller = $this->_controllerName($model);
  97. App::uses($model, 'Model');
  98. if (class_exists($model)) {
  99. $actions = $this->bakeActions($controller);
  100. if ($this->bake($controller, $actions) && $unitTestExists) {
  101. $this->bakeTest($controller);
  102. }
  103. }
  104. }
  105. }
  106. /**
  107. * Interactive
  108. *
  109. * @return void
  110. */
  111. protected function _interactive() {
  112. $this->interactive = true;
  113. $this->hr();
  114. $this->out(__d('cake_console', "Bake Controller\nPath: %s", $this->path));
  115. $this->hr();
  116. if (empty($this->connection)) {
  117. $this->connection = $this->DbConfig->getConfig();
  118. }
  119. $controllerName = $this->getName();
  120. $this->hr();
  121. $this->out(__d('cake_console', 'Baking %sController', $controllerName));
  122. $this->hr();
  123. $helpers = $components = array();
  124. $actions = '';
  125. $wannaUseSession = 'y';
  126. $wannaBakeAdminCrud = 'n';
  127. $useDynamicScaffold = 'n';
  128. $wannaBakeCrud = 'y';
  129. $question[] = __d('cake_console', "Would you like to build your controller interactively?");
  130. if (file_exists($this->path . $controllerName .'Controller.php')) {
  131. $question[] = __d('cake_console', "Warning: Choosing no will overwrite the %sController.", $controllerName);
  132. }
  133. $doItInteractive = $this->in(implode("\n", $question), array('y','n'), 'y');
  134. if (strtolower($doItInteractive) == 'y') {
  135. $this->interactive = true;
  136. $useDynamicScaffold = $this->in(
  137. __d('cake_console', "Would you like to use dynamic scaffolding?"), array('y','n'), 'n'
  138. );
  139. if (strtolower($useDynamicScaffold) == 'y') {
  140. $wannaBakeCrud = 'n';
  141. $actions = 'scaffold';
  142. } else {
  143. list($wannaBakeCrud, $wannaBakeAdminCrud) = $this->_askAboutMethods();
  144. $helpers = $this->doHelpers();
  145. $components = $this->doComponents();
  146. $wannaUseSession = $this->in(
  147. __d('cake_console', "Would you like to use Session flash messages?"), array('y','n'), 'y'
  148. );
  149. }
  150. } else {
  151. list($wannaBakeCrud, $wannaBakeAdminCrud) = $this->_askAboutMethods();
  152. }
  153. if (strtolower($wannaBakeCrud) == 'y') {
  154. $actions = $this->bakeActions($controllerName, null, strtolower($wannaUseSession) == 'y');
  155. }
  156. if (strtolower($wannaBakeAdminCrud) == 'y') {
  157. $admin = $this->Project->getPrefix();
  158. $actions .= $this->bakeActions($controllerName, $admin, strtolower($wannaUseSession) == 'y');
  159. }
  160. $baked = false;
  161. if ($this->interactive === true) {
  162. $this->confirmController($controllerName, $useDynamicScaffold, $helpers, $components);
  163. $looksGood = $this->in(__d('cake_console', 'Look okay?'), array('y','n'), 'y');
  164. if (strtolower($looksGood) == 'y') {
  165. $baked = $this->bake($controllerName, $actions, $helpers, $components);
  166. if ($baked && $this->_checkUnitTest()) {
  167. $this->bakeTest($controllerName);
  168. }
  169. }
  170. } else {
  171. $baked = $this->bake($controllerName, $actions, $helpers, $components);
  172. if ($baked && $this->_checkUnitTest()) {
  173. $this->bakeTest($controllerName);
  174. }
  175. }
  176. return $baked;
  177. }
  178. /**
  179. * Confirm a to be baked controller with the user
  180. *
  181. * @return void
  182. */
  183. public function confirmController($controllerName, $useDynamicScaffold, $helpers, $components) {
  184. $this->out();
  185. $this->hr();
  186. $this->out(__d('cake_console', 'The following controller will be created:'));
  187. $this->hr();
  188. $this->out(__d('cake_console', "Controller Name:\n\t%s", $controllerName));
  189. if (strtolower($useDynamicScaffold) == 'y') {
  190. $this->out("var \$scaffold;");
  191. }
  192. $properties = array(
  193. 'helpers' => __d('cake_console', 'Helpers:'),
  194. 'components' => __d('cake_console', 'Components:'),
  195. );
  196. foreach ($properties as $var => $title) {
  197. if (count($$var)) {
  198. $output = '';
  199. $length = count($$var);
  200. foreach ($$var as $i => $propElement) {
  201. if ($i != $length -1) {
  202. $output .= ucfirst($propElement) . ', ';
  203. } else {
  204. $output .= ucfirst($propElement);
  205. }
  206. }
  207. $this->out($title . "\n\t" . $output);
  208. }
  209. }
  210. $this->hr();
  211. }
  212. /**
  213. * Interact with the user and ask about which methods (admin or regular they want to bake)
  214. *
  215. * @return array Array containing (bakeRegular, bakeAdmin) answers
  216. */
  217. protected function _askAboutMethods() {
  218. $wannaBakeCrud = $this->in(
  219. __d('cake_console', "Would you like to create some basic class methods \n(index(), add(), view(), edit())?"),
  220. array('y','n'), 'n'
  221. );
  222. $wannaBakeAdminCrud = $this->in(
  223. __d('cake_console', "Would you like to create the basic class methods for admin routing?"),
  224. array('y','n'), 'n'
  225. );
  226. return array($wannaBakeCrud, $wannaBakeAdminCrud);
  227. }
  228. /**
  229. * Bake scaffold actions
  230. *
  231. * @param string $controllerName Controller name
  232. * @param string $admin Admin route to use
  233. * @param boolean $wannaUseSession Set to true to use sessions, false otherwise
  234. * @return string Baked actions
  235. */
  236. public function bakeActions($controllerName, $admin = null, $wannaUseSession = true) {
  237. $currentModelName = $modelImport = $this->_modelName($controllerName);
  238. $plugin = $this->plugin;
  239. if ($plugin) {
  240. $plugin .= '.';
  241. }
  242. App::uses($modelImport, $plugin . 'Model');
  243. if (!class_exists($modelImport)) {
  244. $this->err(__d('cake_console', 'You must have a model for this class to build basic methods. Please try again.'));
  245. $this->_stop();
  246. }
  247. $modelObj = ClassRegistry::init($currentModelName);
  248. $controllerPath = $this->_controllerPath($controllerName);
  249. $pluralName = $this->_pluralName($currentModelName);
  250. $singularName = Inflector::variable($currentModelName);
  251. $singularHumanName = $this->_singularHumanName($controllerName);
  252. $pluralHumanName = $this->_pluralName($controllerName);
  253. $displayField = $modelObj->displayField;
  254. $primaryKey = $modelObj->primaryKey;
  255. $this->Template->set(compact('plugin', 'admin', 'controllerPath', 'pluralName', 'singularName',
  256. 'singularHumanName', 'pluralHumanName', 'modelObj', 'wannaUseSession', 'currentModelName',
  257. 'displayField', 'primaryKey'
  258. ));
  259. $actions = $this->Template->generate('actions', 'controller_actions');
  260. return $actions;
  261. }
  262. /**
  263. * Assembles and writes a Controller file
  264. *
  265. * @param string $controllerName Controller name
  266. * @param string $actions Actions to add, or set the whole controller to use $scaffold (set $actions to 'scaffold')
  267. * @param array $helpers Helpers to use in controller
  268. * @param array $components Components to use in controller
  269. * @param array $uses Models to use in controller
  270. * @return string Baked controller
  271. */
  272. public function bake($controllerName, $actions = '', $helpers = null, $components = null) {
  273. $this->out("\n" . __d('cake_console', 'Baking controller class for %s...', $controllerName), 1, Shell::QUIET);
  274. $isScaffold = ($actions === 'scaffold') ? true : false;
  275. $this->Template->set('plugin', $this->plugin);
  276. $this->Template->set(compact('controllerName', 'actions', 'helpers', 'components', 'isScaffold'));
  277. $contents = $this->Template->generate('classes', 'controller');
  278. $path = $this->getPath();
  279. $filename = $path . $this->_controllerName($controllerName) . 'Controller.php';
  280. if ($this->createFile($filename, $contents)) {
  281. return $contents;
  282. }
  283. return false;
  284. }
  285. /**
  286. * Assembles and writes a unit test file
  287. *
  288. * @param string $className Controller class name
  289. * @return string Baked test
  290. */
  291. public function bakeTest($className) {
  292. $this->Test->plugin = $this->plugin;
  293. $this->Test->connection = $this->connection;
  294. $this->Test->interactive = $this->interactive;
  295. return $this->Test->bake('Controller', $className);
  296. }
  297. /**
  298. * Interact with the user and get a list of additional helpers
  299. *
  300. * @return array Helpers that the user wants to use.
  301. */
  302. public function doHelpers() {
  303. return $this->_doPropertyChoices(
  304. __d('cake_console', "Would you like this controller to use other helpers\nbesides HtmlHelper and FormHelper?"),
  305. __d('cake_console', "Please provide a comma separated list of the other\nhelper names you'd like to use.\nExample: 'Ajax, Javascript, Time'")
  306. );
  307. }
  308. /**
  309. * Interact with the user and get a list of additional components
  310. *
  311. * @return array Components the user wants to use.
  312. */
  313. public function doComponents() {
  314. return $this->_doPropertyChoices(
  315. __d('cake_console', "Would you like this controller to use any components?"),
  316. __d('cake_console', "Please provide a comma separated list of the component names you'd like to use.\nExample: 'Acl, Security, RequestHandler'")
  317. );
  318. }
  319. /**
  320. * Common code for property choice handling.
  321. *
  322. * @param string $prompt A yes/no question to precede the list
  323. * @param sting $example A question for a comma separated list, with examples.
  324. * @return array Array of values for property.
  325. */
  326. protected function _doPropertyChoices($prompt, $example) {
  327. $proceed = $this->in($prompt, array('y','n'), 'n');
  328. $property = array();
  329. if (strtolower($proceed) == 'y') {
  330. $propertyList = $this->in($example);
  331. $propertyListTrimmed = str_replace(' ', '', $propertyList);
  332. $property = explode(',', $propertyListTrimmed);
  333. }
  334. return array_filter($property);
  335. }
  336. /**
  337. * Outputs and gets the list of possible controllers from database
  338. *
  339. * @param string $useDbConfig Database configuration name
  340. * @param boolean $interactive Whether you are using listAll interactively and want options output.
  341. * @return array Set of controllers
  342. */
  343. public function listAll($useDbConfig = null) {
  344. if (is_null($useDbConfig)) {
  345. $useDbConfig = $this->connection;
  346. }
  347. $this->__tables = $this->Model->getAllTables($useDbConfig);
  348. if ($this->interactive == true) {
  349. $this->out(__d('cake_console', 'Possible Controllers based on your current database:'));
  350. $this->_controllerNames = array();
  351. $count = count($this->__tables);
  352. for ($i = 0; $i < $count; $i++) {
  353. $this->_controllerNames[] = $this->_controllerName($this->_modelName($this->__tables[$i]));
  354. $this->out($i + 1 . ". " . $this->_controllerNames[$i]);
  355. }
  356. return $this->_controllerNames;
  357. }
  358. return $this->__tables;
  359. }
  360. /**
  361. * Forces the user to specify the controller he wants to bake, and returns the selected controller name.
  362. *
  363. * @param string $useDbConfig Connection name to get a controller name for.
  364. * @return string Controller name
  365. */
  366. public function getName($useDbConfig = null) {
  367. $controllers = $this->listAll($useDbConfig);
  368. $enteredController = '';
  369. while ($enteredController == '') {
  370. $enteredController = $this->in(__d('cake_console', "Enter a number from the list above,\ntype in the name of another controller, or 'q' to exit"), null, 'q');
  371. if ($enteredController === 'q') {
  372. $this->out(__d('cake_console', 'Exit'));
  373. return $this->_stop();
  374. }
  375. if ($enteredController == '' || intval($enteredController) > count($controllers)) {
  376. $this->err(__d('cake_console', "The Controller name you supplied was empty,\nor the number you selected was not an option. Please try again."));
  377. $enteredController = '';
  378. }
  379. }
  380. if (intval($enteredController) > 0 && intval($enteredController) <= count($controllers) ) {
  381. $controllerName = $controllers[intval($enteredController) - 1];
  382. } else {
  383. $controllerName = Inflector::camelize($enteredController);
  384. }
  385. return $controllerName;
  386. }
  387. /**
  388. * get the option parser.
  389. *
  390. * @return void
  391. */
  392. public function getOptionParser() {
  393. $parser = parent::getOptionParser();
  394. return $parser->description(
  395. __d('cake_console', 'Bake a controller for a model. Using options you can bake public, admin or both.')
  396. )->addArgument('name', array(
  397. 'help' => __d('cake_console', 'Name of the controller to bake. Can use Plugin.name to bake controllers into plugins.')
  398. ))->addOption('public', array(
  399. 'help' => __d('cake_console', 'Bake a controller with basic crud actions (index, view, add, edit, delete).'),
  400. 'boolean' => true
  401. ))->addOption('admin', array(
  402. 'help' => __d('cake_console', 'Bake a controller with crud actions for one of the Routing.prefixes.'),
  403. 'boolean' => true
  404. ))->addOption('plugin', array(
  405. 'short' => 'p',
  406. 'help' => __d('cake_console', 'Plugin to bake the controller into.')
  407. ))->addOption('connection', array(
  408. 'short' => 'c',
  409. 'help' => __d('cake_console', 'The connection the controller\'s model is on.')
  410. ))->addSubcommand('all', array(
  411. 'help' => __d('cake_console', 'Bake all controllers with CRUD methods.')
  412. ))->epilog(__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.'));
  413. }
  414. /**
  415. * Displays help contents
  416. *
  417. */
  418. public function help() {
  419. $this->hr();
  420. $this->out("Usage: cake bake controller <arg1> <arg2>...");
  421. $this->hr();
  422. $this->out('Arguments:');
  423. $this->out();
  424. $this->out("<name>");
  425. $this->out("\tName of the controller to bake. Can use Plugin.name");
  426. $this->out("\tas a shortcut for plugin baking.");
  427. $this->out();
  428. $this->out('Params:');
  429. $this->out();
  430. $this->out('-connection <config>');
  431. $this->out("\tset db config <config>. uses 'default' if none is specified");
  432. $this->out();
  433. $this->out('Commands:');
  434. $this->out();
  435. $this->out("controller <name>");
  436. $this->out("\tbakes controller with var \$scaffold");
  437. $this->out();
  438. $this->out("controller <name> public");
  439. $this->out("\tbakes controller with basic crud actions");
  440. $this->out("\t(index, view, add, edit, delete)");
  441. $this->out();
  442. $this->out("controller <name> admin");
  443. $this->out("\tbakes a controller with basic crud actions for one of the");
  444. $this->out("\tConfigure::read('Routing.prefixes') methods.");
  445. $this->out();
  446. $this->out("controller <name> public admin");
  447. $this->out("\tbakes a controller with basic crud actions for one");
  448. $this->out("\tConfigure::read('Routing.prefixes') and non admin methods.");
  449. $this->out("\t(index, view, add, edit, delete,");
  450. $this->out("\tadmin_index, admin_view, admin_edit, admin_add, admin_delete)");
  451. $this->out();
  452. $this->out("controller all");
  453. $this->out("\tbakes all controllers with CRUD methods.");
  454. $this->out();
  455. $this->_stop();
  456. }
  457. }