lithium\console\command\Create

class

The create command allows you to rapidly develop your models, views, controllers, and tests by generating the minimum code necessary to test and run your application.

These commands create the Posts-controller and model.

li3 create controller Posts
li3 create model Posts

To create the tests for each run:

li3 create test controller Posts
li3 create test model Posts

To create everything in one go run:

li3 create Posts

Have your own model template? Use the --template option:

li3 create --template=MyModel model Posts

Subclasses

Source

class Create extends \lithium\console\Command {

	/**
	 * The name of the template to use to generate the file. This allows you to add a custom
	 * template to be used in place of the core template for each command. Place templates in
	 * `<library>\extensions\command\create\template`.
	 *
	 * ```sh
	 * li3 create --template=MyModel model Posts
	 * ```
	 *
	 * @var string
	 */
	public $template = null;

	/**
	 * Name of the library to use, defaults to the current application.
	 *
	 * The following command creates a `Posts` model inside the `blog` library.
	 * ```sh
	 * li3 create --library=blog model Posts
	 * ```
	 *
	 * @var string
	 */
	public $library = null;

	/**
	 * Holds library data from `lithium\core\Libraries::get()`.
	 *
	 * @var array
	 */
	protected $_library = [];

	/**
	 * Class initializer. Parses template and sets up params that need to be filled.
	 *
	 * @return void
	 */
	protected function _init() {
		parent::_init();
		$this->library = $this->library ?: true;
		$defaults = ['prefix' => null, 'path' => null];
		$this->_library = (array) Libraries::get($this->library) + $defaults;
	}

	/**
	 * Run the create command. Takes `$command` and delegates to `$command::$method`
	 *
	 * When just `$type` is given and `$name` is not given,
	 * will use `$type` as a name and create all types of things for it.
	 *
	 * @param string $type The type of thing to create (i.e. `'test'`, `'model'` or `'controller'`).
	 * @param string $name The name of the thing to create (i.e. `'Posts'`).
	 * @return boolean
	 */
	public function run($type, $name = null) {
		if ($type && !$name) {
			return $this->_default($type);
		}
		$this->request->shift();
		$this->template = $this->template ?: $type;

		if ($this->_execute($type)) {
			return true;
		}
		$this->error("Thing of type `{$type}` could not be created.");
		return false;
	}

	/**
	 * Execute the given sub-command for the current request.
	 *
	 * @param string $command The sub-command name. example: Model, Controller, Test
	 * @return boolean
	 */
	protected function _execute($command) {
		try {
			if (!$class = $this->_instance($command)) {
				return false;
			}
		} catch (ClassNotFoundException $e) {
			return false;
		}
		$data = [];
		$params = $class->invokeMethod('_params');

		foreach ($params as $param) {
			$data[$param] = $class->invokeMethod("_{$param}", [$this->request]);
		}

		if ($message = $class->invokeMethod('_save', [$data])) {
			$this->out($message);
			return true;
		}
		return false;
	}

	/**
	 * Run through the default set. model, controller, test model, test controller
	 *
	 * @param string $name class name to create
	 * @return boolean
	 */
	protected function _default($name) {
		$commands = [
			['model', $name],
			['controller', $name],
			['test', 'model', $name],
			['test', 'controller', $name]
		];
		foreach ($commands as $args) {
			$command = $this->template = $this->request->params['command'] = array_shift($args);
			$this->request->params['action'] = array_shift($args);
			$this->request->params['args'] = $args;

			if (!$this->_execute($command)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Get the namespace.
	 *
	 * @param string $request
	 * @param array $options
	 * @return string
	 */
	protected function _namespace($request, $options  = []) {
		$name = $request->command;
		$defaults = [
			'prefix' => $this->_library['prefix'],
			'prepend' => null,
			'spaces' => [
				'model' => 'models', 'view' => 'views', 'controller' => 'controllers',
				'command' => 'extensions.command', 'adapter' => 'extensions.adapter',
				'helper' => 'extensions.helper'
			]
		];
		$options += $defaults;

		if (isset($options['spaces'][$name])) {
			$name = $options['spaces'][$name];
		}
		return str_replace('.', '\\', $options['prefix'] . $options['prepend'] . $name);
	}

	/**
	 * Parse a template to find available variables specified in `{:name}` format. Each variable
	 * corresponds to a method in the sub command. For example, a `{:namespace}` variable will
	 * call the namespace method in the model command when `li3 create model Post` is called.
	 *
	 * @return array
	 */
	protected function _params() {
		$contents = $this->_template();

		if (empty($contents)) {
			return [];
		}
		preg_match_all('/(?:\{:(?P<params>[^}]+)\})/', $contents, $keys);

		if (!empty($keys['params'])) {
			return array_values(array_unique($keys['params']));
		}
		return [];
	}

	/**
	 * Returns the contents of the template.
	 *
	 * @return string
	 */
	protected function _template() {
		$file = Libraries::locate('command.create.template', $this->template, [
			'filter' => false, 'type' => 'file', 'suffix' => '.txt.php'
		]);
		if (!$file || is_array($file)) {
			return false;
		}
		return file_get_contents($file);
	}

	/**
	 * Get an instance of a sub-command
	 *
	 * @param string $name the name of the sub-command to instantiate
	 * @param array $config
	 * @return object
	 */
	protected function _instance($name, array $config = []) {
		if ($class = Libraries::locate('command.create', Inflector::camelize($name))) {
			$this->request->params['template'] = $this->template;

			return new $class([
				'request' => $this->request,
				'classes' => $this->_classes
			]);
		}
		return parent::_instance($name, $config);
	}

	/**
	 * Save a template with the current params. Writes file to `Create::$path`.
	 *
	 * @param array $params
	 * @return string|boolean A result string on success of writing the file. If any errors
	 *         occur along the way such as missing information boolean false is returned.
	 */
	protected function _save(array $params = []) {
		$defaults = ['namespace' => null, 'class' => null];
		$params += $defaults;

		if (empty($params['class']) || empty($this->_library['path'])) {
			return false;
		}
		$contents = $this->_template();
		$result = Text::insert($contents, $params);
		$namespace = str_replace($this->_library['prefix'], '\\', $params['namespace']);
		$path = str_replace('\\', '/', "{$namespace}\\{$params['class']}");
		$path = $this->_library['path'] . stristr($path, '/');
		$file = str_replace('//', '/', "{$path}.php");
		$directory = dirname($file);
		$relative = str_replace($this->_library['path'] . '/', "", $file);

		if ((!is_dir($directory)) && !mkdir($directory, 0755, true)) {
			return false;
		}
		if (file_exists($file)) {
			$prompt = "{$relative} already exists. Overwrite?";
			$choices = ['y', 'n'];
			if ($this->in($prompt, compact('choices')) !== 'y') {
				return "{$params['class']} skipped.";
			}
		}

		if (file_put_contents($file, "<?php\n\n{$result}\n\n?>")) {
			return "{$params['class']} created in {$relative}.";
		}
		return false;
	}
}