Initial commit: apps.supersam24.ru
This commit is contained in:
commit
e955e1da36
6566 files changed
+1271095
No files matched your search
+332
@@ -0,0 +1,332 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 0.1.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Bake\Shell;
|
||||
|
||||
use Bake\Utility\CommonOptionsTrait;
|
||||
use Cake\Cache\Cache;
|
||||
use Cake\Console\Shell;
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Core\ConventionsTrait;
|
||||
use Cake\Core\Plugin;
|
||||
use Cake\Datasource\ConnectionManager;
|
||||
use Cake\Utility\Inflector;
|
||||
|
||||
/**
|
||||
* Command-line code generation utility to automate programmer chores.
|
||||
*
|
||||
* Bake is CakePHP's code generation script, which can help you kickstart
|
||||
* application development by writing fully functional skeleton controllers,
|
||||
* models, and templates. Going further, Bake can also write Unit Tests for you.
|
||||
*
|
||||
* @link https://book.cakephp.org/3.0/en/bake/usage.html
|
||||
*
|
||||
* @property \Bake\Shell\Task\ModelTask $Model
|
||||
*/
|
||||
class BakeShell extends Shell
|
||||
{
|
||||
use CommonOptionsTrait;
|
||||
use ConventionsTrait;
|
||||
|
||||
/**
|
||||
* The connection being used.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $connection = 'default';
|
||||
|
||||
/**
|
||||
* Assign $this->connection to the active task if a connection param is set.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function startup()
|
||||
{
|
||||
parent::startup();
|
||||
Configure::write('debug', true);
|
||||
Cache::disable();
|
||||
// Loading WyriHaximus/TwigView Plugin through the Plugin::load() for backward compatibility.
|
||||
if (!Plugin::isLoaded('WyriHaximus/TwigView')) {
|
||||
Plugin::load('WyriHaximus/TwigView', ['bootstrap' => true]);
|
||||
}
|
||||
|
||||
$task = $this->_camelize($this->command);
|
||||
|
||||
if (isset($this->{$task}) && !in_array($task, ['Project'])) {
|
||||
if (isset($this->params['connection'])) {
|
||||
$this->{$task}->connection = $this->params['connection'];
|
||||
}
|
||||
if (isset($this->params['tablePrefix'])) {
|
||||
$this->{$task}->tablePrefix = $this->params['tablePrefix'];
|
||||
}
|
||||
}
|
||||
if (isset($this->params['connection'])) {
|
||||
$this->connection = $this->params['connection'];
|
||||
}
|
||||
|
||||
if ($this->params['quiet']) {
|
||||
$this->interactive = false;
|
||||
if (isset($this->{$task}) && !in_array($task, ['Project'])) {
|
||||
$this->{$task}->interactive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override main() to handle action
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
if ($this->args && $this->args[0] === 'view') {
|
||||
$this->out('<error>The view command has been renamed.</error>');
|
||||
$this->out('To create template files, please use the template command:', 2);
|
||||
$args = $this->args;
|
||||
array_shift($args);
|
||||
$args = implode($args, ' ');
|
||||
$this->out(sprintf(' <info>`bin/cake bake template %s`</info>', $args), 2);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$connections = ConnectionManager::configured();
|
||||
if (empty($connections)) {
|
||||
$this->out('Your database configuration was not found.');
|
||||
$this->out('Add your database connection information to config/app.php.');
|
||||
|
||||
return false;
|
||||
}
|
||||
$this->out('The following commands can be used to generate skeleton code for your application.', 2);
|
||||
$this->out('<info>Available bake commands:</info>', 2);
|
||||
$this->out('- all');
|
||||
$names = [];
|
||||
foreach ($this->tasks as $task) {
|
||||
list(, $name) = pluginSplit($task);
|
||||
$names[] = Inflector::underscore($name);
|
||||
}
|
||||
sort($names);
|
||||
foreach ($names as $name) {
|
||||
$this->out('- ' . $name);
|
||||
}
|
||||
$this->out('');
|
||||
$this->out('By using <info>`cake bake [name]`</info> you can invoke a specific bake task.');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate the tasks bake will use.
|
||||
*
|
||||
* Scans the following paths for tasks that are subclasses of
|
||||
* Cake\Shell\Task\BakeTask:
|
||||
*
|
||||
* - Cake/Shell/Task/
|
||||
* - Shell/Task for each loaded plugin
|
||||
* - App/Shell/Task/
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function loadTasks()
|
||||
{
|
||||
$tasks = [];
|
||||
|
||||
foreach (Plugin::loaded() as $plugin) {
|
||||
$tasks = $this->_findTasks(
|
||||
$tasks,
|
||||
Plugin::classPath($plugin),
|
||||
str_replace('/', '\\', $plugin),
|
||||
$plugin
|
||||
);
|
||||
}
|
||||
$tasks = $this->_findTasks($tasks, APP, Configure::read('App.namespace'));
|
||||
|
||||
$this->tasks = array_values($tasks);
|
||||
|
||||
return parent::loadTasks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Append matching tasks in $path to the $tasks array.
|
||||
*
|
||||
* @param array $tasks The task list to modify and return.
|
||||
* @param string $path The base path to look in.
|
||||
* @param string $namespace The base namespace.
|
||||
* @param string|null $prefix The prefix to append.
|
||||
* @return array Updated tasks.
|
||||
*/
|
||||
protected function _findTasks($tasks, $path, $namespace, $prefix = null)
|
||||
{
|
||||
$path .= 'Shell/Task';
|
||||
if (!is_dir($path)) {
|
||||
return $tasks;
|
||||
}
|
||||
$candidates = $this->_findClassFiles($path, $namespace);
|
||||
$classes = $this->_findTaskClasses($candidates);
|
||||
foreach ($classes as $class) {
|
||||
list(, $name) = namespaceSplit($class);
|
||||
$name = substr($name, 0, -4);
|
||||
$fullName = ($prefix ? $prefix . '.' : '') . $name;
|
||||
$tasks[$name] = $fullName;
|
||||
}
|
||||
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find task classes in a given path.
|
||||
*
|
||||
* @param string $path The path to scan.
|
||||
* @param string $namespace Namespace.
|
||||
* @return array An array of files that may contain bake tasks.
|
||||
*/
|
||||
protected function _findClassFiles($path, $namespace)
|
||||
{
|
||||
$iterator = new \DirectoryIterator($path);
|
||||
$candidates = [];
|
||||
foreach ($iterator as $item) {
|
||||
if ($item->isDot() || $item->isDir()) {
|
||||
continue;
|
||||
}
|
||||
$name = $item->getBasename('.php');
|
||||
$candidates[] = $namespace . '\Shell\Task\\' . $name;
|
||||
}
|
||||
|
||||
return $candidates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find bake tasks in a given set of files.
|
||||
*
|
||||
* @param array $files The array of files.
|
||||
* @return array An array of matching classes.
|
||||
*/
|
||||
protected function _findTaskClasses($files)
|
||||
{
|
||||
$classes = [];
|
||||
foreach ($files as $className) {
|
||||
if (!class_exists($className)) {
|
||||
continue;
|
||||
}
|
||||
$reflect = new \ReflectionClass($className);
|
||||
if (!$reflect->isInstantiable()) {
|
||||
continue;
|
||||
}
|
||||
if (!$reflect->isSubclassOf('Bake\Shell\Task\BakeTask')) {
|
||||
continue;
|
||||
}
|
||||
$classes[] = $className;
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quickly bake the MVC
|
||||
*
|
||||
* @param string|null $name Name.
|
||||
* @return bool
|
||||
*/
|
||||
public function all($name = null)
|
||||
{
|
||||
if (
|
||||
$this->param('connection') && $this->param('everything') &&
|
||||
$this->param('connection') !== 'default'
|
||||
) {
|
||||
$this->warn('Can only bake everything on default connection');
|
||||
|
||||
return false;
|
||||
}
|
||||
$this->out('Bake All');
|
||||
$this->hr();
|
||||
|
||||
if (!empty($this->params['connection'])) {
|
||||
$this->connection = $this->params['connection'];
|
||||
}
|
||||
|
||||
if (empty($name) && !$this->param('everything')) {
|
||||
$this->Model->connection = $this->connection;
|
||||
$this->out('Possible model names based on your database:');
|
||||
foreach ($this->Model->listUnskipped() as $table) {
|
||||
$this->out('- ' . $table);
|
||||
}
|
||||
$this->out('Run <info>`cake bake all [name]`</info> to generate skeleton files.');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$allTables = collection([$name]);
|
||||
$filteredTables = $allTables;
|
||||
|
||||
if ($this->param('everything')) {
|
||||
$this->Model->connection = $this->connection;
|
||||
$filteredTables = collection($this->Model->listUnskipped());
|
||||
}
|
||||
|
||||
foreach (['Model', 'Controller', 'Template'] as $task) {
|
||||
$filteredTables->each(function ($tableName) use ($task) {
|
||||
$tableName = $this->_camelize($tableName);
|
||||
$this->{$task}->connection = $this->connection;
|
||||
$this->{$task}->interactive = $this->interactive;
|
||||
$this->{$task}->main($tableName);
|
||||
});
|
||||
}
|
||||
|
||||
$this->out('<success>Bake All complete.</success>', 1, Shell::QUIET);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return \Cake\Console\ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser()
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$parser->setDescription(
|
||||
'The Bake script generates controllers, models and template files for your application.' .
|
||||
' If run with no command line arguments, Bake guides the user through the class creation process.' .
|
||||
' You can customize the generation process by telling Bake where different parts of your application' .
|
||||
' are using command line arguments.'
|
||||
)->addSubcommand('all', [
|
||||
'help' => 'Bake a complete MVC skeleton.',
|
||||
])->addOption('everything', [
|
||||
'help' => 'Bake a complete MVC skeleton, using all the available tables. ' .
|
||||
'Usage: "bake all --everything"',
|
||||
'default' => false,
|
||||
'boolean' => true,
|
||||
])->addOption('prefix', [
|
||||
'help' => 'Prefix to bake controllers and templates into.',
|
||||
])->addOption('tablePrefix', [
|
||||
'help' => 'Table prefix to be used in models.',
|
||||
'default' => null,
|
||||
]);
|
||||
|
||||
$parser = $this->_setCommonOptions($parser);
|
||||
|
||||
foreach ($this->_taskMap as $task => $config) {
|
||||
$taskParser = $this->{$task}->getOptionParser();
|
||||
$this->{$task}->interactive = $this->interactive;
|
||||
$parser->addSubcommand(Inflector::underscore($task), [
|
||||
'help' => $taskParser->getDescription(),
|
||||
'parser' => $taskParser,
|
||||
]);
|
||||
}
|
||||
|
||||
return $parser;
|
||||
}
|
||||
}
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 0.1.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Bake\Shell\Task;
|
||||
|
||||
use Bake\Utility\CommonOptionsTrait;
|
||||
use Cake\Cache\Cache;
|
||||
use Cake\Console\Shell;
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Core\ConventionsTrait;
|
||||
use Cake\Filesystem\File;
|
||||
|
||||
/**
|
||||
* Base class for Bake Tasks.
|
||||
*/
|
||||
class BakeTask extends Shell
|
||||
{
|
||||
use CommonOptionsTrait;
|
||||
use ConventionsTrait;
|
||||
|
||||
/**
|
||||
* Table prefix
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $tablePrefix = null;
|
||||
|
||||
/**
|
||||
* The pathFragment appended to the plugin/app path.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $pathFragment;
|
||||
|
||||
/**
|
||||
* Name of plugin
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $plugin = null;
|
||||
|
||||
/**
|
||||
* The db connection being used for baking
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $connection = null;
|
||||
|
||||
/**
|
||||
* Disable caching and enable debug for baking.
|
||||
* This forces the most current database schema to be used.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function startup()
|
||||
{
|
||||
Configure::write('debug', true);
|
||||
Cache::disable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize hook.
|
||||
*
|
||||
* Populates the connection property, which is useful for tasks of tasks.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initialize()
|
||||
{
|
||||
if (empty($this->connection) && !empty($this->params['connection'])) {
|
||||
$this->connection = $this->params['connection'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the prefix name.
|
||||
*
|
||||
* Handles camelcasing each namespace in the prefix path.
|
||||
*
|
||||
* @return string The inflected prefix path.
|
||||
*/
|
||||
protected function _getPrefix()
|
||||
{
|
||||
$prefix = $this->param('prefix');
|
||||
if (!$prefix) {
|
||||
return '';
|
||||
}
|
||||
$parts = explode('/', $prefix);
|
||||
|
||||
return implode('/', array_map([$this, '_camelize'], $parts));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path for output. Checks the plugin property
|
||||
* and returns the correct path.
|
||||
*
|
||||
* @return string Path to output.
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
$path = APP . $this->pathFragment;
|
||||
if (isset($this->plugin)) {
|
||||
$path = $this->_pluginPath($this->plugin) . 'src/' . $this->pathFragment;
|
||||
}
|
||||
$prefix = $this->_getPrefix();
|
||||
if ($prefix) {
|
||||
$path .= $prefix . DS;
|
||||
}
|
||||
|
||||
return str_replace('/', DS, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base execute method parses some parameters and sets some properties on the bake tasks.
|
||||
* call when overriding execute()
|
||||
*
|
||||
* @return bool|int|null|void
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
if (isset($this->params['plugin'])) {
|
||||
$parts = explode('/', $this->params['plugin']);
|
||||
$this->plugin = implode('/', array_map([$this, '_camelize'], $parts));
|
||||
if (strpos($this->plugin, '\\')) {
|
||||
$this->abort('Invalid plugin namespace separator, please use / instead of \ for plugins.');
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (isset($this->params['connection'])) {
|
||||
$this->connection = $this->params['connection'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes an external shell command and pipes its output to the stdout
|
||||
*
|
||||
* @param string $command the command to execute
|
||||
* @return void
|
||||
* @throws \RuntimeException if any errors occurred during the execution
|
||||
*/
|
||||
public function callProcess($command)
|
||||
{
|
||||
$descriptorSpec = [
|
||||
0 => ['pipe', 'r'],
|
||||
1 => ['pipe', 'w'],
|
||||
2 => ['pipe', 'w'],
|
||||
];
|
||||
$this->_io->verbose('Running ' . $command);
|
||||
$process = proc_open(
|
||||
$command,
|
||||
$descriptorSpec,
|
||||
$pipes
|
||||
);
|
||||
if (!is_resource($process)) {
|
||||
$this->abort('Could not start subprocess.');
|
||||
|
||||
return;
|
||||
}
|
||||
fclose($pipes[0]);
|
||||
|
||||
$output = stream_get_contents($pipes[1]);
|
||||
fclose($pipes[1]);
|
||||
|
||||
$error = stream_get_contents($pipes[2]);
|
||||
fclose($pipes[2]);
|
||||
$exit = proc_close($process);
|
||||
|
||||
if ($exit !== 0) {
|
||||
throw new \RuntimeException($error);
|
||||
}
|
||||
|
||||
$this->out($output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles splitting up the plugin prefix and classname.
|
||||
*
|
||||
* Sets the plugin parameter and plugin property.
|
||||
*
|
||||
* @param string $name The name to possibly split.
|
||||
* @return string The name without the plugin prefix.
|
||||
*/
|
||||
protected function _getName($name)
|
||||
{
|
||||
if (strpos($name, '.')) {
|
||||
list($plugin, $name) = pluginSplit($name);
|
||||
$this->plugin = $this->params['plugin'] = $plugin;
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete empty file in a given path
|
||||
*
|
||||
* @param string $path Path to folder which contains 'empty' file.
|
||||
* @return void
|
||||
*/
|
||||
protected function _deleteEmptyFile($path)
|
||||
{
|
||||
$File = new File($path);
|
||||
if ($File->exists()) {
|
||||
$File->delete();
|
||||
$this->out(sprintf('<success>Deleted</success> `%s`', $path), 1, Shell::QUIET);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the option parser for this task.
|
||||
*
|
||||
* This base class method sets up some commonly used options.
|
||||
*
|
||||
* @return \Cake\Console\ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser()
|
||||
{
|
||||
return $this->_setCommonOptions(parent::getOptionParser());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 0.1.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Bake\Shell\Task;
|
||||
|
||||
use Bake\View\BakeView;
|
||||
use Cake\Console\Shell;
|
||||
use Cake\Core\ConventionsTrait;
|
||||
use Cake\Event\Event;
|
||||
use Cake\Event\EventManager;
|
||||
use Cake\Http\Response;
|
||||
use Cake\Http\ServerRequest as Request;
|
||||
use Cake\View\Exception\MissingTemplateException;
|
||||
use Cake\View\ViewVarsTrait;
|
||||
|
||||
/**
|
||||
* Used by other tasks to generate templated output, Acts as an interface to BakeView
|
||||
*/
|
||||
class BakeTemplateTask extends Shell
|
||||
{
|
||||
use ConventionsTrait;
|
||||
|
||||
use ViewVarsTrait;
|
||||
|
||||
/**
|
||||
* BakeView instance
|
||||
*
|
||||
* @var \Bake\View\BakeView|null
|
||||
*/
|
||||
public $View;
|
||||
|
||||
/**
|
||||
* Get view instance
|
||||
*
|
||||
* @return \Cake\View\View
|
||||
* @triggers Bake.initialize $view
|
||||
*/
|
||||
public function getView()
|
||||
{
|
||||
if ($this->View) {
|
||||
return $this->View;
|
||||
}
|
||||
|
||||
$theme = isset($this->params['theme']) ? $this->params['theme'] : '';
|
||||
|
||||
$viewOptions = [
|
||||
'helpers' => [
|
||||
'Bake.Bake',
|
||||
'Bake.DocBlock',
|
||||
],
|
||||
'theme' => $theme,
|
||||
];
|
||||
|
||||
$view = new BakeView(new Request(), new Response(), null, $viewOptions);
|
||||
$event = new Event('Bake.initialize', $view);
|
||||
EventManager::instance()->dispatch($event);
|
||||
/** @var \Bake\View\BakeView $view */
|
||||
$view = $event->getSubject();
|
||||
$this->View = $view;
|
||||
|
||||
return $this->View;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the template
|
||||
*
|
||||
* @param string $template bake template to render
|
||||
* @param array|null $vars Additional vars to set to template scope.
|
||||
* @return string contents of generated code template
|
||||
*/
|
||||
public function generate($template, $vars = null)
|
||||
{
|
||||
if ($vars !== null) {
|
||||
$this->set($vars);
|
||||
}
|
||||
|
||||
$this->getView()->set($this->viewVars);
|
||||
|
||||
try {
|
||||
return $this->View->render($template);
|
||||
} catch (MissingTemplateException $e) {
|
||||
$this->_io->verbose(sprintf('No bake template found for "%s"', $template));
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 0.1.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Bake\Shell\Task;
|
||||
|
||||
/**
|
||||
* Behavior code generator.
|
||||
*
|
||||
* @property \Bake\Shell\Task\BakeTemplateTask $BakeTemplate
|
||||
* @property \Bake\Shell\Task\TestTask $Test
|
||||
*/
|
||||
class BehaviorTask extends SimpleBakeTask
|
||||
{
|
||||
/**
|
||||
* Task name used in path generation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $pathFragment = 'Model/Behavior/';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function name()
|
||||
{
|
||||
return 'behavior';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function fileName($name)
|
||||
{
|
||||
return $name . 'Behavior.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function template()
|
||||
{
|
||||
return 'Model/behavior';
|
||||
}
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 0.1.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Bake\Shell\Task;
|
||||
|
||||
use Cake\Core\Configure;
|
||||
|
||||
/**
|
||||
* Task for creating cells.
|
||||
*
|
||||
* @property \Bake\Shell\Task\BakeTemplateTask $BakeTemplate
|
||||
* @property \Bake\Shell\Task\TestTask $Test
|
||||
*/
|
||||
class CellTask extends SimpleBakeTask
|
||||
{
|
||||
/**
|
||||
* Task name used in path generation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $pathFragment = 'View/Cell/';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function name()
|
||||
{
|
||||
return 'cell';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function fileName($name)
|
||||
{
|
||||
return $name . 'Cell.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function template()
|
||||
{
|
||||
return 'View/cell';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get template data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function templateData()
|
||||
{
|
||||
$prefix = $this->_getPrefix();
|
||||
if ($prefix) {
|
||||
$prefix = '\\' . str_replace('/', '\\', $prefix);
|
||||
}
|
||||
|
||||
$namespace = Configure::read('App.namespace');
|
||||
if ($this->plugin) {
|
||||
$namespace = $this->_pluginNamespace($this->plugin);
|
||||
}
|
||||
|
||||
return compact('namespace', 'prefix');
|
||||
}
|
||||
|
||||
/**
|
||||
* Bake the Cell class and template file.
|
||||
*
|
||||
* @param string $name The name of the cell to make.
|
||||
* @return string
|
||||
*/
|
||||
public function bake($name)
|
||||
{
|
||||
$this->bakeTemplate($name);
|
||||
|
||||
return parent::bake($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bake an empty file for a cell.
|
||||
*
|
||||
* @param string $name The name of the cell a template is needed for.
|
||||
* @return void
|
||||
*/
|
||||
public function bakeTemplate($name)
|
||||
{
|
||||
$restore = $this->pathFragment;
|
||||
|
||||
$this->pathFragment = 'Template/Cell/';
|
||||
$path = $this->getPath();
|
||||
$path .= implode(DS, [$name, 'display.ctp']);
|
||||
|
||||
$this->pathFragment = $restore;
|
||||
|
||||
$this->createFile($path, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return \Cake\Console\ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser()
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
$parser
|
||||
->addOption('prefix', [
|
||||
'help' => 'The namespace prefix to use.',
|
||||
]);
|
||||
|
||||
return $parser;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 1.7.4
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Bake\Shell\Task;
|
||||
|
||||
/**
|
||||
* Shell code generator.
|
||||
*
|
||||
* @property \Bake\Shell\Task\BakeTemplateTask $BakeTemplate
|
||||
* @property \Bake\Shell\Task\TestTask $Test
|
||||
*/
|
||||
class CommandTask extends SimpleBakeTask
|
||||
{
|
||||
/**
|
||||
* Task name used in path generation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $pathFragment = 'Command/';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function name()
|
||||
{
|
||||
return 'command';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function fileName($name)
|
||||
{
|
||||
return $name . 'Command.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function template()
|
||||
{
|
||||
return 'Command/command';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 0.1.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Bake\Shell\Task;
|
||||
|
||||
/**
|
||||
* Component code generator.
|
||||
*
|
||||
* @property \Bake\Shell\Task\BakeTemplateTask $BakeTemplate
|
||||
* @property \Bake\Shell\Task\TestTask $Test
|
||||
*/
|
||||
class ComponentTask extends SimpleBakeTask
|
||||
{
|
||||
/**
|
||||
* Task name used in path generation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $pathFragment = 'Controller/Component/';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function name()
|
||||
{
|
||||
return 'component';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function fileName($name)
|
||||
{
|
||||
return $name . 'Component.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function template()
|
||||
{
|
||||
return 'Controller/component';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 0.1.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Bake\Shell\Task;
|
||||
|
||||
use Cake\Console\Shell;
|
||||
use Cake\Core\Configure;
|
||||
use Cake\ORM\TableRegistry;
|
||||
|
||||
/**
|
||||
* Task class for creating and updating controller files.
|
||||
*
|
||||
* @property \Bake\Shell\Task\ModelTask $Model
|
||||
* @property \Bake\Shell\Task\BakeTemplateTask $BakeTemplate
|
||||
* @property \Bake\Shell\Task\TestTask $Test
|
||||
*/
|
||||
class ControllerTask extends BakeTask
|
||||
{
|
||||
/**
|
||||
* Tasks to be loaded by this Task
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $tasks = [
|
||||
'Bake.Model',
|
||||
'Bake.BakeTemplate',
|
||||
'Bake.Test',
|
||||
];
|
||||
|
||||
/**
|
||||
* Path fragment for generated code.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $pathFragment = 'Controller/';
|
||||
|
||||
/**
|
||||
* Execution method always used for tasks
|
||||
*
|
||||
* @param string|null $name The name of the controller to bake.
|
||||
* @return null|bool
|
||||
*/
|
||||
public function main($name = null)
|
||||
{
|
||||
parent::main();
|
||||
$name = $this->_getName($name);
|
||||
|
||||
if (empty($name)) {
|
||||
$this->out('Possible controllers based on your current database:');
|
||||
foreach ($this->listAll() as $table) {
|
||||
$this->out('- ' . $this->_camelize($table));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$controller = $this->_camelize($name);
|
||||
$this->bake($controller);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bake All the controllers at once. Will only bake controllers for models that exist.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function all()
|
||||
{
|
||||
$tables = $this->listAll();
|
||||
foreach ($tables as $table) {
|
||||
TableRegistry::getTableLocator()->clear();
|
||||
$this->main($table);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles and writes a Controller file
|
||||
*
|
||||
* @param string $controllerName Controller name already pluralized and correctly cased.
|
||||
* @return string Baked controller
|
||||
*/
|
||||
public function bake($controllerName)
|
||||
{
|
||||
$this->out("\n" . sprintf('Baking controller class for %s...', $controllerName), 1, Shell::QUIET);
|
||||
|
||||
$actions = [];
|
||||
if (!$this->param('no-actions') && !$this->param('actions')) {
|
||||
$actions = ['index', 'view', 'add', 'edit', 'delete'];
|
||||
}
|
||||
if ($this->param('actions')) {
|
||||
$actions = array_map('trim', explode(',', $this->param('actions')));
|
||||
$actions = array_filter($actions);
|
||||
}
|
||||
|
||||
$helpers = $this->getHelpers();
|
||||
$components = $this->getComponents();
|
||||
|
||||
$prefix = $this->_getPrefix();
|
||||
if ($prefix) {
|
||||
$prefix = '\\' . str_replace('/', '\\', $prefix);
|
||||
}
|
||||
|
||||
$namespace = Configure::read('App.namespace');
|
||||
if ($this->plugin) {
|
||||
$namespace = $this->_pluginNamespace($this->plugin);
|
||||
}
|
||||
|
||||
$currentModelName = $controllerName;
|
||||
$plugin = $this->plugin;
|
||||
if ($plugin) {
|
||||
$plugin .= '.';
|
||||
}
|
||||
|
||||
if (TableRegistry::getTableLocator()->exists($plugin . $currentModelName)) {
|
||||
$modelObj = TableRegistry::getTableLocator()->get($plugin . $currentModelName);
|
||||
} else {
|
||||
$modelObj = TableRegistry::getTableLocator()->get($plugin . $currentModelName, [
|
||||
'connectionName' => $this->connection,
|
||||
]);
|
||||
}
|
||||
|
||||
$pluralName = $this->_variableName($currentModelName);
|
||||
$singularName = $this->_singularName($currentModelName);
|
||||
$singularHumanName = $this->_singularHumanName($controllerName);
|
||||
$pluralHumanName = $this->_variableName($controllerName);
|
||||
|
||||
$defaultModel = sprintf('%s\Model\Table\%sTable', $namespace, $controllerName);
|
||||
if (!class_exists($defaultModel)) {
|
||||
$defaultModel = null;
|
||||
}
|
||||
$entityClassName = $this->_entityName($modelObj->getAlias());
|
||||
|
||||
$data = compact(
|
||||
'actions',
|
||||
'components',
|
||||
'currentModelName',
|
||||
'defaultModel',
|
||||
'entityClassName',
|
||||
'helpers',
|
||||
'modelObj',
|
||||
'namespace',
|
||||
'plugin',
|
||||
'pluralHumanName',
|
||||
'pluralName',
|
||||
'prefix',
|
||||
'singularHumanName',
|
||||
'singularName'
|
||||
);
|
||||
$data['name'] = $controllerName;
|
||||
|
||||
$out = $this->bakeController($controllerName, $data);
|
||||
$this->bakeTest($controllerName);
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the controller code
|
||||
*
|
||||
* @param string $controllerName The name of the controller.
|
||||
* @param array $data The data to turn into code.
|
||||
* @return string The generated controller file.
|
||||
*/
|
||||
public function bakeController($controllerName, array $data)
|
||||
{
|
||||
$data += [
|
||||
'name' => null,
|
||||
'namespace' => null,
|
||||
'prefix' => null,
|
||||
'actions' => null,
|
||||
'helpers' => null,
|
||||
'components' => null,
|
||||
'plugin' => null,
|
||||
'pluginPath' => null,
|
||||
];
|
||||
|
||||
$this->BakeTemplate->set($data);
|
||||
|
||||
$contents = $this->BakeTemplate->generate('Controller/controller');
|
||||
|
||||
$path = $this->getPath();
|
||||
$filename = $path . $controllerName . 'Controller.php';
|
||||
$this->createFile($filename, $contents);
|
||||
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles and writes a unit test file
|
||||
*
|
||||
* @param string $className Controller class name
|
||||
* @return string|false Baked test
|
||||
*/
|
||||
public function bakeTest($className)
|
||||
{
|
||||
if (!empty($this->params['no-test'])) {
|
||||
return false;
|
||||
}
|
||||
$this->Test->plugin = $this->plugin;
|
||||
$this->Test->connection = $this->connection;
|
||||
$this->Test->interactive = $this->interactive;
|
||||
|
||||
return $this->Test->bake('Controller', $className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of components for the controller.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getComponents()
|
||||
{
|
||||
$components = [];
|
||||
if (!empty($this->params['components'])) {
|
||||
$components = explode(',', $this->params['components']);
|
||||
$components = array_values(array_filter(array_map('trim', $components)));
|
||||
}
|
||||
|
||||
return $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of helpers for the controller.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getHelpers()
|
||||
{
|
||||
$helpers = [];
|
||||
if (!empty($this->params['helpers'])) {
|
||||
$helpers = explode(',', $this->params['helpers']);
|
||||
$helpers = array_values(array_filter(array_map('trim', $helpers)));
|
||||
}
|
||||
|
||||
return $helpers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs and gets the list of possible controllers from database
|
||||
*
|
||||
* @return array Set of controllers
|
||||
*/
|
||||
public function listAll()
|
||||
{
|
||||
$this->Model->connection = $this->connection;
|
||||
|
||||
return $this->Model->listUnskipped();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return \Cake\Console\ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser()
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
$parser->setDescription(
|
||||
'Bake a controller skeleton.'
|
||||
)->addArgument('name', [
|
||||
'help' => 'Name of the controller to bake (without the `Controller` suffix). ' .
|
||||
'You can use Plugin.name to bake controllers into plugins.',
|
||||
])->addOption('components', [
|
||||
'help' => 'The comma separated list of components to use.',
|
||||
])->addOption('helpers', [
|
||||
'help' => 'The comma separated list of helpers to use.',
|
||||
])->addOption('prefix', [
|
||||
'help' => 'The namespace/routing prefix to use.',
|
||||
])->addOption('actions', [
|
||||
'help' => 'The comma separated list of actions to generate. ' .
|
||||
'You can include custom methods provided by your template set here.',
|
||||
])->addOption('no-test', [
|
||||
'boolean' => true,
|
||||
'help' => 'Do not generate a test skeleton.',
|
||||
])->addOption('no-actions', [
|
||||
'boolean' => true,
|
||||
'help' => 'Do not generate basic CRUD action methods.',
|
||||
])->addSubcommand('all', [
|
||||
'help' => 'Bake all controllers with CRUD methods.',
|
||||
]);
|
||||
|
||||
return $parser;
|
||||
}
|
||||
}
|
||||
+466
@@ -0,0 +1,466 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 0.1.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Bake\Shell\Task;
|
||||
|
||||
use Cake\Console\Shell;
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Database\Exception;
|
||||
use Cake\Database\Schema\TableSchema;
|
||||
use Cake\Datasource\ConnectionManager;
|
||||
use Cake\ORM\TableRegistry;
|
||||
use Cake\Utility\Inflector;
|
||||
use Cake\Utility\Text;
|
||||
use DateTimeInterface;
|
||||
|
||||
/**
|
||||
* Task class for creating and updating fixtures files.
|
||||
*
|
||||
* @property \Bake\Shell\Task\BakeTemplateTask $BakeTemplate
|
||||
* @property \Bake\Shell\Task\ModelTask $Model
|
||||
*/
|
||||
class FixtureTask extends BakeTask
|
||||
{
|
||||
/**
|
||||
* Tasks to be loaded by this Task
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $tasks = [
|
||||
'Bake.Model',
|
||||
'Bake.BakeTemplate',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the file path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
$dir = 'Fixture/';
|
||||
$path = defined('TESTS') ? TESTS . $dir : ROOT . DS . 'tests' . DS . $dir;
|
||||
if (isset($this->plugin)) {
|
||||
$path = $this->_pluginPath($this->plugin) . 'tests/' . $dir;
|
||||
}
|
||||
|
||||
return str_replace('/', DS, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return \Cake\Console\ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser()
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$parser = $parser->setDescription(
|
||||
'Generate fixtures for use with the test suite. You can use `bake fixture all` to bake all fixtures.'
|
||||
)->addArgument('name', [
|
||||
'help' => 'Name of the fixture to bake (without the `Fixture` suffix). ' .
|
||||
'You can use Plugin.name to bake plugin fixtures.',
|
||||
])->addOption('table', [
|
||||
'help' => 'The table name if it does not follow conventions.',
|
||||
])->addOption('count', [
|
||||
'help' => 'When using generated data, the number of records to include in the fixture(s).',
|
||||
'short' => 'n',
|
||||
'default' => 1,
|
||||
])->addOption('schema', [
|
||||
'help' => 'Create a fixture that imports schema, instead of dumping a schema snapshot into the fixture.',
|
||||
'short' => 's',
|
||||
'boolean' => true,
|
||||
])->addOption('records', [
|
||||
'help' => 'Generate a fixture with records from the non-test database.' .
|
||||
' Used with --count and --conditions to limit which records are added to the fixture.',
|
||||
'short' => 'r',
|
||||
'boolean' => true,
|
||||
])->addOption('conditions', [
|
||||
'help' => 'The SQL snippet to use when importing records.',
|
||||
'default' => '1=1',
|
||||
])->addSubcommand('all', [
|
||||
'help' => 'Bake all fixture files for tables in the chosen connection.',
|
||||
]);
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execution method always used for tasks
|
||||
* Handles dispatching to interactive, named, or all processes.
|
||||
*
|
||||
* @param string|null $name The name of the fixture to bake.
|
||||
* @return null|bool
|
||||
*/
|
||||
public function main($name = null)
|
||||
{
|
||||
parent::main();
|
||||
$name = $this->_getName($name);
|
||||
|
||||
if (empty($name)) {
|
||||
$this->out('Choose a fixture to bake from the following:');
|
||||
foreach ($this->Model->listUnskipped() as $table) {
|
||||
$this->out('- ' . $this->_camelize($table));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$table = null;
|
||||
if (isset($this->params['table'])) {
|
||||
$table = $this->params['table'];
|
||||
}
|
||||
$model = $this->_camelize($name);
|
||||
$this->bake($model, $table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bake All the Fixtures at once. Will only bake fixtures for models that exist.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function all()
|
||||
{
|
||||
$tables = $this->Model->listUnskipped();
|
||||
|
||||
foreach ($tables as $table) {
|
||||
$this->main($table);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles and writes a Fixture file
|
||||
*
|
||||
* @param string $model Name of model to bake.
|
||||
* @param string|null $useTable Name of table to use.
|
||||
* @return string Baked fixture content
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function bake($model, $useTable = null)
|
||||
{
|
||||
$table = $schema = $records = $import = $modelImport = null;
|
||||
|
||||
if (!$useTable) {
|
||||
$useTable = Inflector::tableize($model);
|
||||
} elseif ($useTable !== Inflector::tableize($model)) {
|
||||
$table = $useTable;
|
||||
}
|
||||
|
||||
$importBits = [];
|
||||
if (!empty($this->params['schema'])) {
|
||||
$modelImport = true;
|
||||
$importBits[] = "'table' => '{$useTable}'";
|
||||
}
|
||||
if (!empty($importBits) && $this->connection !== 'default') {
|
||||
$importBits[] = "'connection' => '{$this->connection}'";
|
||||
}
|
||||
if (!empty($importBits)) {
|
||||
$import = sprintf("[%s]", implode(', ', $importBits));
|
||||
}
|
||||
|
||||
try {
|
||||
$data = $this->readSchema($model, $useTable);
|
||||
} catch (Exception $e) {
|
||||
TableRegistry::getTableLocator()->remove($model);
|
||||
$useTable = Inflector::underscore($model);
|
||||
$table = $useTable;
|
||||
$data = $this->readSchema($model, $useTable);
|
||||
}
|
||||
|
||||
if ($modelImport === null) {
|
||||
$schema = $this->_generateSchema($data);
|
||||
}
|
||||
|
||||
if (empty($this->params['records'])) {
|
||||
$recordCount = 1;
|
||||
if (isset($this->params['count'])) {
|
||||
$recordCount = $this->params['count'];
|
||||
}
|
||||
$records = $this->_makeRecordString($this->_generateRecords($data, $recordCount));
|
||||
}
|
||||
if (!empty($this->params['records'])) {
|
||||
$records = $this->_makeRecordString($this->_getRecordsFromTable($model, $useTable));
|
||||
}
|
||||
|
||||
return $this->generateFixtureFile($model, compact('records', 'table', 'schema', 'import'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get schema metadata for the current table mapping.
|
||||
*
|
||||
* @param string $name The model alias to use
|
||||
* @param string $table The table name to get schema metadata for.
|
||||
* @return \Cake\Database\Schema\TableSchema
|
||||
*/
|
||||
public function readSchema($name, $table)
|
||||
{
|
||||
$connection = ConnectionManager::get($this->connection);
|
||||
|
||||
if (TableRegistry::getTableLocator()->exists($name)) {
|
||||
$model = TableRegistry::getTableLocator()->get($name);
|
||||
} else {
|
||||
$model = TableRegistry::getTableLocator()->get($name, [
|
||||
'table' => $table,
|
||||
'connection' => $connection,
|
||||
]);
|
||||
}
|
||||
|
||||
return $model->getSchema();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the fixture file, and write to disk
|
||||
*
|
||||
* @param string $model name of the model being generated
|
||||
* @param array $otherVars Contents of the fixture file.
|
||||
* @return string Content saved into fixture file.
|
||||
*/
|
||||
public function generateFixtureFile($model, array $otherVars)
|
||||
{
|
||||
$defaults = [
|
||||
'name' => $model,
|
||||
'table' => null,
|
||||
'schema' => null,
|
||||
'records' => null,
|
||||
'import' => null,
|
||||
'fields' => null,
|
||||
'namespace' => Configure::read('App.namespace'),
|
||||
];
|
||||
if ($this->plugin) {
|
||||
$defaults['namespace'] = $this->_pluginNamespace($this->plugin);
|
||||
}
|
||||
$vars = $otherVars + $defaults;
|
||||
|
||||
$path = $this->getPath();
|
||||
$filename = $vars['name'] . 'Fixture.php';
|
||||
|
||||
$this->BakeTemplate->set('model', $model);
|
||||
$this->BakeTemplate->set($vars);
|
||||
$content = $this->BakeTemplate->generate('tests/fixture');
|
||||
|
||||
$this->out("\n" . sprintf('Baking test fixture for %s...', $model), 1, Shell::QUIET);
|
||||
$this->createFile($path . $filename, $content);
|
||||
$emptyFile = $path . 'empty';
|
||||
$this->_deleteEmptyFile($emptyFile);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a string representation of a schema.
|
||||
*
|
||||
* @param \Cake\Database\Schema\TableSchema $table Table schema
|
||||
* @return string fields definitions
|
||||
*/
|
||||
protected function _generateSchema(TableSchema $table)
|
||||
{
|
||||
$cols = $indexes = $constraints = [];
|
||||
foreach ($table->columns() as $field) {
|
||||
$fieldData = $table->getColumn($field);
|
||||
$properties = implode(', ', $this->_values($fieldData));
|
||||
$cols[] = " '$field' => [$properties],";
|
||||
}
|
||||
foreach ($table->indexes() as $index) {
|
||||
$fieldData = $table->getIndex($index);
|
||||
$properties = implode(', ', $this->_values($fieldData));
|
||||
$indexes[] = " '$index' => [$properties],";
|
||||
}
|
||||
foreach ($table->constraints() as $index) {
|
||||
$fieldData = $table->getConstraint($index);
|
||||
$properties = implode(', ', $this->_values($fieldData));
|
||||
$constraints[] = " '$index' => [$properties],";
|
||||
}
|
||||
$options = $this->_values($table->getOptions());
|
||||
|
||||
$content = implode("\n", $cols) . "\n";
|
||||
if (!empty($indexes)) {
|
||||
$content .= " '_indexes' => [\n" . implode("\n", $indexes) . "\n ],\n";
|
||||
}
|
||||
if (!empty($constraints)) {
|
||||
$content .= " '_constraints' => [\n" . implode("\n", $constraints) . "\n ],\n";
|
||||
}
|
||||
if (!empty($options)) {
|
||||
foreach ($options as &$option) {
|
||||
$option = ' ' . $option;
|
||||
}
|
||||
$content .= " '_options' => [\n" . implode(",\n", $options) . "\n ],\n";
|
||||
}
|
||||
|
||||
return "[\n$content ]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats Schema columns from Model Object
|
||||
*
|
||||
* @param array $values options keys(type, null, default, key, length, extra)
|
||||
* @return array Formatted values
|
||||
*/
|
||||
protected function _values($values)
|
||||
{
|
||||
$vals = [];
|
||||
if (!is_array($values)) {
|
||||
return $vals;
|
||||
}
|
||||
foreach ($values as $key => $val) {
|
||||
if (is_array($val)) {
|
||||
$vals[] = "'{$key}' => [" . implode(", ", $this->_values($val)) . "]";
|
||||
} else {
|
||||
$val = var_export($val, true);
|
||||
if ($val === 'NULL') {
|
||||
$val = 'null';
|
||||
}
|
||||
if (!is_numeric($key)) {
|
||||
$vals[] = "'{$key}' => {$val}";
|
||||
} else {
|
||||
$vals[] = "{$val}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $vals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate String representation of Records
|
||||
*
|
||||
* @param \Cake\Database\Schema\TableSchema $table Table schema array
|
||||
* @param int $recordCount The number of records to generate.
|
||||
* @return array Array of records to use in the fixture.
|
||||
*/
|
||||
protected function _generateRecords(TableSchema $table, $recordCount = 1)
|
||||
{
|
||||
$records = [];
|
||||
for ($i = 0; $i < $recordCount; $i++) {
|
||||
$record = [];
|
||||
foreach ($table->columns() as $field) {
|
||||
$fieldInfo = $table->getColumn($field);
|
||||
$insert = '';
|
||||
switch ($fieldInfo['type']) {
|
||||
case 'decimal':
|
||||
$insert = $i + 1.5;
|
||||
break;
|
||||
case 'biginteger':
|
||||
case 'integer':
|
||||
case 'float':
|
||||
case 'smallinteger':
|
||||
case 'tinyinteger':
|
||||
$insert = $i + 1;
|
||||
break;
|
||||
case 'string':
|
||||
case 'binary':
|
||||
$isPrimary = in_array($field, $table->primaryKey());
|
||||
if ($isPrimary) {
|
||||
$insert = Text::uuid();
|
||||
} else {
|
||||
$insert = "Lorem ipsum dolor sit amet";
|
||||
if (!empty($fieldInfo['length'])) {
|
||||
$insert = substr($insert, 0, (int)$fieldInfo['length'] > 2 ? (int)$fieldInfo['length'] - 2 : (int)$fieldInfo['length']);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'timestamp':
|
||||
$insert = time();
|
||||
break;
|
||||
case 'datetime':
|
||||
$insert = date('Y-m-d H:i:s');
|
||||
break;
|
||||
case 'date':
|
||||
$insert = date('Y-m-d');
|
||||
break;
|
||||
case 'time':
|
||||
$insert = date('H:i:s');
|
||||
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;
|
||||
case 'uuid':
|
||||
$insert = Text::uuid();
|
||||
break;
|
||||
}
|
||||
$record[$field] = $insert;
|
||||
}
|
||||
$records[] = $record;
|
||||
}
|
||||
|
||||
return $records;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a $records array into a string.
|
||||
*
|
||||
* @param array $records Array of records to be converted to string
|
||||
* @return string A string value of the $records array.
|
||||
*/
|
||||
protected function _makeRecordString($records)
|
||||
{
|
||||
$out = "[\n";
|
||||
foreach ($records as $record) {
|
||||
$values = [];
|
||||
foreach ($record as $field => $value) {
|
||||
if ($value instanceof DateTimeInterface) {
|
||||
$value = $value->format('Y-m-d H:i:s');
|
||||
}
|
||||
$val = var_export($value, true);
|
||||
if ($val === 'NULL') {
|
||||
$val = 'null';
|
||||
}
|
||||
$values[] = " '$field' => $val";
|
||||
}
|
||||
$out .= " [\n";
|
||||
$out .= implode(",\n", $values);
|
||||
$out .= ",\n ],\n";
|
||||
}
|
||||
$out .= " ]";
|
||||
|
||||
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|null $useTable Name of table to use.
|
||||
* @return array Array of records.
|
||||
*/
|
||||
protected function _getRecordsFromTable($modelName, $useTable = null)
|
||||
{
|
||||
$recordCount = (isset($this->params['count']) ? $this->params['count'] : 10);
|
||||
$conditions = (isset($this->params['conditions']) ? $this->params['conditions'] : '1=1');
|
||||
if (TableRegistry::getTableLocator()->exists($modelName)) {
|
||||
$model = TableRegistry::getTableLocator()->get($modelName);
|
||||
} else {
|
||||
$model = TableRegistry::getTableLocator()->get($modelName, [
|
||||
'table' => $useTable,
|
||||
'connection' => ConnectionManager::get($this->connection),
|
||||
]);
|
||||
}
|
||||
$records = $model->find('all')
|
||||
->where($conditions)
|
||||
->limit($recordCount)
|
||||
->enableHydration(false);
|
||||
|
||||
return $records->toArray();
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 1.0.9
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Bake\Shell\Task;
|
||||
|
||||
/**
|
||||
* ShellHelper code generator.
|
||||
*
|
||||
* @property \Bake\Shell\Task\BakeTemplateTask $BakeTemplate
|
||||
* @property \Bake\Shell\Task\TestTask $Test
|
||||
*/
|
||||
class FormTask extends SimpleBakeTask
|
||||
{
|
||||
/**
|
||||
* Task name used in path generation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $pathFragment = 'Form/';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function name()
|
||||
{
|
||||
return 'form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function fileName($name)
|
||||
{
|
||||
return $name . 'Form.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function template()
|
||||
{
|
||||
return 'Form/form';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 0.1.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Bake\Shell\Task;
|
||||
|
||||
/**
|
||||
* Helper code generator.
|
||||
*
|
||||
* @property \Bake\Shell\Task\BakeTemplateTask $BakeTemplate
|
||||
* @property \Bake\Shell\Task\TestTask $Test
|
||||
*/
|
||||
class HelperTask extends SimpleBakeTask
|
||||
{
|
||||
/**
|
||||
* Task name used in path generation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $pathFragment = 'View/Helper/';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function name()
|
||||
{
|
||||
return 'helper';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function fileName($name)
|
||||
{
|
||||
return $name . 'Helper.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function template()
|
||||
{
|
||||
return 'View/helper';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 1.1.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Bake\Shell\Task;
|
||||
|
||||
use Cake\Utility\Inflector;
|
||||
|
||||
/**
|
||||
* Mailer code generator.
|
||||
*
|
||||
* @property \Bake\Shell\Task\BakeTemplateTask $BakeTemplate
|
||||
* @property \Bake\Shell\Task\TestTask $Test
|
||||
*/
|
||||
class MailerTask extends SimpleBakeTask
|
||||
{
|
||||
/**
|
||||
* Task name used in path generation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $pathFragment = 'Mailer/';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function name()
|
||||
{
|
||||
return 'mailer';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function fileName($name)
|
||||
{
|
||||
return $name . 'Mailer.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function template()
|
||||
{
|
||||
return 'Mailer/mailer';
|
||||
}
|
||||
|
||||
/**
|
||||
* Bake the Mailer class and html/text layout files.
|
||||
*
|
||||
* @param string $name The name of the mailer to make.
|
||||
* @return string
|
||||
*/
|
||||
public function bake($name)
|
||||
{
|
||||
$this->bakeLayouts($name);
|
||||
|
||||
return parent::bake($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bake empty layout files for html/text emails.
|
||||
*
|
||||
* @param string $name The name of the mailer layouts are needed for.
|
||||
* @return void
|
||||
*/
|
||||
public function bakeLayouts($name)
|
||||
{
|
||||
$restore = $this->pathFragment;
|
||||
$layoutsPath = implode(DS, ['Template', 'Layout', 'Email']);
|
||||
|
||||
foreach (['html', 'text'] as $type) {
|
||||
$this->pathFragment = implode(DS, [$layoutsPath, $type, Inflector::underscore($name) . '.ctp']);
|
||||
$path = $this->getPath();
|
||||
$this->createFile($path, '');
|
||||
}
|
||||
|
||||
$this->pathFragment = $restore;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 1.3.6
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Bake\Shell\Task;
|
||||
|
||||
/**
|
||||
* Middleware code generator.
|
||||
*
|
||||
* @property \Bake\Shell\Task\BakeTemplateTask $BakeTemplate
|
||||
* @property \Bake\Shell\Task\TestTask $Test
|
||||
*/
|
||||
class MiddlewareTask extends SimpleBakeTask
|
||||
{
|
||||
/**
|
||||
* Task name used in path generation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $pathFragment = 'Middleware/';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function name()
|
||||
{
|
||||
return 'middleware';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function fileName($name)
|
||||
{
|
||||
return $name . 'Middleware.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function template()
|
||||
{
|
||||
return 'Middleware/middleware';
|
||||
}
|
||||
}
|
||||
+1220
File diff suppressed because it is too large.
Load diff
+392
@@ -0,0 +1,392 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 0.1.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Bake\Shell\Task;
|
||||
|
||||
use Cake\Core\App;
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Core\Plugin;
|
||||
use Cake\Filesystem\File;
|
||||
use Cake\Filesystem\Folder;
|
||||
use Cake\Utility\Inflector;
|
||||
|
||||
/**
|
||||
* The Plugin Task handles creating an empty plugin, ready to be used
|
||||
*
|
||||
* @property \Bake\Shell\Task\BakeTemplateTask $BakeTemplate
|
||||
*/
|
||||
class PluginTask extends BakeTask
|
||||
{
|
||||
/**
|
||||
* Tasks this task uses.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $tasks = [
|
||||
'Bake.BakeTemplate',
|
||||
];
|
||||
|
||||
/**
|
||||
* Plugin path.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $path;
|
||||
|
||||
/**
|
||||
* initialize
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initialize()
|
||||
{
|
||||
$this->path = current(App::path('Plugin'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Execution method always used for tasks
|
||||
*
|
||||
* @param string|null $name The name of the plugin to bake.
|
||||
* @return null|bool
|
||||
*/
|
||||
public function main($name = null)
|
||||
{
|
||||
if (empty($name)) {
|
||||
$this->err('<error>You must provide a plugin name in CamelCase format.</error>');
|
||||
$this->err('To make an "MyExample" plugin, run <info>`cake bake plugin MyExample`</info>.');
|
||||
|
||||
return false;
|
||||
}
|
||||
$parts = explode('/', $name);
|
||||
$plugin = implode('/', array_map([$this, '_camelize'], $parts));
|
||||
|
||||
$pluginPath = $this->_pluginPath($plugin);
|
||||
if (is_dir($pluginPath)) {
|
||||
$this->out(sprintf('Plugin: %s already exists, no action taken', $plugin));
|
||||
$this->out(sprintf('Path: %s', $pluginPath));
|
||||
|
||||
return false;
|
||||
}
|
||||
if (!$this->bake($plugin)) {
|
||||
$this->abort(sprintf("An error occurred trying to bake: %s in %s", $plugin, $this->path . $plugin));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bake the plugin's contents
|
||||
*
|
||||
* Also update the autoloader and the root composer.json file if it can be found
|
||||
*
|
||||
* @param string $plugin Name of the plugin in CamelCased format
|
||||
* @return bool|null
|
||||
*/
|
||||
public function bake($plugin)
|
||||
{
|
||||
$pathOptions = App::path('Plugin');
|
||||
if (count($pathOptions) > 1) {
|
||||
$this->findPath($pathOptions);
|
||||
}
|
||||
$this->out(sprintf("<info>Plugin Name:</info> %s", $plugin));
|
||||
$this->out(sprintf("<info>Plugin Directory:</info> %s", $this->path . $plugin));
|
||||
$this->hr();
|
||||
|
||||
$looksGood = $this->in('Look okay?', ['y', 'n', 'q'], 'y');
|
||||
|
||||
if (strtolower($looksGood) !== 'y') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->_generateFiles($plugin, $this->path);
|
||||
|
||||
$this->_modifyAutoloader($plugin, $this->path);
|
||||
$this->_modifyApplication($plugin);
|
||||
|
||||
$this->hr();
|
||||
$this->out(sprintf('<success>Created:</success> %s in %s', $plugin, $this->path . $plugin), 2);
|
||||
|
||||
$emptyFile = $this->path . 'empty';
|
||||
$this->_deleteEmptyFile($emptyFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the application class
|
||||
*
|
||||
* @param string $plugin Name of plugin
|
||||
* the plugin
|
||||
* @return void
|
||||
*/
|
||||
protected function _modifyApplication($plugin)
|
||||
{
|
||||
$application = new File(ROOT . DS . 'src' . DS . 'Application.php', false);
|
||||
if (!$application->exists()) {
|
||||
$this->err('<warning>Could not update application Application.php file, as it could not be found.</warning>');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->dispatchShell('plugin', 'load', $plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate all files for a plugin
|
||||
*
|
||||
* Find the first path which contains `src/Template/Bake/Plugin` that contains
|
||||
* something, and use that as the template to recursively render a plugin's
|
||||
* contents. Allows the creation of a bake them containing a `Plugin` folder
|
||||
* to provide customized bake output for plugins.
|
||||
*
|
||||
* @param string $pluginName the CamelCase name of the plugin
|
||||
* @param string $path the path to the plugins dir (the containing folder)
|
||||
* @return void
|
||||
*/
|
||||
protected function _generateFiles($pluginName, $path)
|
||||
{
|
||||
$namespace = str_replace('/', '\\', $pluginName);
|
||||
$baseNamespace = Configure::read('App.namespace');
|
||||
|
||||
$name = $pluginName;
|
||||
$vendor = 'your-name-here';
|
||||
if (strpos($pluginName, '/') !== false) {
|
||||
list($vendor, $name) = explode('/', $pluginName);
|
||||
}
|
||||
$package = $vendor . '/' . $name;
|
||||
|
||||
$this->BakeTemplate->set([
|
||||
'package' => $package,
|
||||
'namespace' => $namespace,
|
||||
'baseNamespace' => $baseNamespace,
|
||||
'plugin' => $pluginName,
|
||||
'routePath' => Inflector::dasherize($pluginName),
|
||||
'path' => $path,
|
||||
'root' => ROOT,
|
||||
]);
|
||||
|
||||
$root = $path . $pluginName . DS;
|
||||
|
||||
$paths = [];
|
||||
if (!empty($this->params['theme'])) {
|
||||
$paths[] = Plugin::path($this->params['theme']) . 'src/Template/';
|
||||
}
|
||||
|
||||
$paths = array_merge($paths, Configure::read('App.paths.templates'));
|
||||
$paths[] = Plugin::path('Bake') . 'src/Template/';
|
||||
|
||||
do {
|
||||
$templatesPath = array_shift($paths) . 'Bake/Plugin';
|
||||
$templatesDir = new Folder($templatesPath);
|
||||
$templates = $templatesDir->findRecursive('.*\.(twig|ctp)');
|
||||
} while (!$templates);
|
||||
|
||||
sort($templates);
|
||||
foreach ($templates as $template) {
|
||||
$template = substr($template, strrpos($template, 'Plugin' . DIRECTORY_SEPARATOR) + 7, -4);
|
||||
$template = rtrim($template, '.');
|
||||
$this->_generateFile($template, $root);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a file
|
||||
*
|
||||
* @param string $template The template to render
|
||||
* @param string $root The path to the plugin's root
|
||||
* @return void
|
||||
*/
|
||||
protected function _generateFile($template, $root)
|
||||
{
|
||||
$this->out(sprintf('Generating %s file...', $template));
|
||||
$out = $this->BakeTemplate->generate('Plugin/' . $template);
|
||||
$this->createFile($root . $template, $out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies App's composer.json to include the plugin and tries to call
|
||||
* composer dump-autoload to refresh the autoloader cache
|
||||
*
|
||||
* @param string $plugin Name of plugin
|
||||
* @param string $path The path to save the phpunit.xml file to.
|
||||
* @return bool True if composer could be modified correctly
|
||||
*/
|
||||
protected function _modifyAutoloader($plugin, $path)
|
||||
{
|
||||
$file = $this->_rootComposerFilePath();
|
||||
|
||||
if (!file_exists($file)) {
|
||||
$this->out(sprintf('<info>Main composer file %s not found</info>', $file));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$autoloadPath = str_replace(ROOT, '.', $this->path);
|
||||
$autoloadPath = str_replace('\\', '/', $autoloadPath);
|
||||
$namespace = str_replace('/', '\\', $plugin);
|
||||
|
||||
$config = json_decode(file_get_contents($file), true);
|
||||
$config['autoload']['psr-4'][$namespace . '\\'] = $autoloadPath . $plugin . "/src/";
|
||||
$config['autoload-dev']['psr-4'][$namespace . '\\Test\\'] = $autoloadPath . $plugin . "/tests/";
|
||||
|
||||
$this->out('<info>Modifying composer autoloader</info>');
|
||||
|
||||
$out = json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . "\n";
|
||||
$this->createFile($file, $out);
|
||||
|
||||
$composer = $this->findComposer();
|
||||
|
||||
if (!$composer) {
|
||||
$this->abort('Could not locate composer. Add composer to your PATH, or use the --composer option.');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$cwd = getcwd();
|
||||
|
||||
// Windows makes running multiple commands at once hard.
|
||||
chdir(dirname($this->_rootComposerFilePath()));
|
||||
$command = 'php ' . escapeshellarg($composer) . ' dump-autoload';
|
||||
$this->callProcess($command);
|
||||
|
||||
chdir($cwd);
|
||||
} catch (\RuntimeException $e) {
|
||||
$error = $e->getMessage();
|
||||
$this->abort(sprintf('Could not run `composer dump-autoload`: %s', $error));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The path to the main application's composer file
|
||||
*
|
||||
* This is a test isolation wrapper
|
||||
*
|
||||
* @return string the abs file path
|
||||
*/
|
||||
protected function _rootComposerFilePath()
|
||||
{
|
||||
return ROOT . DS . 'composer.json';
|
||||
}
|
||||
|
||||
/**
|
||||
* find and change $this->path to the user selection
|
||||
*
|
||||
* @param array $pathOptions The list of paths to look in.
|
||||
* @return void
|
||||
*/
|
||||
public function findPath(array $pathOptions)
|
||||
{
|
||||
$valid = false;
|
||||
foreach ($pathOptions as $i => $path) {
|
||||
if (!is_dir($path)) {
|
||||
unset($pathOptions[$i]);
|
||||
}
|
||||
}
|
||||
$pathOptions = array_values($pathOptions);
|
||||
$max = count($pathOptions);
|
||||
|
||||
if ($max === 0) {
|
||||
$this->err('No valid plugin paths found! Please configure a plugin path that exists.');
|
||||
throw new \RuntimeException();
|
||||
}
|
||||
|
||||
if ($max === 1) {
|
||||
$this->path = $pathOptions[0];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$choice = null;
|
||||
while (!$valid) {
|
||||
foreach ($pathOptions as $i => $option) {
|
||||
$this->out($i + 1 . '. ' . $option);
|
||||
}
|
||||
$prompt = 'Choose a plugin path from the paths above.';
|
||||
$choice = (int)$this->in($prompt, null, '1');
|
||||
if ($choice > 0 && $choice <= $max) {
|
||||
$valid = true;
|
||||
}
|
||||
}
|
||||
$this->path = $pathOptions[$choice - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return \Cake\Console\ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser()
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
$parser->setDescription(
|
||||
'Create the directory structure, AppController class and testing setup for a new plugin. ' .
|
||||
'Can create plugins in any of your bootstrapped plugin paths.'
|
||||
)->addArgument('name', [
|
||||
'help' => 'CamelCased name of the plugin to create.',
|
||||
])->addOption('composer', [
|
||||
'default' => ROOT . DS . 'composer.phar',
|
||||
'help' => 'The path to the composer executable.',
|
||||
])->removeOption('plugin');
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses either the CLI option or looks in $PATH and cwd for composer.
|
||||
*
|
||||
* @return string|bool Either the path to composer or false if it cannot be found.
|
||||
*/
|
||||
public function findComposer()
|
||||
{
|
||||
if (!empty($this->params['composer'])) {
|
||||
$path = $this->params['composer'];
|
||||
if (file_exists($path)) {
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
$composer = false;
|
||||
$path = env('PATH');
|
||||
if (!empty($path)) {
|
||||
$paths = explode(PATH_SEPARATOR, $path);
|
||||
$composer = $this->_searchPath($paths);
|
||||
}
|
||||
|
||||
return $composer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the $PATH for composer.
|
||||
*
|
||||
* @param array $path The paths to search.
|
||||
* @return string|bool
|
||||
*/
|
||||
protected function _searchPath($path)
|
||||
{
|
||||
$composer = ['composer.phar', 'composer'];
|
||||
foreach ($path as $dir) {
|
||||
foreach ($composer as $cmd) {
|
||||
if (is_file($dir . DS . $cmd)) {
|
||||
$this->_io->verbose('Found composer executable in ' . $dir);
|
||||
|
||||
return $dir . DS . $cmd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 1.1.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Bake\Shell\Task;
|
||||
|
||||
/**
|
||||
* ShellHelper code generator.
|
||||
*
|
||||
* @property \Bake\Shell\Task\BakeTemplateTask $BakeTemplate
|
||||
* @property \Bake\Shell\Task\TestTask $Test
|
||||
*/
|
||||
class ShellHelperTask extends SimpleBakeTask
|
||||
{
|
||||
/**
|
||||
* Task name used in path generation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $pathFragment = 'Shell/Helper/';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function name()
|
||||
{
|
||||
return 'shell_helper';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function fileName($name)
|
||||
{
|
||||
return $name . 'Helper.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function template()
|
||||
{
|
||||
return 'Shell/helper';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 0.1.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Bake\Shell\Task;
|
||||
|
||||
/**
|
||||
* Shell code generator.
|
||||
*
|
||||
* @property \Bake\Shell\Task\BakeTemplateTask $BakeTemplate
|
||||
* @property \Bake\Shell\Task\TestTask $Test
|
||||
*/
|
||||
class ShellTask extends SimpleBakeTask
|
||||
{
|
||||
/**
|
||||
* Task name used in path generation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $pathFragment = 'Shell/';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function name()
|
||||
{
|
||||
return 'shell';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function fileName($name)
|
||||
{
|
||||
return $name . 'Shell.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function template()
|
||||
{
|
||||
return 'Shell/shell';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 0.1.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Bake\Shell\Task;
|
||||
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Utility\Inflector;
|
||||
|
||||
/**
|
||||
* Base class for simple bake tasks code generator.
|
||||
*
|
||||
* @property \Bake\Shell\Task\BakeTemplateTask $BakeTemplate
|
||||
* @property \Bake\Shell\Task\TestTask $Test
|
||||
*/
|
||||
abstract class SimpleBakeTask extends BakeTask
|
||||
{
|
||||
/**
|
||||
* Tasks to be loaded by this Task
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $tasks = [
|
||||
'Bake.BakeTemplate',
|
||||
'Bake.Test',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the generated object's name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function name();
|
||||
|
||||
/**
|
||||
* Get the generated object's filename without the leading path.
|
||||
*
|
||||
* @param string $name The name of the object being generated
|
||||
* @return string
|
||||
*/
|
||||
abstract public function fileName($name);
|
||||
|
||||
/**
|
||||
* Get the template name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function template();
|
||||
|
||||
/**
|
||||
* Get template data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function templateData()
|
||||
{
|
||||
$namespace = Configure::read('App.namespace');
|
||||
if ($this->plugin) {
|
||||
$namespace = $this->_pluginNamespace($this->plugin);
|
||||
}
|
||||
|
||||
return ['namespace' => $namespace];
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute method
|
||||
*
|
||||
* @param string|null $name The name of the object to bake.
|
||||
* @return int|null
|
||||
*/
|
||||
public function main($name = null)
|
||||
{
|
||||
parent::main();
|
||||
if (empty($name)) {
|
||||
$this->abort('You must provide a name to bake a ' . $this->name());
|
||||
|
||||
return null;
|
||||
}
|
||||
$name = $this->_getName($name);
|
||||
$name = Inflector::camelize($name);
|
||||
$this->bake($name);
|
||||
$this->bakeTest($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a class stub
|
||||
*
|
||||
* @param string $name The classname to generate.
|
||||
* @return string
|
||||
*/
|
||||
public function bake($name)
|
||||
{
|
||||
$this->BakeTemplate->set('name', $name);
|
||||
$this->BakeTemplate->set($this->templateData());
|
||||
$contents = $this->BakeTemplate->generate($this->template());
|
||||
|
||||
$filename = $this->getPath() . $this->fileName($name);
|
||||
$this->createFile($filename, $contents);
|
||||
$emptyFile = $this->getPath() . 'empty';
|
||||
$this->_deleteEmptyFile($emptyFile);
|
||||
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a test case.
|
||||
*
|
||||
* @param string $className The class to bake a test for.
|
||||
* @return string|bool|null
|
||||
*/
|
||||
public function bakeTest($className)
|
||||
{
|
||||
if (!empty($this->params['no-test'])) {
|
||||
return null;
|
||||
}
|
||||
$this->Test->plugin = $this->plugin;
|
||||
|
||||
return $this->Test->bake($this->name(), $className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return \Cake\Console\ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser()
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
$name = $this->name();
|
||||
$parser->setDescription(
|
||||
sprintf('Bake a %s class file.', $name)
|
||||
)->addArgument('name', [
|
||||
'help' => sprintf(
|
||||
'Name of the %s to bake. Can use Plugin.name to bake %s files into plugins.',
|
||||
$name,
|
||||
$name
|
||||
),
|
||||
])->addOption('no-test', [
|
||||
'boolean' => true,
|
||||
'help' => 'Do not generate a test skeleton.',
|
||||
]);
|
||||
|
||||
return $parser;
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 1.2.2
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Bake\Shell\Task;
|
||||
|
||||
/**
|
||||
* Shell Task code generator.
|
||||
*
|
||||
* @property \Bake\Shell\Task\BakeTemplateTask $BakeTemplate
|
||||
* @property \Bake\Shell\Task\TestTask $Test
|
||||
*/
|
||||
class TaskTask extends SimpleBakeTask
|
||||
{
|
||||
/**
|
||||
* Task name used in path generation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $pathFragment = 'Shell/Task/';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function name()
|
||||
{
|
||||
return 'task';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function fileName($name)
|
||||
{
|
||||
return $name . 'Task.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function template()
|
||||
{
|
||||
return 'Shell/task';
|
||||
}
|
||||
}
|
||||
+435
@@ -0,0 +1,435 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 0.1.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Bake\Shell\Task;
|
||||
|
||||
use Bake\Utility\Model\AssociationFilter;
|
||||
use Cake\Console\Shell;
|
||||
use Cake\Core\App;
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Datasource\EntityInterface;
|
||||
use Cake\ORM\Table;
|
||||
use Cake\ORM\TableRegistry;
|
||||
use Cake\Utility\Inflector;
|
||||
|
||||
/**
|
||||
* Task class for creating and updating view template files.
|
||||
*
|
||||
* @property \Bake\Shell\Task\ModelTask $Model
|
||||
* @property \Bake\Shell\Task\BakeTemplateTask $BakeTemplate
|
||||
*/
|
||||
class TemplateTask extends BakeTask
|
||||
{
|
||||
/**
|
||||
* Tasks to be loaded by this Task
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $tasks = [
|
||||
'Bake.Model',
|
||||
'Bake.BakeTemplate',
|
||||
];
|
||||
|
||||
/**
|
||||
* path to Template directory
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $pathFragment = 'Template/';
|
||||
|
||||
/**
|
||||
* Name of the controller being used
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $controllerName = null;
|
||||
|
||||
/**
|
||||
* Classname of the controller being used
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $controllerClass = null;
|
||||
|
||||
/**
|
||||
* Name with plugin of the model being used
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $modelName = null;
|
||||
|
||||
/**
|
||||
* Actions to use for scaffolding
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $scaffoldActions = ['index', 'view', 'add', 'edit'];
|
||||
|
||||
/**
|
||||
* AssociationFilter utility
|
||||
*
|
||||
* @var \Bake\Utility\Model\AssociationFilter|null
|
||||
*/
|
||||
protected $_associationFilter = null;
|
||||
|
||||
/**
|
||||
* Template path.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $path;
|
||||
|
||||
/**
|
||||
* Override initialize
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initialize()
|
||||
{
|
||||
$this->path = current(App::path('Template'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Execution method always used for tasks
|
||||
*
|
||||
* @param string|null $name The name of the controller to bake view templates for.
|
||||
* @param string|null $template The template to bake with.
|
||||
* @param string|null $action The output action name. Defaults to $template.
|
||||
* @return mixed
|
||||
*/
|
||||
public function main($name = null, $template = null, $action = null)
|
||||
{
|
||||
parent::main();
|
||||
|
||||
if (empty($name)) {
|
||||
$this->out('Possible tables to bake view templates for based on your current database:');
|
||||
$this->Model->connection = $this->connection;
|
||||
foreach ($this->Model->listUnskipped() as $table) {
|
||||
$this->out('- ' . $this->_camelize($table));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
$name = $this->_getName($name);
|
||||
|
||||
$controller = null;
|
||||
if (!empty($this->params['controller'])) {
|
||||
$controller = $this->params['controller'];
|
||||
}
|
||||
$this->controller($name, $controller);
|
||||
$this->model($name);
|
||||
|
||||
if ($template && $action === null) {
|
||||
$action = $template;
|
||||
}
|
||||
if ($template) {
|
||||
$this->bake($template, true, $action);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$vars = $this->_loadController();
|
||||
$methods = $this->_methodsToBake();
|
||||
|
||||
foreach ($methods as $method) {
|
||||
$content = $this->getContent($method, $vars);
|
||||
if ($content) {
|
||||
$this->bake($method, $content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the model class for the table.
|
||||
*
|
||||
* @param string $table The table/model that is being baked.
|
||||
* @return void
|
||||
*/
|
||||
public function model($table)
|
||||
{
|
||||
$tableName = $this->_camelize($table);
|
||||
$plugin = null;
|
||||
if (!empty($this->params['plugin'])) {
|
||||
$plugin = $this->params['plugin'] . '.';
|
||||
}
|
||||
$this->modelName = $plugin . $tableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the controller related properties.
|
||||
*
|
||||
* @param string $table The table/model that is being baked.
|
||||
* @param string|null $controller The controller name if specified.
|
||||
* @return void
|
||||
*/
|
||||
public function controller($table, $controller = null)
|
||||
{
|
||||
$tableName = $this->_camelize($table);
|
||||
if (empty($controller)) {
|
||||
$controller = $tableName;
|
||||
}
|
||||
$this->controllerName = $controller;
|
||||
|
||||
$plugin = $this->param('plugin');
|
||||
if ($plugin) {
|
||||
$plugin .= '.';
|
||||
}
|
||||
$prefix = $this->_getPrefix();
|
||||
if ($prefix) {
|
||||
$prefix .= '/';
|
||||
}
|
||||
$this->controllerClass = (string)App::className($plugin . $prefix . $controller, 'Controller', 'Controller');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path base for view templates.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
$path = parent::getPath();
|
||||
$path .= $this->controllerName . DS;
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of actions that can / should have view templates baked for them.
|
||||
*
|
||||
* @return array Array of action names that should be baked
|
||||
*/
|
||||
protected function _methodsToBake()
|
||||
{
|
||||
$base = Configure::read('App.namespace');
|
||||
|
||||
$methods = [];
|
||||
if (class_exists($this->controllerClass)) {
|
||||
$methods = array_diff(
|
||||
array_map(
|
||||
'Cake\Utility\Inflector::underscore',
|
||||
get_class_methods($this->controllerClass)
|
||||
),
|
||||
array_map(
|
||||
'Cake\Utility\Inflector::underscore',
|
||||
get_class_methods($base . '\Controller\AppController')
|
||||
)
|
||||
);
|
||||
}
|
||||
if (empty($methods)) {
|
||||
$methods = $this->scaffoldActions;
|
||||
}
|
||||
foreach ($methods as $i => $method) {
|
||||
if ($method[0] === '_') {
|
||||
unset($methods[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
return $methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bake All view templates for all controller actions.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function all()
|
||||
{
|
||||
$this->Model->connection = $this->connection;
|
||||
$tables = $this->Model->listUnskipped();
|
||||
|
||||
foreach ($tables as $table) {
|
||||
$this->main($table);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads Controller and sets variables for the template
|
||||
* Available template variables:
|
||||
*
|
||||
* - 'modelObject'
|
||||
* - 'modelClass'
|
||||
* - 'entityClass'
|
||||
* - 'primaryKey'
|
||||
* - 'displayField'
|
||||
* - 'singularVar'
|
||||
* - 'pluralVar'
|
||||
* - 'singularHumanName'
|
||||
* - 'pluralHumanName'
|
||||
* - 'fields'
|
||||
* - 'keyFields'
|
||||
* - 'schema'
|
||||
*
|
||||
* @return array Returns variables to be made available to a view template
|
||||
*/
|
||||
protected function _loadController()
|
||||
{
|
||||
if (TableRegistry::getTableLocator()->exists($this->modelName)) {
|
||||
$modelObject = TableRegistry::getTableLocator()->get($this->modelName);
|
||||
} else {
|
||||
$modelObject = TableRegistry::getTableLocator()->get($this->modelName, [
|
||||
'connectionName' => $this->connection,
|
||||
]);
|
||||
}
|
||||
|
||||
$namespace = Configure::read('App.namespace');
|
||||
|
||||
$primaryKey = (array)$modelObject->getPrimaryKey();
|
||||
$displayField = $modelObject->getDisplayField();
|
||||
$singularVar = $this->_singularName($this->controllerName);
|
||||
$singularHumanName = $this->_singularHumanName($this->controllerName);
|
||||
$schema = $modelObject->getSchema();
|
||||
$fields = $schema->columns();
|
||||
$modelClass = $this->modelName;
|
||||
|
||||
list(, $entityClass) = namespaceSplit($this->_entityName($this->modelName));
|
||||
$entityClass = sprintf('%s\Model\Entity\%s', $namespace, $entityClass);
|
||||
if (!class_exists($entityClass)) {
|
||||
$entityClass = EntityInterface::class;
|
||||
}
|
||||
$associations = $this->_filteredAssociations($modelObject);
|
||||
$keyFields = [];
|
||||
if (!empty($associations['BelongsTo'])) {
|
||||
foreach ($associations['BelongsTo'] as $assoc) {
|
||||
$keyFields[$assoc['foreignKey']] = $assoc['variable'];
|
||||
}
|
||||
}
|
||||
|
||||
$pluralVar = Inflector::variable($this->controllerName);
|
||||
$pluralHumanName = $this->_pluralHumanName($this->controllerName);
|
||||
|
||||
return compact(
|
||||
'modelObject',
|
||||
'modelClass',
|
||||
'entityClass',
|
||||
'schema',
|
||||
'primaryKey',
|
||||
'displayField',
|
||||
'singularVar',
|
||||
'pluralVar',
|
||||
'singularHumanName',
|
||||
'pluralHumanName',
|
||||
'fields',
|
||||
'associations',
|
||||
'keyFields',
|
||||
'namespace'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles and writes bakes the view file.
|
||||
*
|
||||
* @param string $template Template file to use.
|
||||
* @param string|true $content Content to write.
|
||||
* @param string $outputFile The output file to create. If null will use `$template`
|
||||
* @return string|false Generated file content.
|
||||
*/
|
||||
public function bake($template, $content = '', $outputFile = null)
|
||||
{
|
||||
if ($outputFile === null) {
|
||||
$outputFile = $template;
|
||||
}
|
||||
if ($content === true) {
|
||||
$content = $this->getContent($template);
|
||||
}
|
||||
if (empty($content)) {
|
||||
$this->err("<warning>No generated content for '{$template}.ctp', not generating template.</warning>");
|
||||
|
||||
return false;
|
||||
}
|
||||
$this->out("\n" . sprintf('Baking `%s` view template file...', $outputFile), 1, Shell::QUIET);
|
||||
$path = $this->getPath();
|
||||
$filename = $path . Inflector::underscore($outputFile) . '.ctp';
|
||||
$this->createFile($filename, $content);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds content from template and variables
|
||||
*
|
||||
* @param string $action name to generate content to
|
||||
* @param array|null $vars passed for use in templates
|
||||
* @return string|false Content from template
|
||||
*/
|
||||
public function getContent($action, $vars = null)
|
||||
{
|
||||
if (!$vars) {
|
||||
$vars = $this->_loadController();
|
||||
}
|
||||
|
||||
if (empty($vars['primaryKey'])) {
|
||||
$this->abort('Cannot generate views for models with no primary key');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($action === "index" && !empty($this->params['index-columns'])) {
|
||||
$this->BakeTemplate->set('indexColumns', $this->params['index-columns']);
|
||||
}
|
||||
|
||||
$this->BakeTemplate->set('action', $action);
|
||||
$this->BakeTemplate->set('plugin', $this->plugin);
|
||||
$this->BakeTemplate->set($vars);
|
||||
|
||||
return $this->BakeTemplate->generate("Template/$action");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return \Cake\Console\ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser()
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$parser->setDescription(
|
||||
'Bake views for a controller, using built-in or custom templates. '
|
||||
)->addArgument('controller', [
|
||||
'help' => 'Name of the controller views to bake. You can use Plugin.name as a shortcut for plugin baking.',
|
||||
])->addArgument('action', [
|
||||
'help' => "Will bake a single action's file. core templates are (index, add, edit, view)",
|
||||
])->addArgument('alias', [
|
||||
'help' => 'Will bake the template in <action> but create the filename after <alias>.',
|
||||
])->addOption('controller', [
|
||||
'help' => 'The controller name if you have a controller that does not follow conventions.',
|
||||
])->addOption('prefix', [
|
||||
'help' => 'The routing prefix to generate views for.',
|
||||
])->addOption('index-columns', [
|
||||
'help' => 'Limit for the number of index columns',
|
||||
'default' => 0,
|
||||
])->addSubcommand('all', [
|
||||
'help' => '[optional] Bake all CRUD action views for all controllers. Requires models and controllers to exist.',
|
||||
]);
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get filtered associations
|
||||
* To be mocked...
|
||||
*
|
||||
* @param \Cake\ORM\Table $model Table
|
||||
* @return array associations
|
||||
*/
|
||||
protected function _filteredAssociations(Table $model)
|
||||
{
|
||||
if (is_null($this->_associationFilter)) {
|
||||
$this->_associationFilter = new AssociationFilter();
|
||||
}
|
||||
|
||||
return $this->_associationFilter->filterAssociations($model);
|
||||
}
|
||||
}
|
||||
+752
@@ -0,0 +1,752 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since 0.1.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
namespace Bake\Shell\Task;
|
||||
|
||||
use Cake\Console\Shell;
|
||||
use Cake\Controller\Controller;
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Core\Exception\Exception;
|
||||
use Cake\Core\Plugin;
|
||||
use Cake\Filesystem\Folder;
|
||||
use Cake\Http\Response;
|
||||
use Cake\Http\ServerRequest as Request;
|
||||
use Cake\ORM\Association;
|
||||
use Cake\ORM\Table;
|
||||
use Cake\ORM\TableRegistry;
|
||||
use Cake\Utility\Inflector;
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
* Task class for creating and updating test files.
|
||||
*
|
||||
* @property \Bake\Shell\Task\BakeTemplateTask $BakeTemplate
|
||||
*/
|
||||
class TestTask extends BakeTask
|
||||
{
|
||||
/**
|
||||
* Tasks used.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $tasks = ['Bake.BakeTemplate'];
|
||||
|
||||
/**
|
||||
* class types that methods can be generated for
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $classTypes = [
|
||||
'Entity' => 'Model\Entity',
|
||||
'Table' => 'Model\Table',
|
||||
'Controller' => 'Controller',
|
||||
'Component' => 'Controller\Component',
|
||||
'Behavior' => 'Model\Behavior',
|
||||
'Helper' => 'View\Helper',
|
||||
'Shell' => 'Shell',
|
||||
'Task' => 'Shell\Task',
|
||||
'ShellHelper' => 'Shell\Helper',
|
||||
'Cell' => 'View\Cell',
|
||||
'Form' => 'Form',
|
||||
'Mailer' => 'Mailer',
|
||||
'Command' => 'Command',
|
||||
];
|
||||
|
||||
/**
|
||||
* class types that methods can be generated for
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $classSuffixes = [
|
||||
'Entity' => '',
|
||||
'Table' => 'Table',
|
||||
'Controller' => 'Controller',
|
||||
'Component' => 'Component',
|
||||
'Behavior' => 'Behavior',
|
||||
'Helper' => 'Helper',
|
||||
'Shell' => 'Shell',
|
||||
'Task' => 'Task',
|
||||
'ShellHelper' => 'Helper',
|
||||
'Cell' => 'Cell',
|
||||
'Form' => 'Form',
|
||||
'Mailer' => 'Mailer',
|
||||
'Command' => 'Command',
|
||||
];
|
||||
|
||||
/**
|
||||
* Internal list of fixtures that have been added so far.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_fixtures = [];
|
||||
|
||||
/**
|
||||
* Execution method always used for tasks
|
||||
*
|
||||
* @param string|null $type Class type.
|
||||
* @param string|null $name Name.
|
||||
* @return array|null
|
||||
*/
|
||||
public function main($type = null, $name = null)
|
||||
{
|
||||
parent::main();
|
||||
$type = $this->normalize($type);
|
||||
$name = $this->_getName($name);
|
||||
|
||||
if (empty($type) && empty($name)) {
|
||||
$this->outputTypeChoices();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->param('all')) {
|
||||
$this->_bakeAll($type);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (empty($name)) {
|
||||
return $this->outputClassChoices($type);
|
||||
}
|
||||
|
||||
if ($this->bake($type, $name)) {
|
||||
$this->out('<success>Done</success>');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output a list of class types you can bake a test for.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function outputTypeChoices()
|
||||
{
|
||||
$this->out(
|
||||
'You must provide a class type to bake a test for. The valid types are:',
|
||||
2
|
||||
);
|
||||
$i = 0;
|
||||
foreach ($this->classTypes as $option => $package) {
|
||||
$this->out(++$i . '. ' . $option);
|
||||
}
|
||||
$this->out('');
|
||||
$this->out('Re-run your command as `cake bake <type> <classname>`');
|
||||
}
|
||||
|
||||
/**
|
||||
* Output a list of possible classnames you might want to generate a test for.
|
||||
*
|
||||
* @param string $typeName The typename to get classes for.
|
||||
* @return array
|
||||
*/
|
||||
public function outputClassChoices($typeName)
|
||||
{
|
||||
$type = $this->mapType($typeName);
|
||||
$this->out(
|
||||
'You must provide a class to bake a test for. Some possible options are:',
|
||||
2
|
||||
);
|
||||
$options = $this->_getClassOptions($type);
|
||||
$i = 0;
|
||||
foreach ($options as $option) {
|
||||
$this->out(++$i . '. ' . $option);
|
||||
}
|
||||
$this->out('');
|
||||
$this->out('Re-run your command as `cake bake ' . $typeName . ' <classname>`');
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type The typename to get bake all classes for.
|
||||
* @return void
|
||||
*/
|
||||
protected function _bakeAll($type)
|
||||
{
|
||||
$mappedType = $this->mapType($type);
|
||||
$classes = $this->_getClassOptions($mappedType);
|
||||
|
||||
foreach ($classes as $class) {
|
||||
if ($this->bake($type, $class)) {
|
||||
$this->out('<success>Done - ' . $class . '</success>');
|
||||
} else {
|
||||
$this->out('<error>Failed - ' . $class . '</error>');
|
||||
}
|
||||
}
|
||||
|
||||
$this->out('<info>Bake finished</info>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the possible classes for a given type.
|
||||
*
|
||||
* @param string $namespace The namespace fragment to look for classes in.
|
||||
* @return array
|
||||
*/
|
||||
protected function _getClassOptions($namespace)
|
||||
{
|
||||
$classes = [];
|
||||
$base = APP;
|
||||
if ($this->plugin) {
|
||||
$base = Plugin::classPath($this->plugin);
|
||||
}
|
||||
$path = $base . str_replace('\\', DS, $namespace);
|
||||
$folder = new Folder($path);
|
||||
list(, $files) = $folder->read();
|
||||
foreach ($files as $file) {
|
||||
$classes[] = str_replace('.php', '', $file);
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return string|false
|
||||
*/
|
||||
public function bake($type, $className)
|
||||
{
|
||||
$type = $this->normalize($type);
|
||||
if (!isset($this->classSuffixes[$type]) || !isset($this->classTypes[$type])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$fullClassName = $this->getRealClassName($type, $className);
|
||||
|
||||
if (empty($this->params['no-fixture'])) {
|
||||
if (!empty($this->params['fixtures'])) {
|
||||
$fixtures = array_map('trim', explode(',', $this->params['fixtures']));
|
||||
$this->_fixtures = array_filter($fixtures);
|
||||
} elseif ($this->typeCanDetectFixtures($type) && class_exists($fullClassName)) {
|
||||
$this->out('Bake is detecting possible fixtures...');
|
||||
$testSubject = $this->buildTestSubject($type, $fullClassName);
|
||||
$this->generateFixtureList($testSubject);
|
||||
}
|
||||
}
|
||||
|
||||
$methods = [];
|
||||
if (class_exists($fullClassName)) {
|
||||
$methods = $this->getTestableMethods($fullClassName);
|
||||
}
|
||||
$mock = $this->hasMockClass($type);
|
||||
list($preConstruct, $construction, $postConstruct) = $this->generateConstructor($type, $fullClassName);
|
||||
$uses = $this->generateUses($type, $fullClassName);
|
||||
|
||||
$subject = $className;
|
||||
list($namespace, $className) = namespaceSplit($fullClassName);
|
||||
|
||||
$baseNamespace = Configure::read('App.namespace');
|
||||
if ($this->plugin) {
|
||||
$baseNamespace = $this->_pluginNamespace($this->plugin);
|
||||
}
|
||||
$subNamespace = substr($namespace, strlen($baseNamespace) + 1);
|
||||
|
||||
$properties = $this->generateProperties($type, $subject, $fullClassName);
|
||||
|
||||
$this->out("\n" . sprintf('Baking test case for %s ...', $fullClassName), 1, Shell::QUIET);
|
||||
|
||||
$this->BakeTemplate->set('fixtures', $this->_fixtures);
|
||||
$this->BakeTemplate->set('plugin', $this->plugin);
|
||||
$this->BakeTemplate->set(compact(
|
||||
'subject',
|
||||
'className',
|
||||
'properties',
|
||||
'methods',
|
||||
'type',
|
||||
'fullClassName',
|
||||
'mock',
|
||||
'type',
|
||||
'preConstruct',
|
||||
'postConstruct',
|
||||
'construction',
|
||||
'uses',
|
||||
'baseNamespace',
|
||||
'subNamespace',
|
||||
'namespace'
|
||||
));
|
||||
$out = $this->BakeTemplate->generate('tests/test_case');
|
||||
|
||||
$filename = $this->testCaseFileName($type, $fullClassName);
|
||||
$emptyFile = $this->getPath() . $this->getSubspacePath($type) . DS . 'empty';
|
||||
$this->_deleteEmptyFile($emptyFile);
|
||||
if ($this->createFile($filename, $out)) {
|
||||
return $out;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the chosen type can find its own fixtures.
|
||||
* Currently only model, and controller are supported
|
||||
*
|
||||
* @param string $type The Type of object you are generating tests for eg. controller
|
||||
* @return bool
|
||||
*/
|
||||
public function typeCanDetectFixtures($type)
|
||||
{
|
||||
return in_array($type, ['Controller', 'Table'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an instance of the class to be tested.
|
||||
* So that fixtures can be detected
|
||||
*
|
||||
* @param string $type The type of object you are generating tests for eg. controller
|
||||
* @param string $class The classname of the class the test is being generated for.
|
||||
* @return object And instance of the class that is going to be tested.
|
||||
*/
|
||||
public function buildTestSubject($type, $class)
|
||||
{
|
||||
if ($type === 'Table') {
|
||||
list(, $name) = namespaceSplit($class);
|
||||
$name = str_replace('Table', '', $name);
|
||||
if ($this->plugin) {
|
||||
$name = $this->plugin . '.' . $name;
|
||||
}
|
||||
if (TableRegistry::getTableLocator()->exists($name)) {
|
||||
$instance = TableRegistry::getTableLocator()->get($name);
|
||||
} else {
|
||||
$instance = TableRegistry::getTableLocator()->get($name, [
|
||||
'connectionName' => $this->connection,
|
||||
]);
|
||||
}
|
||||
} elseif ($type === 'Controller') {
|
||||
$instance = new $class(new Request(), new Response());
|
||||
} else {
|
||||
$instance = new $class();
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the real class name from the cake short form. If the class name is already
|
||||
* suffixed with the type, the type will not be duplicated.
|
||||
*
|
||||
* @param string $type The Type of object you are generating tests for eg. controller.
|
||||
* @param string $class the Classname of the class the test is being generated for.
|
||||
* @return string Real class name
|
||||
*/
|
||||
public function getRealClassName($type, $class)
|
||||
{
|
||||
$namespace = Configure::read('App.namespace');
|
||||
if ($this->plugin) {
|
||||
$namespace = str_replace('/', '\\', $this->plugin);
|
||||
}
|
||||
$suffix = $this->classSuffixes[$type];
|
||||
$subSpace = $this->mapType($type);
|
||||
if ($suffix && strpos($class, $suffix) === false) {
|
||||
$class .= $suffix;
|
||||
}
|
||||
$prefix = $this->_getPrefix();
|
||||
if (in_array($type, ['Controller', 'Cell'], true) && $prefix) {
|
||||
$subSpace .= '\\' . str_replace('/', '\\', $prefix);
|
||||
}
|
||||
|
||||
return $namespace . '\\' . $subSpace . '\\' . $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the subspace path for a test.
|
||||
*
|
||||
* @param string $type The Type of object you are generating tests for eg. controller.
|
||||
* @return string Path of the subspace.
|
||||
*/
|
||||
public function getSubspacePath($type)
|
||||
{
|
||||
$subspace = $this->mapType($type);
|
||||
|
||||
return str_replace('\\', DS, $subspace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the types that TestTask uses to concrete types that App::className can use.
|
||||
*
|
||||
* @param string $type The type of thing having a test generated.
|
||||
* @return string
|
||||
* @throws \Cake\Core\Exception\Exception When invalid object types are requested.
|
||||
*/
|
||||
public function mapType($type)
|
||||
{
|
||||
if (empty($this->classTypes[$type])) {
|
||||
throw new Exception('Invalid object type.');
|
||||
}
|
||||
|
||||
return $this->classTypes[$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.
|
||||
*/
|
||||
public function getTestableMethods($className)
|
||||
{
|
||||
$class = new ReflectionClass($className);
|
||||
$out = [];
|
||||
foreach ($class->getMethods() as $method) {
|
||||
if ($method->getDeclaringClass()->getName() !== $className) {
|
||||
continue;
|
||||
}
|
||||
if (!$method->isPublic()) {
|
||||
continue;
|
||||
}
|
||||
$out[] = $method->getName();
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the list of fixtures that will be required to run this test based on
|
||||
* loaded models.
|
||||
*
|
||||
* @param object $subject The object you want to generate fixtures for.
|
||||
* @return array Array of fixtures to be included in the test.
|
||||
*/
|
||||
public function generateFixtureList($subject)
|
||||
{
|
||||
$this->_fixtures = [];
|
||||
if ($subject instanceof Table) {
|
||||
$this->_processModel($subject);
|
||||
} elseif ($subject instanceof Controller) {
|
||||
$this->_processController($subject);
|
||||
}
|
||||
|
||||
return array_values($this->_fixtures);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a model, pull out model name + associations converted to fixture names.
|
||||
*
|
||||
* @param \Cake\ORM\Table $subject A Model class to scan for associations and pull fixtures off of.
|
||||
* @return void
|
||||
*/
|
||||
protected function _processModel($subject)
|
||||
{
|
||||
if (!$subject instanceof Table) {
|
||||
return;
|
||||
}
|
||||
$this->_addFixture($subject->getAlias());
|
||||
foreach ($subject->associations()->keys() as $alias) {
|
||||
$assoc = $subject->getAssociation($alias);
|
||||
$target = $assoc->getTarget();
|
||||
$name = $target->getAlias();
|
||||
$subjectClass = get_class($subject);
|
||||
|
||||
if ($subjectClass !== 'Cake\ORM\Table' && $subjectClass === get_class($target)) {
|
||||
continue;
|
||||
}
|
||||
if (!isset($this->_fixtures[$name])) {
|
||||
$this->_addFixture($target->getAlias());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process all the models attached to a controller
|
||||
* and generate a fixture list.
|
||||
*
|
||||
* @param \Cake\Controller\Controller $subject A controller to pull model names off of.
|
||||
* @return void
|
||||
*/
|
||||
protected function _processController($subject)
|
||||
{
|
||||
$models = [$subject->modelClass];
|
||||
foreach ($models as $model) {
|
||||
list(, $model) = pluginSplit($model);
|
||||
$this->_processModel($subject->{$model});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add class name to the fixture list.
|
||||
* Sets the app. or plugin.plugin_name. prefix.
|
||||
*
|
||||
* @param string $name Name of the Model class that a fixture might be required for.
|
||||
* @return void
|
||||
*/
|
||||
protected function _addFixture($name)
|
||||
{
|
||||
if ($this->plugin) {
|
||||
$prefix = 'plugin.' . $this->plugin . '.';
|
||||
} else {
|
||||
$prefix = 'app.';
|
||||
}
|
||||
$fixture = $prefix . $this->_fixtureName($name);
|
||||
$this->_fixtures[$name] = $fixture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is a mock class required for this type of test?
|
||||
* Controllers require a mock class.
|
||||
*
|
||||
* @param string $type The type of object tests are being generated for eg. controller.
|
||||
* @return bool
|
||||
*/
|
||||
public function hasMockClass($type)
|
||||
{
|
||||
return $type === 'Controller';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a constructor code snippet for the type and class name
|
||||
*
|
||||
* @param string $type The Type of object you are generating tests for eg. controller
|
||||
* @param string $fullClassName The full classname of the class the test is being generated for.
|
||||
* @return array Constructor snippets for the thing you are building.
|
||||
*/
|
||||
public function generateConstructor($type, $fullClassName)
|
||||
{
|
||||
list(, $className) = namespaceSplit($fullClassName);
|
||||
$pre = $construct = $post = '';
|
||||
if ($type === 'Table') {
|
||||
$tableName = str_replace('Table', '', $className);
|
||||
$pre = "\$config = TableRegistry::getTableLocator()->exists('{$tableName}') ? [] : ['className' => {$className}::class];";
|
||||
$construct = "TableRegistry::getTableLocator()->get('{$tableName}', \$config);";
|
||||
}
|
||||
if ($type === 'Behavior' || $type === 'Entity' || $type === 'Form') {
|
||||
$construct = "new {$className}();";
|
||||
}
|
||||
if ($type === 'Helper') {
|
||||
$pre = "\$view = new View();";
|
||||
$construct = "new {$className}(\$view);";
|
||||
}
|
||||
if ($type === 'Command') {
|
||||
$construct = "\$this->useCommandRunner();";
|
||||
}
|
||||
if ($type === 'Component') {
|
||||
$pre = "\$registry = new ComponentRegistry();";
|
||||
$construct = "new {$className}(\$registry);";
|
||||
}
|
||||
if ($type === 'Shell') {
|
||||
$pre = "\$this->io = \$this->getMockBuilder('Cake\Console\ConsoleIo')->getMock();";
|
||||
$construct = "new {$className}(\$this->io);";
|
||||
}
|
||||
if ($type === 'Task') {
|
||||
$pre = "\$this->io = \$this->getMockBuilder('Cake\Console\ConsoleIo')->getMock();";
|
||||
$construct = "new {$className}(\$this->io);";
|
||||
}
|
||||
if ($type === 'Cell') {
|
||||
$pre = "\$this->request = \$this->getMockBuilder('Cake\Http\ServerRequest')->getMock();\n";
|
||||
$pre .= " \$this->response = \$this->getMockBuilder('Cake\Http\Response')->getMock();";
|
||||
$construct = "new {$className}(\$this->request, \$this->response);";
|
||||
}
|
||||
if ($type === 'ShellHelper') {
|
||||
$pre = "\$this->stub = new ConsoleOutput();\n";
|
||||
$pre .= " \$this->io = new ConsoleIo(\$this->stub);";
|
||||
$construct = "new {$className}(\$this->io);";
|
||||
}
|
||||
|
||||
return [$pre, $construct, $post];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate property info for the type and class name
|
||||
*
|
||||
* The generated property info consists of a set of arrays that hold the following keys:
|
||||
*
|
||||
* - `description` (the property description)
|
||||
* - `type` (the property docblock type)
|
||||
* - `name` (the property name)
|
||||
* - `value` (optional - the properties initial value)
|
||||
*
|
||||
* @param string $type The Type of object you are generating tests for eg. controller
|
||||
* @param string $subject The name of the test subject.
|
||||
* @param string $fullClassName The Classname of the class the test is being generated for.
|
||||
* @return array An array containing property info
|
||||
*/
|
||||
public function generateProperties($type, $subject, $fullClassName)
|
||||
{
|
||||
$properties = [];
|
||||
switch ($type) {
|
||||
case 'Cell':
|
||||
$properties[] = [
|
||||
'description' => 'Request mock',
|
||||
'type' => '\Cake\Http\ServerRequest|\PHPUnit_Framework_MockObject_MockObject',
|
||||
'name' => 'request',
|
||||
];
|
||||
$properties[] = [
|
||||
'description' => 'Response mock',
|
||||
'type' => '\Cake\Http\Response|\PHPUnit_Framework_MockObject_MockObject',
|
||||
'name' => 'response',
|
||||
];
|
||||
break;
|
||||
|
||||
case 'Shell':
|
||||
case 'Task':
|
||||
$properties[] = [
|
||||
'description' => 'ConsoleIo mock',
|
||||
'type' => '\Cake\Console\ConsoleIo|\PHPUnit_Framework_MockObject_MockObject',
|
||||
'name' => 'io',
|
||||
];
|
||||
break;
|
||||
|
||||
case 'ShellHelper':
|
||||
$properties[] = [
|
||||
'description' => 'ConsoleOutput stub',
|
||||
'type' => '\Cake\TestSuite\Stub\ConsoleOutput',
|
||||
'name' => 'stub',
|
||||
];
|
||||
$properties[] = [
|
||||
'description' => 'ConsoleIo mock',
|
||||
'type' => '\Cake\Console\ConsoleIo',
|
||||
'name' => 'io',
|
||||
];
|
||||
break;
|
||||
}
|
||||
|
||||
if (!in_array($type, ['Controller', 'Command'])) {
|
||||
$properties[] = [
|
||||
'description' => 'Test subject',
|
||||
'type' => '\\' . $fullClassName,
|
||||
'name' => $subject,
|
||||
];
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the uses() calls for a type & class name
|
||||
*
|
||||
* @param string $type The Type of object you are generating tests for eg. controller
|
||||
* @param string $fullClassName The Classname of the class the test is being generated for.
|
||||
* @return array An array containing used classes
|
||||
*/
|
||||
public function generateUses($type, $fullClassName)
|
||||
{
|
||||
$uses = [];
|
||||
if ($type === 'Component') {
|
||||
$uses[] = 'Cake\Controller\ComponentRegistry';
|
||||
}
|
||||
if ($type === 'Table') {
|
||||
$uses[] = 'Cake\ORM\TableRegistry';
|
||||
}
|
||||
if ($type === 'Helper') {
|
||||
$uses[] = 'Cake\View\View';
|
||||
}
|
||||
if ($type === 'ShellHelper') {
|
||||
$uses[] = 'Cake\TestSuite\Stub\ConsoleOutput';
|
||||
$uses[] = 'Cake\Console\ConsoleIo';
|
||||
}
|
||||
$uses[] = $fullClassName;
|
||||
|
||||
return $uses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
$dir = 'TestCase/';
|
||||
$path = defined('TESTS') ? TESTS . $dir : ROOT . DS . 'tests' . DS . $dir;
|
||||
if (isset($this->plugin)) {
|
||||
$path = $this->_pluginPath($this->plugin) . 'tests/' . $dir;
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the filename for the test case. resolve the suffixes for controllers
|
||||
* and get the plugin path if needed.
|
||||
*
|
||||
* @param string $type The Type of object you are generating tests for eg. controller
|
||||
* @param string $className The fully qualified classname of the class the test is being generated for.
|
||||
* @return string filename the test should be created on.
|
||||
*/
|
||||
public function testCaseFileName($type, $className)
|
||||
{
|
||||
$path = $this->getPath();
|
||||
$namespace = Configure::read('App.namespace');
|
||||
if ($this->plugin) {
|
||||
$namespace = $this->plugin;
|
||||
}
|
||||
|
||||
$classTail = substr($className, strlen($namespace) + 1);
|
||||
$path = $path . $classTail . 'Test.php';
|
||||
|
||||
return str_replace(['/', '\\'], DS, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return \Cake\Console\ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser()
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$types = array_keys($this->classTypes);
|
||||
$types = array_merge($types, array_map([$this, 'underscore'], $types));
|
||||
|
||||
$parser->setDescription(
|
||||
'Bake test case skeletons for classes.'
|
||||
)->addArgument('type', [
|
||||
'help' => 'Type of class to bake, can be any of the following:' .
|
||||
' controller, model, helper, component or behavior.',
|
||||
'choices' => $types,
|
||||
])->addArgument('name', [
|
||||
'help' => 'An existing class to bake tests for.',
|
||||
])->addOption('fixtures', [
|
||||
'help' => 'A comma separated list of fixture names you want to include.',
|
||||
])->addOption('no-fixture', [
|
||||
'boolean' => true,
|
||||
'default' => false,
|
||||
'help' => 'Select if you want to bake without fixture.',
|
||||
])->addOption('prefix', [
|
||||
'default' => false,
|
||||
'help' => 'Use when baking tests for prefixed controllers.',
|
||||
])->addOption('all', [
|
||||
'boolean' => true,
|
||||
'help' => 'Bake all classes of the given type',
|
||||
]);
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes string into CamelCase format.
|
||||
*
|
||||
* @param string $string String to inflect
|
||||
* @return string
|
||||
*/
|
||||
protected function normalize($string)
|
||||
{
|
||||
return Inflector::camelize(Inflector::underscore($string));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to allow under_score format for CLI env usage.
|
||||
*
|
||||
* @param string $string String to inflect
|
||||
* @return string
|
||||
*/
|
||||
protected function underscore($string)
|
||||
{
|
||||
return Inflector::underscore($string);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user