Commit 054b6f58 by Qiang Xue

Finished controller code generator.

parent 2b711ffd
...@@ -13,35 +13,47 @@ use yii\gii\components\TextDiff; ...@@ -13,35 +13,47 @@ use yii\gii\components\TextDiff;
use yii\helpers\Html; use yii\helpers\Html;
/** /**
* CodeFile represents a code file to be generated.
* *
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class CodeFile extends Object class CodeFile extends Object
{ {
/**
* The code file is new.
*/
const OP_NEW = 'new'; const OP_NEW = 'new';
/**
* The code file already exists, and the new one may need to overwrite it.
*/
const OP_OVERWRITE = 'overwrite'; const OP_OVERWRITE = 'overwrite';
/**
* The new code file and the existing one are identical.
*/
const OP_SKIP = 'skip'; const OP_SKIP = 'skip';
/**
* @var string an ID that uniquely identifies this code file.
*/
public $id; public $id;
/** /**
* @var string the file path that the new code should be saved to. * @var string the file path that the new code should be saved to.
*/ */
public $path; public $path;
/** /**
* @var mixed the newly generated code. If this is null, it means {@link path} * @var string the newly generated code content
* should be treated as a directory.
*/ */
public $content; public $content;
/** /**
* @var string the operation to be performed * @var string the operation to be performed. This can be [[OP_NEW]], [[OP_OVERWRITE]] or [[OP_SKIP]].
*/ */
public $operation; public $operation;
/** /**
* Constructor. * Constructor.
* @param string $path the file path that the new code should be saved to. * @param string $path the file path that the new code should be saved to.
* @param string $content the newly generated code * @param string $content the newly generated code content.
*/ */
public function __construct($path, $content) public function __construct($path, $content)
{ {
...@@ -56,8 +68,8 @@ class CodeFile extends Object ...@@ -56,8 +68,8 @@ class CodeFile extends Object
} }
/** /**
* Saves the code into the file {@link path}. * Saves the code into the file specified by [[path]].
* @return string|boolean * @return string|boolean the error occurred while saving the code file, or true if no error.
*/ */
public function save() public function save()
{ {
...@@ -65,9 +77,9 @@ class CodeFile extends Object ...@@ -65,9 +77,9 @@ class CodeFile extends Object
if ($this->operation === self::OP_NEW) { if ($this->operation === self::OP_NEW) {
$dir = dirname($this->path); $dir = dirname($this->path);
if (!is_dir($dir)) { if (!is_dir($dir)) {
$oldmask = @umask(0); $mask = @umask(0);
$result = @mkdir($dir, $module->newDirMode, true); $result = @mkdir($dir, $module->newDirMode, true);
@umask($oldmask); @umask($mask);
if (!$result) { if (!$result) {
return "Unable to create the directory '$dir'."; return "Unable to create the directory '$dir'.";
} }
...@@ -76,9 +88,9 @@ class CodeFile extends Object ...@@ -76,9 +88,9 @@ class CodeFile extends Object
if (@file_put_contents($this->path, $this->content) === false) { if (@file_put_contents($this->path, $this->content) === false) {
return "Unable to write the file '{$this->path}'."; return "Unable to write the file '{$this->path}'.";
} else { } else {
$oldmask = @umask(0); $mask = @umask(0);
@chmod($this->path, $module->newFileMode); @chmod($this->path, $module->newFileMode);
@umask($oldmask); @umask($mask);
} }
return true; return true;
} }
...@@ -116,18 +128,18 @@ class CodeFile extends Object ...@@ -116,18 +128,18 @@ class CodeFile extends Object
} }
if ($type === 'php') { if ($type === 'php') {
return '<div class="content">' . highlight_string($this->content, true) . '</div>'; return highlight_string($this->content, true);
} elseif(in_array($type, array('txt','js','css'))) { } elseif (!in_array($type, array('jpg', 'gif', 'png', 'exe'))) {
return '<div class="content">' . nl2br(Html::encode($this->content)) . '</div>'; return nl2br(Html::encode($this->content));
} else { } else {
return '<div class="error">Preview is not available for this file type.</div>'; return false;
} }
} }
public function diff() public function diff()
{ {
$type = $this->getType(); $type = strtolower($this->getType());
if (!in_array($type, array('php', 'txt','js','css'))) { if (in_array($type, array('jpg', 'gif', 'png', 'exe'))) {
return false; return false;
} elseif ($this->operation === self::OP_OVERWRITE) { } elseif ($this->operation === self::OP_OVERWRITE) {
return TextDiff::compare(file_get_contents($this->path), $this->content); return TextDiff::compare(file_get_contents($this->path), $this->content);
......
...@@ -10,18 +10,32 @@ namespace yii\gii; ...@@ -10,18 +10,32 @@ namespace yii\gii;
use yii\web\AssetBundle; use yii\web\AssetBundle;
/** /**
* This declares the asset files required by Gii.
*
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class GiiAsset extends AssetBundle class GiiAsset extends AssetBundle
{ {
/**
* @inheritdoc
*/
public $sourcePath = '@yii/gii/assets'; public $sourcePath = '@yii/gii/assets';
/**
* @inheritdoc
*/
public $css = array( public $css = array(
'main.css', 'main.css',
); );
/**
* @inheritdoc
*/
public $js = array( public $js = array(
'gii.js', 'gii.js',
); );
/**
* @inheritdoc
*/
public $depends = array( public $depends = array(
'yii\web\YiiAsset', 'yii\web\YiiAsset',
'yii\bootstrap\BootstrapAsset', 'yii\bootstrap\BootstrapAsset',
......
...@@ -11,11 +11,52 @@ use Yii; ...@@ -11,11 +11,52 @@ use Yii;
use yii\web\HttpException; use yii\web\HttpException;
/** /**
* This is the main module class for the Gii module.
*
* To use Gii, include it as a module in the application configuration like the following:
*
* ~~~
* return array(
* ......
* 'modules' => array(
* 'gii' => array(
* 'class' => 'yii\gii\Module',
* ),
* ),
* )
* ~~~
*
* Because Gii generates new code files on the server, you should only use it on your own
* development machine. To prevent other people from using this module, by default, Gii
* can only be accessed by localhost. You may configure its [[allowedIPs]] property if
* you want to make it accessible on other machines.
*
* With the above configuration, you will be able to access GiiModule in your browser using
* the URL `http://localhost/path/to/index.php?r=gii`
*
* If your application enables [[UrlManager::enablePrettyUrl|pretty URLs]], depending on your
* configuration, you may need to add the following URL rules in your application configuration
* in order to access Gii:
*
* ~~~
* 'rules'=>array(
* 'gii' => 'gii',
* 'gii/<controller>' => 'gii/<controller>',
* 'gii/<controller>/<action>' => 'gii/<controller>/<action>',
* ...
* ),
* ~~~
*
* You can then access Gii via URL: `http://localhost/path/to/index.php/gii`
*
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class Module extends \yii\base\Module class Module extends \yii\base\Module
{ {
/**
* @inheritdoc
*/
public $controllerNamespace = 'yii\gii\controllers'; public $controllerNamespace = 'yii\gii\controllers';
/** /**
* @var array the list of IPs that are allowed to access this module. * @var array the list of IPs that are allowed to access this module.
...@@ -26,9 +67,15 @@ class Module extends \yii\base\Module ...@@ -26,9 +67,15 @@ class Module extends \yii\base\Module
*/ */
public $allowedIPs = array('127.0.0.1', '::1'); public $allowedIPs = array('127.0.0.1', '::1');
/** /**
* @var array a list of path aliases that refer to the directories containing code generators. * @var array|Generator[] a list of generator configurations or instances. The array keys
* The directory referred by a single path alias may contain multiple code generators, each stored * are the generator IDs (e.g. "crud"), and the array elements are the corresponding generator
* under a sub-directory whose name is the generator name. * configurations or the instances.
*
* After the module is initialized, this property will become an array of generator instances
* which are created based on the configurations previously taken by this property.
*
* Newly assigned generators will be merged with the [[coreGenerators()|core ones]], and the former
* takes precedence in case when they have the same generator ID.
*/ */
public $generators = array(); public $generators = array();
/** /**
...@@ -46,7 +93,7 @@ class Module extends \yii\base\Module ...@@ -46,7 +93,7 @@ class Module extends \yii\base\Module
/** /**
* Initializes the gii module. * @inheritdoc
*/ */
public function init() public function init()
{ {
...@@ -56,6 +103,9 @@ class Module extends \yii\base\Module ...@@ -56,6 +103,9 @@ class Module extends \yii\base\Module
} }
} }
/**
* @inheritdoc
*/
public function beforeAction($action) public function beforeAction($action)
{ {
if ($this->checkAccess()) { if ($this->checkAccess()) {
...@@ -65,6 +115,9 @@ class Module extends \yii\base\Module ...@@ -65,6 +115,9 @@ class Module extends \yii\base\Module
} }
} }
/**
* @return boolean whether the module can be accessed by the current user
*/
protected function checkAccess() protected function checkAccess()
{ {
$ip = Yii::$app->getRequest()->getUserIP(); $ip = Yii::$app->getRequest()->getUserIP();
...@@ -76,6 +129,10 @@ class Module extends \yii\base\Module ...@@ -76,6 +129,10 @@ class Module extends \yii\base\Module
return false; return false;
} }
/**
* Returns the list of the core code generator configurations.
* @return array the list of the core code generator configurations.
*/
protected function coreGenerators() protected function coreGenerators()
{ {
return array( return array(
......
...@@ -39,10 +39,10 @@ class DefaultController extends Controller ...@@ -39,10 +39,10 @@ class DefaultController extends Controller
if (isset($_POST['preview']) || isset($_POST['generate'])) { if (isset($_POST['preview']) || isset($_POST['generate'])) {
if ($generator->validate()) { if ($generator->validate()) {
$generator->saveStickyAttributes(); $generator->saveStickyAttributes();
$files = $generator->prepare(); $files = $generator->generate();
if (isset($_POST['generate']) && !empty($_POST['answers'])) { if (isset($_POST['generate']) && !empty($_POST['answers'])) {
$params['results'] = $generator->save($files, (array)$_POST['answers'], $hasError); $params['hasError'] = $generator->save($files, (array)$_POST['answers'], $results);
$params['hasError'] = $hasError; $params['results'] = $results;
} else { } else {
$params['files'] = $files; $params['files'] = $files;
$params['answers'] = isset($_POST['answers']) ? $_POST['answers'] : null; $params['answers'] = isset($_POST['answers']) ? $_POST['answers'] : null;
...@@ -57,9 +57,14 @@ class DefaultController extends Controller ...@@ -57,9 +57,14 @@ class DefaultController extends Controller
{ {
$generator = $this->loadGenerator($id); $generator = $this->loadGenerator($id);
if ($generator->validate()) { if ($generator->validate()) {
foreach ($generator->prepare() as $f) { foreach ($generator->generate() as $f) {
if ($f->id === $file) { if ($f->id === $file) {
return $f->preview(); $content = $f->preview();
if ($content !== false) {
return '<div class="content">' . $content . '</content>';
} else {
return '<div class="error">Preview is not available for this file type.</div>';
}
} }
} }
} }
...@@ -70,7 +75,7 @@ class DefaultController extends Controller ...@@ -70,7 +75,7 @@ class DefaultController extends Controller
{ {
$generator = $this->loadGenerator($id); $generator = $this->loadGenerator($id);
if ($generator->validate()) { if ($generator->validate()) {
foreach ($generator->prepare() as $f) { foreach ($generator->generate() as $f) {
if ($f->id === $file) { if ($f->id === $file) {
return $this->renderPartial('diff', array( return $this->renderPartial('diff', array(
'diff' => $f->diff(), 'diff' => $f->diff(),
......
...@@ -10,43 +10,69 @@ namespace yii\gii\generators\controller; ...@@ -10,43 +10,69 @@ namespace yii\gii\generators\controller;
use Yii; use Yii;
use yii\gii\CodeFile; use yii\gii\CodeFile;
use yii\helpers\Html; use yii\helpers\Html;
use yii\helpers\Inflector;
/** /**
* This generator will generate a controller and one or a few action view files.
* *
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class Generator extends \yii\gii\Generator class Generator extends \yii\gii\Generator
{ {
/**
* @var string the controller ID
*/
public $controller; public $controller;
/**
* @var string the base class of the controller
*/
public $baseClass = 'yii\web\Controller'; public $baseClass = 'yii\web\Controller';
/**
* @var string the namespace of the controller class
*/
public $ns = 'app\controllers'; public $ns = 'app\controllers';
/**
* @var string list of action IDs separated by commas or spaces
*/
public $actions = 'index'; public $actions = 'index';
/**
* @inheritdoc
*/
public function getName() public function getName()
{ {
return 'Controller Generator'; return 'Controller Generator';
} }
/**
* @inheritdoc
*/
public function getDescription() public function getDescription()
{ {
return 'This generator helps you to quickly generate a new controller class, return 'This generator helps you to quickly generate a new controller class,
one or several controller actions and their corresponding views.'; one or several controller actions and their corresponding views.';
} }
/**
* @inheritdoc
*/
public function rules() public function rules()
{ {
return array_merge(parent::rules(), array( return array_merge(parent::rules(), array(
array('controller, actions, baseClass, ns', 'filter', 'filter' => 'trim'), array('controller, actions, baseClass, ns', 'filter', 'filter' => 'trim'),
array('controller, baseClass', 'required'), array('controller, baseClass', 'required'),
array('controller', 'match', 'pattern' => '/^[\w+\\/]*$/', 'message' => 'Only word characters and slashes are allowed.'), array('controller', 'match', 'pattern' => '/^[a-z\\-\\/]*$/', 'message' => 'Only a-z, dashes (-) and slashes (/) are allowed.'),
array('actions', 'match', 'pattern' => '/^\w+[\w\s,]*$/', 'message' => 'Only word characters, spaces and commas are allowed.'), array('actions', 'match', 'pattern' => '/^[a-z\\-,\\s]*$/', 'message' => 'Only a-z, dashes (-), spaces and commas are allowed.'),
array('baseClass', 'match', 'pattern' => '/^[a-zA-Z_][\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'), array('baseClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'),
array('baseClass', 'validateReservedWord'), array('baseClass', 'validateReservedWord'),
array('ns', 'match', 'pattern' => '/^[a-zA-Z_][\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'), array('ns', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'),
)); ));
} }
/**
* @inheritdoc
*/
public function attributeLabels() public function attributeLabels()
{ {
return array( return array(
...@@ -57,6 +83,9 @@ class Generator extends \yii\gii\Generator ...@@ -57,6 +83,9 @@ class Generator extends \yii\gii\Generator
); );
} }
/**
* @inheritdoc
*/
public function requiredTemplates() public function requiredTemplates()
{ {
return array( return array(
...@@ -65,26 +94,40 @@ class Generator extends \yii\gii\Generator ...@@ -65,26 +94,40 @@ class Generator extends \yii\gii\Generator
); );
} }
/**
* @inheritdoc
*/
public function stickyAttributes() public function stickyAttributes()
{ {
return array('ns', 'baseClass'); return array('ns', 'baseClass');
} }
/**
* @inheritdoc
*/
public function hints() public function hints()
{ {
return array( return array(
'controller' => 'Controller ID should be in lower case and may contain module ID(s). For example: 'controller' => 'Controller ID should be in lower case and may contain module ID(s) separated by slashes. For example:
<ul> <ul>
<li><code>order</code> generates <code>OrderController.php</code></li> <li><code>order</code> generates <code>OrderController.php</code></li>
<li><code>order-item</code> generates <code>OrderItemController.php</code></li> <li><code>order-item</code> generates <code>OrderItemController.php</code></li>
<li><code>admin/user</code> generates <code>UserController.php</code> within the <code>admin</code> module.</li> <li><code>admin/user</code> generates <code>UserController.php</code> within the <code>admin</code> module.</li>
</ul>', </ul>',
'actions' => 'Provide one or multiple action IDs to generate empty action method(s) in the controller. Separate multiple action IDs with commas or spaces.', 'actions' => 'Provide one or multiple action IDs to generate empty action method(s) in the controller. Separate multiple action IDs with commas or spaces.
Action IDs should be in lower case. For example:
<ul>
<li><code>index</code> generates <code>actionIndex()</code></li>
<li><code>create-order</code> generates <code>actionCreateOrder()</code></li>
</ul>',
'ns' => 'This is the namespace that the new controller class will should use.', 'ns' => 'This is the namespace that the new controller class will should use.',
'baseClass' => 'This is the class that the new controller class will extend from. Please make sure the class exists and can be autoloaded.', 'baseClass' => 'This is the class that the new controller class will extend from. Please make sure the class exists and can be autoloaded.',
); );
} }
/**
* @inheritdoc
*/
public function successMessage() public function successMessage()
{ {
$actions = $this->getActionIDs(); $actions = $this->getActionIDs();
...@@ -97,7 +140,10 @@ class Generator extends \yii\gii\Generator ...@@ -97,7 +140,10 @@ class Generator extends \yii\gii\Generator
return "The controller has been generated successfully. You may $link."; return "The controller has been generated successfully. You may $link.";
} }
public function prepare() /**
* @inheritdoc
*/
public function generate()
{ {
$files = array(); $files = array();
...@@ -105,19 +151,23 @@ class Generator extends \yii\gii\Generator ...@@ -105,19 +151,23 @@ class Generator extends \yii\gii\Generator
$files[] = new CodeFile( $files[] = new CodeFile(
$this->getControllerFile(), $this->getControllerFile(),
$this->generateCode($templatePath . '/controller.php') $this->render($templatePath . '/controller.php')
); );
foreach ($this->getActionIDs() as $action) { foreach ($this->getActionIDs() as $action) {
$files[] = new CodeFile( $files[] = new CodeFile(
$this->getViewFile($action), $this->getViewFile($action),
$this->generateCode($templatePath . '/view.php', array('action' => $action)) $this->render($templatePath . '/view.php', array('action' => $action))
); );
} }
return $files; return $files;
} }
/**
* Normalizes [[actions]] into an array of action IDs.
* @return array an array of action IDs entered by the user
*/
public function getActionIDs() public function getActionIDs()
{ {
$actions = array_unique(preg_split('/[\s,]+/', $this->actions, -1, PREG_SPLIT_NO_EMPTY)); $actions = array_unique(preg_split('/[\s,]+/', $this->actions, -1, PREG_SPLIT_NO_EMPTY));
...@@ -127,10 +177,15 @@ class Generator extends \yii\gii\Generator ...@@ -127,10 +177,15 @@ class Generator extends \yii\gii\Generator
public function getControllerClass() public function getControllerClass()
{ {
return Inflector::id2camel($this->getControllerID()) . 'Controller';
}
public function getControllerID()
{
if (($pos = strrpos($this->controller, '/')) !== false) { if (($pos = strrpos($this->controller, '/')) !== false) {
return ucfirst(substr($this->controller, $pos + 1)) . 'Controller'; return substr($this->controller, $pos + 1);
} else { } else {
return ucfirst($this->controller) . 'Controller'; return $this->controller;
} }
} }
...@@ -145,36 +200,15 @@ class Generator extends \yii\gii\Generator ...@@ -145,36 +200,15 @@ class Generator extends \yii\gii\Generator
return Yii::$app; return Yii::$app;
} }
public function getControllerID()
{
if ($this->getModule() !== Yii::$app) {
$id = substr($this->controller, strpos($this->controller, '/') + 1);
} else {
$id = $this->controller;
}
if (($pos = strrpos($id, '/')) !== false) {
$id[$pos + 1] = strtolower($id[$pos + 1]);
} else {
$id[0] = strtolower($id[0]);
}
return $id;
}
public function getControllerFile() public function getControllerFile()
{ {
$module = $this->getModule(); $module = $this->getModule();
$id = $this->getControllerID(); return $module->getControllerPath() . '/' . $this->getControllerClass() . '.php';
if (($pos = strrpos($id, '/')) !== false) {
$id[$pos + 1] = strtoupper($id[$pos + 1]);
} else {
$id[0] = strtoupper($id[0]);
}
return $module->getControllerPath() . '/' . $id . 'Controller.php';
} }
public function getViewFile($action) public function getViewFile($action)
{ {
$module=$this->getModule(); $module = $this->getModule();
return $module->getViewPath().'/'.$this->getControllerID().'/'.$action.'.php'; return $module->getViewPath() . '/' . $this->getControllerID() . '/' . $action . '.php';
} }
} }
<?php <?php
use yii\helpers\Inflector;
/** /**
* This is the template for generating a controller class file. * This is the template for generating a controller class file.
* *
...@@ -12,10 +15,10 @@ ...@@ -12,10 +15,10 @@
namespace <?php echo $generator->ns; ?>; namespace <?php echo $generator->ns; ?>;
<?php endif; ?> <?php endif; ?>
class <?php echo $generator->controllerClass; ?> extends <?php echo '\\' . ltrim($generator->baseClass, '\\') . "\n"; ?> class <?php echo $generator->getControllerClass(); ?> extends <?php echo '\\' . trim($generator->baseClass, '\\') . "\n"; ?>
{ {
<?php foreach($generator->getActionIDs() as $action): ?> <?php foreach($generator->getActionIDs() as $action): ?>
public function action<?php echo ucfirst($action); ?>() public function action<?php echo Inflector::id2camel($action); ?>()
{ {
return $this->render('<?php echo $action; ?>'); return $this->render('<?php echo $action; ?>');
} }
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
*/ */
<?php echo "?>"; ?> <?php echo "?>"; ?>
<h1><?php echo $action; ?></h1> <h1><?php echo $generator->getControllerID() . '/' . $action; ?></h1>
<p> <p>
You may change the content of this page by modifying You may change the content of this page by modifying
......
...@@ -24,4 +24,12 @@ class Generator extends \yii\gii\Generator ...@@ -24,4 +24,12 @@ class Generator extends \yii\gii\Generator
return 'This generator generates a controller and views that implement CRUD (Create, Read, Update, Delete) return 'This generator generates a controller and views that implement CRUD (Create, Read, Update, Delete)
operations for the specified data model.'; operations for the specified data model.';
} }
/**
* @inheritdoc
*/
public function generate()
{
return array();
}
} }
...@@ -23,4 +23,12 @@ class Generator extends \yii\gii\Generator ...@@ -23,4 +23,12 @@ class Generator extends \yii\gii\Generator
{ {
return 'This generator generates a view script file that displays a form to collect input for the specified model class.'; return 'This generator generates a view script file that displays a form to collect input for the specified model class.';
} }
/**
* @inheritdoc
*/
public function generate()
{
return array();
}
} }
...@@ -23,4 +23,12 @@ class Generator extends \yii\gii\Generator ...@@ -23,4 +23,12 @@ class Generator extends \yii\gii\Generator
{ {
return 'This generator generates a model class for the specified database table.'; return 'This generator generates a model class for the specified database table.';
} }
/**
* @inheritdoc
*/
public function generate()
{
return array();
}
} }
...@@ -23,4 +23,12 @@ class Generator extends \yii\gii\Generator ...@@ -23,4 +23,12 @@ class Generator extends \yii\gii\Generator
{ {
return 'This generator helps you to generate the skeleton code needed by a Yii module.'; return 'This generator helps you to generate the skeleton code needed by a Yii module.';
} }
/**
* @inheritdoc
*/
public function generate()
{
return array();
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment