Commit 054b6f58 by Qiang Xue

Finished controller code generator.

parent 2b711ffd
......@@ -13,35 +13,47 @@ use yii\gii\components\TextDiff;
use yii\helpers\Html;
/**
* CodeFile represents a code file to be generated.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class CodeFile extends Object
{
/**
* The code file is new.
*/
const OP_NEW = 'new';
/**
* The code file already exists, and the new one may need to overwrite it.
*/
const OP_OVERWRITE = 'overwrite';
/**
* The new code file and the existing one are identical.
*/
const OP_SKIP = 'skip';
/**
* @var string an ID that uniquely identifies this code file.
*/
public $id;
/**
* @var string the file path that the new code should be saved to.
*/
public $path;
/**
* @var mixed the newly generated code. If this is null, it means {@link path}
* should be treated as a directory.
* @var string the newly generated code 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;
/**
* Constructor.
* @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)
{
......@@ -56,8 +68,8 @@ class CodeFile extends Object
}
/**
* Saves the code into the file {@link path}.
* @return string|boolean
* Saves the code into the file specified by [[path]].
* @return string|boolean the error occurred while saving the code file, or true if no error.
*/
public function save()
{
......@@ -65,9 +77,9 @@ class CodeFile extends Object
if ($this->operation === self::OP_NEW) {
$dir = dirname($this->path);
if (!is_dir($dir)) {
$oldmask = @umask(0);
$mask = @umask(0);
$result = @mkdir($dir, $module->newDirMode, true);
@umask($oldmask);
@umask($mask);
if (!$result) {
return "Unable to create the directory '$dir'.";
}
......@@ -76,9 +88,9 @@ class CodeFile extends Object
if (@file_put_contents($this->path, $this->content) === false) {
return "Unable to write the file '{$this->path}'.";
} else {
$oldmask = @umask(0);
$mask = @umask(0);
@chmod($this->path, $module->newFileMode);
@umask($oldmask);
@umask($mask);
}
return true;
}
......@@ -116,18 +128,18 @@ class CodeFile extends Object
}
if ($type === 'php') {
return '<div class="content">' . highlight_string($this->content, true) . '</div>';
} elseif(in_array($type, array('txt','js','css'))) {
return '<div class="content">' . nl2br(Html::encode($this->content)) . '</div>';
return highlight_string($this->content, true);
} elseif (!in_array($type, array('jpg', 'gif', 'png', 'exe'))) {
return nl2br(Html::encode($this->content));
} else {
return '<div class="error">Preview is not available for this file type.</div>';
return false;
}
}
public function diff()
{
$type = $this->getType();
if (!in_array($type, array('php', 'txt','js','css'))) {
$type = strtolower($this->getType());
if (in_array($type, array('jpg', 'gif', 'png', 'exe'))) {
return false;
} elseif ($this->operation === self::OP_OVERWRITE) {
return TextDiff::compare(file_get_contents($this->path), $this->content);
......
......@@ -10,18 +10,32 @@ namespace yii\gii;
use yii\web\AssetBundle;
/**
* This declares the asset files required by Gii.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class GiiAsset extends AssetBundle
{
/**
* @inheritdoc
*/
public $sourcePath = '@yii/gii/assets';
/**
* @inheritdoc
*/
public $css = array(
'main.css',
);
/**
* @inheritdoc
*/
public $js = array(
'gii.js',
);
/**
* @inheritdoc
*/
public $depends = array(
'yii\web\YiiAsset',
'yii\bootstrap\BootstrapAsset',
......
......@@ -11,11 +11,52 @@ use Yii;
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>
* @since 2.0
*/
class Module extends \yii\base\Module
{
/**
* @inheritdoc
*/
public $controllerNamespace = 'yii\gii\controllers';
/**
* @var array the list of IPs that are allowed to access this module.
......@@ -26,9 +67,15 @@ class Module extends \yii\base\Module
*/
public $allowedIPs = array('127.0.0.1', '::1');
/**
* @var array a list of path aliases that refer to the directories containing code generators.
* The directory referred by a single path alias may contain multiple code generators, each stored
* under a sub-directory whose name is the generator name.
* @var array|Generator[] a list of generator configurations or instances. The array keys
* are the generator IDs (e.g. "crud"), and the array elements are the corresponding generator
* 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();
/**
......@@ -46,7 +93,7 @@ class Module extends \yii\base\Module
/**
* Initializes the gii module.
* @inheritdoc
*/
public function init()
{
......@@ -56,6 +103,9 @@ class Module extends \yii\base\Module
}
}
/**
* @inheritdoc
*/
public function beforeAction($action)
{
if ($this->checkAccess()) {
......@@ -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()
{
$ip = Yii::$app->getRequest()->getUserIP();
......@@ -76,6 +129,10 @@ class Module extends \yii\base\Module
return false;
}
/**
* Returns the list of the core code generator configurations.
* @return array the list of the core code generator configurations.
*/
protected function coreGenerators()
{
return array(
......
......@@ -39,10 +39,10 @@ class DefaultController extends Controller
if (isset($_POST['preview']) || isset($_POST['generate'])) {
if ($generator->validate()) {
$generator->saveStickyAttributes();
$files = $generator->prepare();
$files = $generator->generate();
if (isset($_POST['generate']) && !empty($_POST['answers'])) {
$params['results'] = $generator->save($files, (array)$_POST['answers'], $hasError);
$params['hasError'] = $hasError;
$params['hasError'] = $generator->save($files, (array)$_POST['answers'], $results);
$params['results'] = $results;
} else {
$params['files'] = $files;
$params['answers'] = isset($_POST['answers']) ? $_POST['answers'] : null;
......@@ -57,9 +57,14 @@ class DefaultController extends Controller
{
$generator = $this->loadGenerator($id);
if ($generator->validate()) {
foreach ($generator->prepare() as $f) {
foreach ($generator->generate() as $f) {
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
{
$generator = $this->loadGenerator($id);
if ($generator->validate()) {
foreach ($generator->prepare() as $f) {
foreach ($generator->generate() as $f) {
if ($f->id === $file) {
return $this->renderPartial('diff', array(
'diff' => $f->diff(),
......
......@@ -10,43 +10,69 @@ namespace yii\gii\generators\controller;
use Yii;
use yii\gii\CodeFile;
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>
* @since 2.0
*/
class Generator extends \yii\gii\Generator
{
/**
* @var string the controller ID
*/
public $controller;
/**
* @var string the base class of the controller
*/
public $baseClass = 'yii\web\Controller';
/**
* @var string the namespace of the controller class
*/
public $ns = 'app\controllers';
/**
* @var string list of action IDs separated by commas or spaces
*/
public $actions = 'index';
/**
* @inheritdoc
*/
public function getName()
{
return 'Controller Generator';
}
/**
* @inheritdoc
*/
public function getDescription()
{
return 'This generator helps you to quickly generate a new controller class,
one or several controller actions and their corresponding views.';
}
/**
* @inheritdoc
*/
public function rules()
{
return array_merge(parent::rules(), array(
array('controller, actions, baseClass, ns', 'filter', 'filter' => 'trim'),
array('controller, baseClass', 'required'),
array('controller', 'match', 'pattern' => '/^[\w+\\/]*$/', 'message' => 'Only word characters and slashes are allowed.'),
array('actions', 'match', 'pattern' => '/^\w+[\w\s,]*$/', 'message' => 'Only word characters, spaces and commas are allowed.'),
array('baseClass', 'match', 'pattern' => '/^[a-zA-Z_][\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'),
array('controller', 'match', 'pattern' => '/^[a-z\\-\\/]*$/', 'message' => 'Only a-z, dashes (-) and slashes (/) are allowed.'),
array('actions', 'match', 'pattern' => '/^[a-z\\-,\\s]*$/', 'message' => 'Only a-z, dashes (-), spaces and commas are allowed.'),
array('baseClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'),
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()
{
return array(
......@@ -57,6 +83,9 @@ class Generator extends \yii\gii\Generator
);
}
/**
* @inheritdoc
*/
public function requiredTemplates()
{
return array(
......@@ -65,26 +94,40 @@ class Generator extends \yii\gii\Generator
);
}
/**
* @inheritdoc
*/
public function stickyAttributes()
{
return array('ns', 'baseClass');
}
/**
* @inheritdoc
*/
public function hints()
{
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>
<li><code>order</code> generates <code>OrderController.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>
</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.',
'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()
{
$actions = $this->getActionIDs();
......@@ -97,7 +140,10 @@ class Generator extends \yii\gii\Generator
return "The controller has been generated successfully. You may $link.";
}
public function prepare()
/**
* @inheritdoc
*/
public function generate()
{
$files = array();
......@@ -105,19 +151,23 @@ class Generator extends \yii\gii\Generator
$files[] = new CodeFile(
$this->getControllerFile(),
$this->generateCode($templatePath . '/controller.php')
$this->render($templatePath . '/controller.php')
);
foreach ($this->getActionIDs() as $action) {
$files[] = new CodeFile(
$this->getViewFile($action),
$this->generateCode($templatePath . '/view.php', array('action' => $action))
$this->render($templatePath . '/view.php', array('action' => $action))
);
}
return $files;
}
/**
* Normalizes [[actions]] into an array of action IDs.
* @return array an array of action IDs entered by the user
*/
public function getActionIDs()
{
$actions = array_unique(preg_split('/[\s,]+/', $this->actions, -1, PREG_SPLIT_NO_EMPTY));
......@@ -127,10 +177,15 @@ class Generator extends \yii\gii\Generator
public function getControllerClass()
{
return Inflector::id2camel($this->getControllerID()) . 'Controller';
}
public function getControllerID()
{
if (($pos = strrpos($this->controller, '/')) !== false) {
return ucfirst(substr($this->controller, $pos + 1)) . 'Controller';
return substr($this->controller, $pos + 1);
} else {
return ucfirst($this->controller) . 'Controller';
return $this->controller;
}
}
......@@ -145,36 +200,15 @@ class Generator extends \yii\gii\Generator
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()
{
$module = $this->getModule();
$id = $this->getControllerID();
if (($pos = strrpos($id, '/')) !== false) {
$id[$pos + 1] = strtoupper($id[$pos + 1]);
} else {
$id[0] = strtoupper($id[0]);
}
return $module->getControllerPath() . '/' . $id . 'Controller.php';
return $module->getControllerPath() . '/' . $this->getControllerClass() . '.php';
}
public function getViewFile($action)
{
$module=$this->getModule();
return $module->getViewPath().'/'.$this->getControllerID().'/'.$action.'.php';
$module = $this->getModule();
return $module->getViewPath() . '/' . $this->getControllerID() . '/' . $action . '.php';
}
}
<?php
use yii\helpers\Inflector;
/**
* This is the template for generating a controller class file.
*
......@@ -12,10 +15,10 @@
namespace <?php echo $generator->ns; ?>;
<?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): ?>
public function action<?php echo ucfirst($action); ?>()
public function action<?php echo Inflector::id2camel($action); ?>()
{
return $this->render('<?php echo $action; ?>');
}
......
......@@ -13,7 +13,7 @@
*/
<?php echo "?>"; ?>
<h1><?php echo $action; ?></h1>
<h1><?php echo $generator->getControllerID() . '/' . $action; ?></h1>
<p>
You may change the content of this page by modifying
......
......@@ -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)
operations for the specified data model.';
}
/**
* @inheritdoc
*/
public function generate()
{
return array();
}
}
......@@ -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.';
}
/**
* @inheritdoc
*/
public function generate()
{
return array();
}
}
......@@ -23,4 +23,12 @@ class Generator extends \yii\gii\Generator
{
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
{
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