pictcode / lib / Cake / Console / Command / Task / ViewTask.php @ 0b1b8047
履歴 | 表示 | アノテート | ダウンロード (14.291 KB)
1 | 635eef61 | spyder1211 | <?php
|
---|---|---|---|
2 | /**
|
||
3 | * The View Tasks handles creating and updating view files.
|
||
4 | *
|
||
5 | * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||
6 | * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||
7 | *
|
||
8 | * Licensed under The MIT License
|
||
9 | * For full copyright and license information, please see the LICENSE.txt
|
||
10 | * Redistributions of files must retain the above copyright notice.
|
||
11 | *
|
||
12 | * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||
13 | * @link http://cakephp.org CakePHP(tm) Project
|
||
14 | * @since CakePHP(tm) v 1.2
|
||
15 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||
16 | */
|
||
17 | |||
18 | App::uses('AppShell', 'Console/Command'); |
||
19 | App::uses('Controller', 'Controller'); |
||
20 | App::uses('BakeTask', 'Console/Command/Task'); |
||
21 | |||
22 | /**
|
||
23 | * Task class for creating and updating view files.
|
||
24 | *
|
||
25 | * @package Cake.Console.Command.Task
|
||
26 | */
|
||
27 | class ViewTask extends BakeTask { |
||
28 | |||
29 | /**
|
||
30 | * Tasks to be loaded by this Task
|
||
31 | *
|
||
32 | * @var array
|
||
33 | */
|
||
34 | public $tasks = array('Project', 'Controller', 'DbConfig', 'Template'); |
||
35 | |||
36 | /**
|
||
37 | * path to View directory
|
||
38 | *
|
||
39 | * @var array
|
||
40 | */
|
||
41 | public $path = null; |
||
42 | |||
43 | /**
|
||
44 | * Name of the controller being used
|
||
45 | *
|
||
46 | * @var string
|
||
47 | */
|
||
48 | public $controllerName = null; |
||
49 | |||
50 | /**
|
||
51 | * The template file to use
|
||
52 | *
|
||
53 | * @var string
|
||
54 | */
|
||
55 | public $template = null; |
||
56 | |||
57 | /**
|
||
58 | * Actions to use for scaffolding
|
||
59 | *
|
||
60 | * @var array
|
||
61 | */
|
||
62 | public $scaffoldActions = array('index', 'view', 'add', 'edit'); |
||
63 | |||
64 | /**
|
||
65 | * An array of action names that don't require templates. These
|
||
66 | * actions will not emit errors when doing bakeActions()
|
||
67 | *
|
||
68 | * @var array
|
||
69 | */
|
||
70 | public $noTemplateActions = array('delete'); |
||
71 | |||
72 | /**
|
||
73 | * Override initialize
|
||
74 | *
|
||
75 | * @return void
|
||
76 | */
|
||
77 | public function initialize() { |
||
78 | $this->path = current(App::path('View')); |
||
79 | } |
||
80 | |||
81 | /**
|
||
82 | * Execution method always used for tasks
|
||
83 | *
|
||
84 | * @return mixed
|
||
85 | */
|
||
86 | public function execute() { |
||
87 | parent::execute();
|
||
88 | if (empty($this->args)) { |
||
89 | $this->_interactive();
|
||
90 | } |
||
91 | if (empty($this->args[0])) { |
||
92 | return null; |
||
93 | } |
||
94 | if (!isset($this->connection)) { |
||
95 | $this->connection = 'default'; |
||
96 | } |
||
97 | $action = null; |
||
98 | $this->controllerName = $this->_controllerName($this->args[0]); |
||
99 | |||
100 | $this->Project->interactive = false; |
||
101 | if (strtolower($this->args[0]) === 'all') { |
||
102 | return $this->all(); |
||
103 | } |
||
104 | |||
105 | if (isset($this->args[1])) { |
||
106 | $this->template = $this->args[1]; |
||
107 | } |
||
108 | if (isset($this->args[2])) { |
||
109 | $action = $this->args[2]; |
||
110 | } |
||
111 | if (!$action) { |
||
112 | $action = $this->template; |
||
113 | } |
||
114 | if ($action) { |
||
115 | return $this->bake($action, true); |
||
116 | } |
||
117 | |||
118 | $vars = $this->_loadController(); |
||
119 | $methods = $this->_methodsToBake(); |
||
120 | |||
121 | foreach ($methods as $method) { |
||
122 | $content = $this->getContent($method, $vars); |
||
123 | if ($content) { |
||
124 | $this->bake($method, $content); |
||
125 | } |
||
126 | } |
||
127 | } |
||
128 | |||
129 | /**
|
||
130 | * Get a list of actions that can / should have views baked for them.
|
||
131 | *
|
||
132 | * @return array Array of action names that should be baked
|
||
133 | */
|
||
134 | protected function _methodsToBake() { |
||
135 | $methods = array_diff( |
||
136 | array_map('strtolower', get_class_methods($this->controllerName . 'Controller')), |
||
137 | array_map('strtolower', get_class_methods('AppController')) |
||
138 | ); |
||
139 | $scaffoldActions = false; |
||
140 | if (empty($methods)) { |
||
141 | $scaffoldActions = true; |
||
142 | $methods = $this->scaffoldActions; |
||
143 | } |
||
144 | $adminRoute = $this->Project->getPrefix(); |
||
145 | foreach ($methods as $i => $method) { |
||
146 | if ($adminRoute && !empty($this->params['admin'])) { |
||
147 | if ($scaffoldActions) { |
||
148 | $methods[$i] = $adminRoute . $method; |
||
149 | continue;
|
||
150 | } elseif (strpos($method, $adminRoute) === false) { |
||
151 | unset($methods[$i]); |
||
152 | } |
||
153 | } |
||
154 | if ($method[0] === '_' || $method === strtolower($this->controllerName . 'Controller')) { |
||
155 | unset($methods[$i]); |
||
156 | } |
||
157 | } |
||
158 | return $methods; |
||
159 | } |
||
160 | |||
161 | /**
|
||
162 | * Bake All views for All controllers.
|
||
163 | *
|
||
164 | * @return void
|
||
165 | */
|
||
166 | public function all() { |
||
167 | $this->Controller->interactive = false; |
||
168 | $tables = $this->Controller->listAll($this->connection, false); |
||
169 | |||
170 | $actions = null; |
||
171 | if (isset($this->args[1])) { |
||
172 | $actions = array($this->args[1]); |
||
173 | } |
||
174 | $this->interactive = false; |
||
175 | foreach ($tables as $table) { |
||
176 | $model = $this->_modelName($table); |
||
177 | $this->controllerName = $this->_controllerName($model); |
||
178 | App::uses($model, 'Model'); |
||
179 | if (class_exists($model)) { |
||
180 | $vars = $this->_loadController(); |
||
181 | if (!$actions) { |
||
182 | $actions = $this->_methodsToBake(); |
||
183 | } |
||
184 | $this->bakeActions($actions, $vars); |
||
185 | $actions = null; |
||
186 | } |
||
187 | } |
||
188 | } |
||
189 | |||
190 | /**
|
||
191 | * Handles interactive baking
|
||
192 | *
|
||
193 | * @return void
|
||
194 | */
|
||
195 | protected function _interactive() { |
||
196 | $this->hr();
|
||
197 | $this->out(sprintf("Bake View\nPath: %s", $this->getPath())); |
||
198 | $this->hr();
|
||
199 | |||
200 | $this->DbConfig->interactive = $this->Controller->interactive = $this->interactive = true; |
||
201 | |||
202 | if (empty($this->connection)) { |
||
203 | $this->connection = $this->DbConfig->getConfig(); |
||
204 | } |
||
205 | |||
206 | $this->Controller->connection = $this->connection; |
||
207 | $this->controllerName = $this->Controller->getName(); |
||
208 | |||
209 | $prompt = __d('cake_console', "Would you like bake to build your views interactively?\nWarning: Choosing no will overwrite %s views if they exist.", $this->controllerName); |
||
210 | $interactive = $this->in($prompt, array('y', 'n'), 'n'); |
||
211 | |||
212 | if (strtolower($interactive) === 'n') { |
||
213 | $this->interactive = false; |
||
214 | } |
||
215 | |||
216 | $prompt = __d('cake_console', "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)."); |
||
217 | $wannaDoScaffold = $this->in($prompt, array('y', 'n'), 'y'); |
||
218 | |||
219 | $wannaDoAdmin = $this->in(__d('cake_console', "Would you like to create the views for admin routing?"), array('y', 'n'), 'n'); |
||
220 | |||
221 | if (strtolower($wannaDoScaffold) === 'y' || strtolower($wannaDoAdmin) === 'y') { |
||
222 | $vars = $this->_loadController(); |
||
223 | if (strtolower($wannaDoScaffold) === 'y') { |
||
224 | $actions = $this->scaffoldActions; |
||
225 | $this->bakeActions($actions, $vars); |
||
226 | } |
||
227 | if (strtolower($wannaDoAdmin) === 'y') { |
||
228 | $admin = $this->Project->getPrefix(); |
||
229 | $regularActions = $this->scaffoldActions; |
||
230 | $adminActions = array(); |
||
231 | foreach ($regularActions as $action) { |
||
232 | $adminActions[] = $admin . $action; |
||
233 | } |
||
234 | $this->bakeActions($adminActions, $vars); |
||
235 | } |
||
236 | $this->hr();
|
||
237 | $this->out();
|
||
238 | $this->out(__d('cake_console', "View Scaffolding Complete.\n")); |
||
239 | } else {
|
||
240 | $this->customAction();
|
||
241 | } |
||
242 | } |
||
243 | |||
244 | /**
|
||
245 | * Loads Controller and sets variables for the template
|
||
246 | * Available template variables
|
||
247 | * 'modelClass', 'primaryKey', 'displayField', 'singularVar', 'pluralVar',
|
||
248 | * 'singularHumanName', 'pluralHumanName', 'fields', 'foreignKeys',
|
||
249 | * 'belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany'
|
||
250 | *
|
||
251 | * @return array Returns a variables to be made available to a view template
|
||
252 | */
|
||
253 | protected function _loadController() { |
||
254 | if (!$this->controllerName) { |
||
255 | $this->err(__d('cake_console', 'Controller not found')); |
||
256 | } |
||
257 | |||
258 | $plugin = null; |
||
259 | if ($this->plugin) { |
||
260 | $plugin = $this->plugin . '.'; |
||
261 | } |
||
262 | |||
263 | $controllerClassName = $this->controllerName . 'Controller'; |
||
264 | App::uses($controllerClassName, $plugin . 'Controller'); |
||
265 | if (!class_exists($controllerClassName)) { |
||
266 | $file = $controllerClassName . '.php'; |
||
267 | $this->err(__d('cake_console', "The file '%s' could not be found.\nIn order to bake a view, you'll need to first create the controller.", $file)); |
||
268 | return $this->_stop(); |
||
269 | } |
||
270 | $controllerObj = new $controllerClassName(); |
||
271 | $controllerObj->plugin = $this->plugin; |
||
272 | $controllerObj->constructClasses();
|
||
273 | $modelClass = $controllerObj->modelClass; |
||
274 | $modelObj = $controllerObj->{$controllerObj->modelClass}; |
||
275 | |||
276 | if ($modelObj) { |
||
277 | $primaryKey = $modelObj->primaryKey; |
||
278 | $displayField = $modelObj->displayField; |
||
279 | $singularVar = Inflector::variable($modelClass); |
||
280 | $singularHumanName = $this->_singularHumanName($this->controllerName); |
||
281 | $schema = $modelObj->schema(true); |
||
282 | $fields = array_keys($schema); |
||
283 | $associations = $this->_associations($modelObj); |
||
284 | } else {
|
||
285 | $primaryKey = $displayField = null; |
||
286 | $singularVar = Inflector::variable(Inflector::singularize($this->controllerName)); |
||
287 | $singularHumanName = $this->_singularHumanName($this->controllerName); |
||
288 | $fields = $schema = $associations = array(); |
||
289 | } |
||
290 | $pluralVar = Inflector::variable($this->controllerName); |
||
291 | $pluralHumanName = $this->_pluralHumanName($this->controllerName); |
||
292 | |||
293 | return compact('modelClass', 'schema', 'primaryKey', 'displayField', 'singularVar', 'pluralVar', |
||
294 | 'singularHumanName', 'pluralHumanName', 'fields', 'associations'); |
||
295 | } |
||
296 | |||
297 | /**
|
||
298 | * Bake a view file for each of the supplied actions
|
||
299 | *
|
||
300 | * @param array $actions Array of actions to make files for.
|
||
301 | * @param array $vars The template variables.
|
||
302 | * @return void
|
||
303 | */
|
||
304 | public function bakeActions($actions, $vars) { |
||
305 | foreach ($actions as $action) { |
||
306 | $content = $this->getContent($action, $vars); |
||
307 | $this->bake($action, $content); |
||
308 | } |
||
309 | } |
||
310 | |||
311 | /**
|
||
312 | * handle creation of baking a custom action view file
|
||
313 | *
|
||
314 | * @return void
|
||
315 | */
|
||
316 | public function customAction() { |
||
317 | $action = ''; |
||
318 | while (!$action) { |
||
319 | $action = $this->in(__d('cake_console', 'Action Name? (use lowercase_underscored function name)')); |
||
320 | if (!$action) { |
||
321 | $this->out(__d('cake_console', 'The action name you supplied was empty. Please try again.')); |
||
322 | } |
||
323 | } |
||
324 | $this->out();
|
||
325 | $this->hr();
|
||
326 | $this->out(__d('cake_console', 'The following view will be created:')); |
||
327 | $this->hr();
|
||
328 | $this->out(__d('cake_console', 'Controller Name: %s', $this->controllerName)); |
||
329 | $this->out(__d('cake_console', 'Action Name: %s', $action)); |
||
330 | $this->out(__d('cake_console', 'Path: %s', $this->getPath() . $this->controllerName . DS . Inflector::underscore($action) . ".ctp")); |
||
331 | $this->hr();
|
||
332 | $looksGood = $this->in(__d('cake_console', 'Look okay?'), array('y', 'n'), 'y'); |
||
333 | if (strtolower($looksGood) === 'y') { |
||
334 | $this->bake($action, ' '); |
||
335 | return $this->_stop(); |
||
336 | } |
||
337 | $this->out(__d('cake_console', 'Bake Aborted.')); |
||
338 | } |
||
339 | |||
340 | /**
|
||
341 | * Assembles and writes bakes the view file.
|
||
342 | *
|
||
343 | * @param string $action Action to bake
|
||
344 | * @param string $content Content to write
|
||
345 | * @return bool Success
|
||
346 | */
|
||
347 | public function bake($action, $content = '') { |
||
348 | if ($content === true) { |
||
349 | $content = $this->getContent($action); |
||
350 | } |
||
351 | if (empty($content)) { |
||
352 | return false; |
||
353 | } |
||
354 | $this->out("\n" . __d('cake_console', 'Baking `%s` view file...', $action), 1, Shell::QUIET); |
||
355 | $path = $this->getPath(); |
||
356 | $filename = $path . $this->controllerName . DS . Inflector::underscore($action) . '.ctp'; |
||
357 | return $this->createFile($filename, $content); |
||
358 | } |
||
359 | |||
360 | /**
|
||
361 | * Builds content from template and variables
|
||
362 | *
|
||
363 | * @param string $action name to generate content to
|
||
364 | * @param array $vars passed for use in templates
|
||
365 | * @return string content from template
|
||
366 | */
|
||
367 | public function getContent($action, $vars = null) { |
||
368 | if (!$vars) { |
||
369 | $vars = $this->_loadController(); |
||
370 | } |
||
371 | |||
372 | $this->Template->set('action', $action); |
||
373 | $this->Template->set('plugin', $this->plugin); |
||
374 | $this->Template->set($vars); |
||
375 | $template = $this->getTemplate($action); |
||
376 | if ($template) { |
||
377 | return $this->Template->generate('views', $template); |
||
378 | } |
||
379 | return false; |
||
380 | } |
||
381 | |||
382 | /**
|
||
383 | * Gets the template name based on the action name
|
||
384 | *
|
||
385 | * @param string $action name
|
||
386 | * @return string template name
|
||
387 | */
|
||
388 | public function getTemplate($action) { |
||
389 | if ($action != $this->template && in_array($action, $this->noTemplateActions)) { |
||
390 | return false; |
||
391 | } |
||
392 | if (!empty($this->template) && $action != $this->template) { |
||
393 | return $this->template; |
||
394 | } |
||
395 | $themePath = $this->Template->getThemePath(); |
||
396 | if (file_exists($themePath . 'views' . DS . $action . '.ctp')) { |
||
397 | return $action; |
||
398 | } |
||
399 | $template = $action; |
||
400 | $prefixes = Configure::read('Routing.prefixes'); |
||
401 | foreach ((array)$prefixes as $prefix) { |
||
402 | if (strpos($template, $prefix) !== false) { |
||
403 | $template = str_replace($prefix . '_', '', $template); |
||
404 | } |
||
405 | } |
||
406 | if (in_array($template, array('add', 'edit'))) { |
||
407 | $template = 'form'; |
||
408 | } elseif (preg_match('@(_add|_edit)$@', $template)) { |
||
409 | $template = str_replace(array('_add', '_edit'), '_form', $template); |
||
410 | } |
||
411 | return $template; |
||
412 | } |
||
413 | |||
414 | /**
|
||
415 | * Gets the option parser instance and configures it.
|
||
416 | *
|
||
417 | * @return ConsoleOptionParser
|
||
418 | */
|
||
419 | public function getOptionParser() { |
||
420 | $parser = parent::getOptionParser(); |
||
421 | |||
422 | $parser->description(
|
||
423 | __d('cake_console', 'Bake views for a controller, using built-in or custom templates.') |
||
424 | )->addArgument('controller', array( |
||
425 | 'help' => __d('cake_console', 'Name of the controller views to bake. Can be Plugin.name as a shortcut for plugin baking.') |
||
426 | ))->addArgument('action', array( |
||
427 | 'help' => __d('cake_console', "Will bake a single action's file. core templates are (index, add, edit, view)") |
||
428 | ))->addArgument('alias', array( |
||
429 | 'help' => __d('cake_console', 'Will bake the template in <action> but create the filename after <alias>.') |
||
430 | ))->addOption('plugin', array( |
||
431 | 'short' => 'p', |
||
432 | 'help' => __d('cake_console', 'Plugin to bake the view into.') |
||
433 | ))->addOption('admin', array( |
||
434 | 'help' => __d('cake_console', 'Set to only bake views for a prefix in Routing.prefixes'), |
||
435 | 'boolean' => true |
||
436 | ))->addOption('theme', array( |
||
437 | 'short' => 't', |
||
438 | 'help' => __d('cake_console', 'Theme to use when baking code.') |
||
439 | ))->addOption('connection', array( |
||
440 | 'short' => 'c', |
||
441 | 'help' => __d('cake_console', 'The connection the connected model is on.') |
||
442 | ))->addOption('force', array( |
||
443 | 'short' => 'f', |
||
444 | 'help' => __d('cake_console', 'Force overwriting existing files without prompting.') |
||
445 | ))->addSubcommand('all', array( |
||
446 | 'help' => __d('cake_console', 'Bake all CRUD action views for all controllers. Requires models and controllers to exist.') |
||
447 | ))->epilog( |
||
448 | __d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.') |
||
449 | ); |
||
450 | |||
451 | return $parser; |
||
452 | } |
||
453 | |||
454 | /**
|
||
455 | * Returns associations for controllers models.
|
||
456 | *
|
||
457 | * @param Model $model The Model instance.
|
||
458 | * @return array associations
|
||
459 | */
|
||
460 | protected function _associations(Model $model) { |
||
461 | $keys = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany'); |
||
462 | $associations = array(); |
||
463 | |||
464 | foreach ($keys as $type) { |
||
465 | foreach ($model->{$type} as $assocKey => $assocData) { |
||
466 | list(, $modelClass) = pluginSplit($assocData['className']); |
||
467 | $associations[$type][$assocKey]['primaryKey'] = $model->{$assocKey}->primaryKey; |
||
468 | $associations[$type][$assocKey]['displayField'] = $model->{$assocKey}->displayField; |
||
469 | $associations[$type][$assocKey]['foreignKey'] = $assocData['foreignKey']; |
||
470 | $associations[$type][$assocKey]['controller'] = Inflector::pluralize(Inflector::underscore($modelClass)); |
||
471 | $associations[$type][$assocKey]['fields'] = array_keys($model->{$assocKey}->schema(true)); |
||
472 | } |
||
473 | } |
||
474 | return $associations; |
||
475 | } |
||
476 | |||
477 | } |