Commit a9991d4e by Qiang Xue

CRUD WIP

parent 746a250e
......@@ -338,10 +338,9 @@ abstract class Module extends Component
/**
* Retrieves the named module.
* @param string $id module ID (case-sensitive)
* @param string $id module ID (case-sensitive).
* @param boolean $load whether to load the module if it is not yet loaded.
* @return Module|null the module instance, null if the module
* does not exist.
* @return Module|null the module instance, null if the module does not exist.
* @see hasModule()
*/
public function getModule($id, $load = true)
......
......@@ -325,6 +325,28 @@ abstract class Generator extends Model
}
/**
* An inline validator that checks if the attribute value refers to a valid namespaced class name.
* The validator will check if the directory containing the new class file exist or not.
* @param string $attribute the attribute being validated
* @param array $params the validation options
*/
public function validateNewClass($attribute, $params)
{
$class = ltrim($this->$attribute, '\\');
if (($pos = strrpos($class, '\\')) === false) {
$this->addError($attribute, "The class name must contain fully qualified namespace name.");
} else {
$ns = substr($class, 0, $pos);
$path = Yii::getAlias('@' . str_replace('\\', '/', $ns), false);
if ($path === false) {
$this->addError($attribute, "The class namespace is invalid: $ns");
} elseif (!is_dir($path)) {
$this->addError($attribute, "Please make sure the directory containing this class exists: $path");
}
}
}
/**
* @param string $value the attribute to be validated
* @return boolean whether the value is a reserved PHP keyword.
*/
......
......@@ -81,6 +81,11 @@ yii.gii = (function ($) {
$('#model-generator .field-generator-modelclass').toggle($(this).val().indexOf('*') == -1);
}).change();
// crud generator: hide Search Model Class input if search is not enabled
$('#crud-generator #generator-enablesearch').on('change', function () {
$('#crud-generator .field-generator-searchmodelclass').toggle(this.checked);
}).change();
// hide Generate button if any input is changed
$('.default-view .form-group input,select,textarea').change(function () {
$('.default-view-results,.default-view-files').hide();
......
......@@ -22,7 +22,8 @@ use yii\web\Controller;
class Generator extends \yii\gii\Generator
{
public $modelClass;
public $controllerID;
public $moduleID;
public $controllerClass;
public $baseControllerClass = 'yii\web\Controller';
public $indexWidgetType = 'grid';
public $enableSearch = true;
......@@ -42,16 +43,18 @@ class Generator extends \yii\gii\Generator
public function rules()
{
return array_merge(parent::rules(), array(
array('modelClass, searchModelClass, controllerID, baseControllerClass', 'filter', 'filter' => 'trim'),
array('modelClass, controllerID, baseControllerClass', 'required'),
array('modelClass, searchModelClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'),
array('moduleID, controllerClass, modelClass, searchModelClass, baseControllerClass', 'filter', 'filter' => 'trim'),
array('modelClass, controllerClass, baseControllerClass, indexWidgetType', 'required'),
array('modelClass, controllerClass, baseControllerClass, searchModelClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'),
array('modelClass', 'validateClass', 'params' => array('extends' => ActiveRecord::className())),
array('controllerID', 'match', 'pattern' => '/^[a-z\\-\\/]*$/', 'message' => 'Only a-z, dashes (-) and slashes (/) are allowed.'),
array('baseControllerClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'),
array('baseControllerClass', 'validateClass', 'params' => array('extends' => Controller::className())),
array('controllerClass', 'match', 'pattern' => '/Controller$/', 'message' => 'Controller class name must be suffixed with "Controller".'),
array('controllerClass, searchModelClass', 'validateNewClass'),
array('enableSearch', 'boolean'),
array('indexWidgetType', 'in', 'range' => array('grid', 'list')),
array('modelClass', 'validateModelClass'),
array('searchModelClass', 'validateSearchModelClass'),
array('moduleID', 'validateModuleID'),
));
}
......@@ -59,7 +62,8 @@ class Generator extends \yii\gii\Generator
{
return array_merge(parent::attributeLabels(), array(
'modelClass' => 'Model Class',
'controllerID' => 'Controller ID',
'moduleID' => 'Module ID',
'controllerClass' => 'Controller Class',
'baseControllerClass' => 'Base Controller Class',
'indexWidgetType' => 'Widget Used in Index Page',
'enableSearch' => 'Enable Search',
......@@ -75,15 +79,12 @@ class Generator extends \yii\gii\Generator
return array(
'modelClass' => 'This is the ActiveRecord class associated with the table that CRUD will be built upon.
You should provide a fully qualified class name, e.g., <code>app\models\Post</code>.',
'controllerID' => 'CRUD controllers are often named after the model class name that they are dealing with.
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>',
'controllerClass' => 'This is the name of the controller class to be generated. You should
provide a fully qualified namespaced class, .e.g, <code>app\controllers\PostController</code>.',
'baseControllerClass' => 'This is the class that the new CRUD controller class will extend from.
You should provide a fully qualified class name, e.g., <code>yii\web\Controller</code>.',
'moduleID' => 'This is the ID of the module that the generated controller will belong to.
If not set, it means the controller will belong to the application.',
'indexWidgetType' => 'This is the widget type to be used in the index page to display list of the models.
You may choose either <code>GridView</code> or <code>ListView</code>',
'enableSearch' => 'Whether to enable the search functionality on the index page. When search is enabled,
......@@ -106,7 +107,17 @@ class Generator extends \yii\gii\Generator
*/
public function stickyAttributes()
{
return array('baseControllerClass', 'indexWidgetType', 'enableSearch');
return array('baseControllerClass', 'moduleID', 'indexWidgetType', 'enableSearch');
}
public function validateModelClass()
{
/** @var ActiveRecord $class */
$class = $this->modelClass;
$pk = $class::primaryKey();
if (empty($pk)) {
$this->addError('modelClass', "The table associated with $class must have primary key(s).");
}
}
public function validateSearchModelClass()
......@@ -116,6 +127,16 @@ class Generator extends \yii\gii\Generator
}
}
public function validateModuleID()
{
if (!empty($this->moduleID)) {
$module = Yii::$app->getModule($this->moduleID);
if ($module === null) {
$this->addError('moduleID', "Module '{$this->moduleID}' does not exist.");
}
}
}
/**
* @inheritdoc
*/
......@@ -130,6 +151,9 @@ class Generator extends \yii\gii\Generator
$templatePath = $this->getTemplatePath() . '/views';
foreach (scandir($templatePath) as $file) {
if (!in_array($file, array('create.php', 'update.php', 'view.php'))) {
continue;
}
if (is_file($templatePath . '/' . $file) && pathinfo($file, PATHINFO_EXTENSION) === 'php') {
$files[] = new CodeFile("$viewPath/$file", $this->render("views/$file"));
}
......@@ -142,39 +166,14 @@ class Generator extends \yii\gii\Generator
return $files;
}
/**
* @return string the controller class name without the namespace part.
*/
public function getControllerClass()
{
return Inflector::id2camel($this->getControllerID()) . 'Controller';
}
/**
* @return string the controller ID (without the module ID prefix)
*/
public function getControllerID()
{
if (($pos = strrpos($this->controllerID, '/')) !== false) {
return substr($this->controllerID, $pos + 1);
} else {
return $this->controllerID;
}
}
/**
* @return \yii\base\Module the module that the new controller belongs to
*/
public function getModule()
{
if (($pos = strpos($this->controllerID, '/')) !== false) {
$id = substr($this->controllerID, 0, $pos);
if (($module = Yii::$app->getModule($id)) !== null) {
return $module;
}
}
return Yii::$app;
$pos = strrpos($this->controllerClass, '\\');
$class = substr(substr($this->controllerClass, $pos + 1), 0, -10);
return Inflector::camel2id($class);
}
/**
......@@ -182,8 +181,7 @@ class Generator extends \yii\gii\Generator
*/
public function getControllerFile()
{
$module = $this->getModule();
return $module->getControllerPath() . '/' . $this->getControllerClass() . '.php';
return Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->controllerClass, '\\')) . '.php');
}
/**
......@@ -191,7 +189,20 @@ class Generator extends \yii\gii\Generator
*/
public function getViewPath()
{
$module = $this->getModule();
$module = empty($this->moduleID) ? Yii::$app : Yii::$app->getModule($this->moduleID);
return $module->getViewPath() . '/' . $this->getControllerID() ;
}
public function getNameAttribute()
{
/** @var \yii\db\ActiveRecord $class */
$class = $this->modelClass;
foreach ($class::getTableSchema()->columnNames as $name) {
if (!strcasecmp($name, 'name') || !strcasecmp($name, 'title')) {
return $name;
}
}
$pk = $class::primaryKey();
return $pk[0];
}
}
......@@ -6,11 +6,12 @@
*/
echo $form->field($generator, 'modelClass');
echo $form->field($generator, 'controllerID');
echo $form->field($generator, 'controllerClass');
echo $form->field($generator, 'baseControllerClass');
echo $form->field($generator, 'moduleID');
echo $form->field($generator, 'enableSearch')->checkbox();
echo $form->field($generator, 'searchModelClass');
echo $form->field($generator, 'indexWidgetType')->dropDownList(array(
'grid' => 'GridView',
'list' => 'ListView',
));
echo $form->field($generator, 'enableSearch')->checkbox();
echo $form->field($generator, 'searchModelClass');
<?php
use yii\helpers\StringHelper;
/**
* This is the template for generating a controller class file for CRUD feature.
* The following variables are available in this template:
* - $this: the CrudCode object
* This is the template for generating a CRUD controller class file.
*
* @var yii\base\View $this
* @var yii\gii\generators\crud\Generator $generator
*/
$pos = strrpos($generator->controllerClass, '\\');
$ns = ltrim(substr($generator->controllerClass, 0, $pos), '\\');
$controllerClass = substr($generator->controllerClass, $pos + 1);
$pos = strrpos($generator->modelClass, '\\');
$modelClass = $pos === false ? $generator->modelClass : substr($generator->modelClass, $pos + 1);
/** @var \yii\db\ActiveRecord $class */
$class = $generator->modelClass;
$pks = $class::primaryKey();
$schema = $class::getTableSchema();
if (count($pks) === 1) {
$ids = '$id';
$params = "array('id' => \$model->{$pks[0]})";
$paramComments = '@param ' . $schema->columns[$pks[0]]->phpType . ' $id';
} else {
$ids = '$' . implode(', $', $pks);
$params = array();
$paramComments = array();
foreach ($pks as $pk) {
$paramComments[] = '@param ' . $schema->columns[$pk]->phpType . ' $' . $pk;
$params[] = "'$pk' => \$model->$pk";
}
$params = implode(', ', $params);
$paramComments = implode("\n\t * ", $paramComments);
}
echo "<?php\n";
?>
<?php echo "<?php\n"; ?>
class <?php echo $this->controllerClass; ?> extends <?php echo $this->baseControllerClass."\n"; ?>
{
/**
* @var string the default layout for the views. Defaults to '//layouts/column2', meaning
* using two-column layout. See 'protected/views/layouts/column2.php'.
*/
public $layout='//layouts/column2';
namespace <?php echo $ns; ?>;
/**
* @return array action filters
*/
public function filters()
{
return array(
'accessControl', // perform access control for CRUD operations
'postOnly + delete', // we only allow deletion via POST request
);
}
use <?php echo ltrim($generator->modelClass, '\\'); ?>;
use yii\data\ActiveDataProvider;
use <?php echo ltrim($generator->baseControllerClass, '\\'); ?>;
use yii\web\HttpException;
/**
* Specifies the access control rules.
* This method is used by the 'accessControl' filter.
* @return array access control rules
/**
* <?php echo $controllerClass; ?> implements the CRUD actions for <?php echo $modelClass; ?> model.
*/
public function accessRules()
{
return array(
array('allow', // allow all users to perform 'index' and 'view' actions
'actions'=>array('index','view'),
'users'=>array('*'),
),
array('allow', // allow authenticated user to perform 'create' and 'update' actions
'actions'=>array('create','update'),
'users'=>array('@'),
),
array('allow', // allow admin user to perform 'admin' and 'delete' actions
'actions'=>array('admin','delete'),
'users'=>array('admin'),
),
array('deny', // deny all users
'users'=>array('*'),
),
);
}
class <?php echo $controllerClass; ?> extends <?php echo StringHelper::basename($generator->baseControllerClass) . "\n"; ?>
{
/**
* Displays a particular model.
* @param integer $id the ID of the model to be displayed
* Displays a single <?php echo $modelClass; ?> model.
* <?php echo $paramComments . "\n"; ?>
* @return mixed
*/
public function actionView($id)
public function actionView(<?php echo $ids; ?>)
{
$this->render('view',array(
'model'=>$this->loadModel($id),
return $this->render('view', array(
'model' => $this->findModel(<?php echo $ids; ?>),
));
}
/**
* Creates a new model.
* Creates a new <?php echo $modelClass; ?> model.
* If creation is successful, the browser will be redirected to the 'view' page.
* @return mixed
*/
public function actionCreate()
{
$model=new <?php echo $this->modelClass; ?>;
// Uncomment the following line if AJAX validation is needed
// $this->performAjaxValidation($model);
if(isset($_POST['<?php echo $this->modelClass; ?>']))
{
$model->attributes=$_POST['<?php echo $this->modelClass; ?>'];
if($model->save())
$this->redirect(array('view','id'=>$model-><?php echo $this->tableSchema->primaryKey; ?>));
}
$model = new <?php echo $modelClass; ?>;
$this->render('create',array(
'model'=>$model,
if ($model->load($_POST) && $model->save()) {
return $this->redirect(array('view', <?php echo $params; ?>));
} else {
return $this->render('create', array(
'model' => $model,
));
}
}
/**
* Updates a particular model.
* Updates an existing <?php echo $modelClass; ?> model.
* If update is successful, the browser will be redirected to the 'view' page.
* @param integer $id the ID of the model to be updated
* <?php echo $paramComments . "\n"; ?>
* @return mixed
*/
public function actionUpdate($id)
public function actionUpdate(<?php echo $ids; ?>)
{
$model=$this->loadModel($id);
$model = $this->findModel(<?php echo $ids; ?>);
// Uncomment the following line if AJAX validation is needed
// $this->performAjaxValidation($model);
if(isset($_POST['<?php echo $this->modelClass; ?>']))
{
$model->attributes=$_POST['<?php echo $this->modelClass; ?>'];
if($model->save())
$this->redirect(array('view','id'=>$model-><?php echo $this->tableSchema->primaryKey; ?>));
}
$this->render('update',array(
'model'=>$model,
if ($model->load($_POST) && $model->save()) {
return $this->redirect(array('view', <?php echo $params; ?>));
} else {
return $this->render('update', array(
'model' => $model,
));
}
}
/**
* Deletes a particular model.
* If deletion is successful, the browser will be redirected to the 'admin' page.
* @param integer $id the ID of the model to be deleted
* Deletes an existing <?php echo $modelClass; ?> model.
* If deletion is successful, the browser will be redirected to the 'index' page.
* <?php echo $paramComments . "\n"; ?>
* @return mixed
*/
public function actionDelete($id)
public function actionDelete(<?php echo $ids; ?>)
{
$this->loadModel($id)->delete();
// if AJAX request (triggered by deletion via admin grid view), we should not redirect the browser
if(!isset($_GET['ajax']))
$this->redirect(isset($_POST['returnUrl']) ? $_POST['returnUrl'] : array('admin'));
$this->findModel(<?php echo $ids; ?>)->delete();
return $this->redirect(array('index'));
}
/**
* Lists all models.
* @return mixed
*/
public function actionIndex()
{
$dataProvider=new CActiveDataProvider('<?php echo $this->modelClass; ?>');
$this->render('index',array(
'dataProvider'=>$dataProvider,
$dataProvider = new ActiveDataProvider('<?php echo $modelClass; ?>');
return $this->render('index', array(
'dataProvider' => $dataProvider,
));
}
/**
* Manages all models.
* Returns the data model based on its primary key value.
* If the data model is not found, a 404 HTTP exception will be thrown.
* <?php echo $paramComments . "\n"; ?>
* @return <?php echo $modelClass; ?> the loaded model
* @throws HttpException if the model cannot be found
*/
public function actionAdmin()
protected function findModel(<?php echo $ids; ?>)
{
$model=new <?php echo $this->modelClass; ?>('search');
$model->unsetAttributes(); // clear any default values
if(isset($_GET['<?php echo $this->modelClass; ?>']))
$model->attributes=$_GET['<?php echo $this->modelClass; ?>'];
$this->render('admin',array(
'model'=>$model,
));
}
/**
* Returns the data model based on the primary key given in the GET variable.
* If the data model is not found, an HTTP exception will be raised.
* @param integer $id the ID of the model to be loaded
* @return <?php echo $this->modelClass; ?> the loaded model
* @throws CHttpException
*/
public function loadModel($id)
{
$model=<?php echo $this->modelClass; ?>::model()->findByPk($id);
if($model===null)
throw new CHttpException(404,'The requested page does not exist.');
return $model;
<?php
if (count($pks) === 1) {
$condition = '$id';
} else {
$condition = array();
foreach ($pks as $pk) {
$condition[] = "'$pk' => \$$pk";
}
/**
* Performs the AJAX validation.
* @param <?php echo $this->modelClass; ?> $model the model to be validated
*/
protected function performAjaxValidation($model)
{
if(isset($_POST['ajax']) && $_POST['ajax']==='<?php echo $this->class2id($this->modelClass); ?>-form')
{
echo CActiveForm::validate($model);
Yii::app()->end();
$condition = 'array(' . implode(', ', $condition) . ')';
}
?>
$model = <?php echo $modelClass; ?>::find(<?php echo $condition; ?>);
if ($model === null) {
throw new HttpException(404, 'The requested page does not exist.');
}
return $model;
}
}
<?php
use yii\helpers\Inflector;
use yii\helpers\StringHelper;
/**
* The following variables are available in this template:
* - $this: the CrudCode object
* @var yii\base\View $this
* @var yii\gii\generators\crud\Generator $generator
*/
?>
<?php echo "<?php\n"; ?>
/* @var $this <?php echo $this->getControllerClass(); ?> */
/* @var $model <?php echo $this->getModelClass(); ?> */
<?php
$label=$this->pluralize($this->class2name($this->modelClass));
echo "\$this->breadcrumbs=array(
'$label'=>array('index'),
'Create',
);\n";
echo "<?php\n";
?>
$this->menu=array(
array('label'=>'List <?php echo $this->modelClass; ?>', 'url'=>array('index')),
array('label'=>'Manage <?php echo $this->modelClass; ?>', 'url'=>array('admin')),
);
use yii\helpers\Html;
/**
* @var yii\base\View $this
* @var <?php echo ltrim($generator->modelClass, '\\'); ?> $model
*/
$this->title = 'Create <?php echo Inflector::camel2words(StringHelper::basename($generator->modelClass)); ?>';
?>
<div class="<?php echo Inflector::camel2id(StringHelper::basename($generator->modelClass)); ?>-create">
<h1><?php echo "<?php"; ?> echo Html::encode($this->title); ?></h1>
<h1>Create <?php echo $this->modelClass; ?></h1>
<?php echo "<?php"; ?> echo $this->render('_form', array(
'model' => $model,
)); ?>
<?php echo "<?php \$this->renderPartial('_form', array('model'=>\$model)); ?>"; ?>
</div>
<?php
use yii\helpers\Inflector;
use yii\helpers\StringHelper;
/**
* The following variables are available in this template:
* - $this: the CrudCode object
* @var yii\base\View $this
* @var yii\gii\generators\crud\Generator $generator
*/
?>
<?php echo "<?php\n"; ?>
/* @var $this <?php echo $this->getControllerClass(); ?> */
/* @var $model <?php echo $this->getModelClass(); ?> */
<?php
$nameColumn=$this->guessNameColumn($this->tableSchema->columns);
$label=$this->pluralize($this->class2name($this->modelClass));
echo "\$this->breadcrumbs=array(
'$label'=>array('index'),
\$model->{$nameColumn}=>array('view','id'=>\$model->{$this->tableSchema->primaryKey}),
'Update',
);\n";
echo "<?php\n";
?>
$this->menu=array(
array('label'=>'List <?php echo $this->modelClass; ?>', 'url'=>array('index')),
array('label'=>'Create <?php echo $this->modelClass; ?>', 'url'=>array('create')),
array('label'=>'View <?php echo $this->modelClass; ?>', 'url'=>array('view', 'id'=>$model-><?php echo $this->tableSchema->primaryKey; ?>)),
array('label'=>'Manage <?php echo $this->modelClass; ?>', 'url'=>array('admin')),
);
use yii\helpers\Html;
/**
* @var yii\base\View $this
* @var <?php echo ltrim($generator->modelClass, '\\'); ?> $model
*/
$this->title = 'Modify <?php echo Inflector::camel2words(StringHelper::basename($generator->modelClass)); ?>: ' . $model-><?php echo $generator->getNameAttribute(); ?>;
?>
<div class="<?php echo Inflector::camel2id(StringHelper::basename($generator->modelClass)); ?>-update">
<h1><?php echo "<?php"; ?> echo Html::encode($this->title); ?></h1>
<h1>Update <?php echo $this->modelClass." <?php echo \$model->{$this->tableSchema->primaryKey}; ?>"; ?></h1>
<?php echo "<?php"; ?> echo $this->render('_form', array(
'model' => $model,
)); ?>
<?php echo "<?php \$this->renderPartial('_form', array('model'=>\$model)); ?>"; ?>
\ No newline at end of file
</div>
<?php
use yii\helpers\Inflector;
use yii\helpers\StringHelper;
/**
* The following variables are available in this template:
* - $this: the CrudCode object
* @var yii\base\View $this
* @var yii\gii\generators\crud\Generator $generator
*/
?>
<?php echo "<?php\n"; ?>
/* @var $this <?php echo $this->getControllerClass(); ?> */
/* @var $model <?php echo $this->getModelClass(); ?> */
<?php
$nameColumn=$this->guessNameColumn($this->tableSchema->columns);
$label=$this->pluralize($this->class2name($this->modelClass));
echo "\$this->breadcrumbs=array(
'$label'=>array('index'),
\$model->{$nameColumn},
);\n";
echo "<?php\n";
?>
$this->menu=array(
array('label'=>'List <?php echo $this->modelClass; ?>', 'url'=>array('index')),
array('label'=>'Create <?php echo $this->modelClass; ?>', 'url'=>array('create')),
array('label'=>'Update <?php echo $this->modelClass; ?>', 'url'=>array('update', 'id'=>$model-><?php echo $this->tableSchema->primaryKey; ?>)),
array('label'=>'Delete <?php echo $this->modelClass; ?>', 'url'=>'#', 'linkOptions'=>array('submit'=>array('delete','id'=>$model-><?php echo $this->tableSchema->primaryKey; ?>),'confirm'=>'Are you sure you want to delete this item?')),
array('label'=>'Manage <?php echo $this->modelClass; ?>', 'url'=>array('admin')),
);
?>
use yii\helpers\Html;
<h1>View <?php echo $this->modelClass." #<?php echo \$model->{$this->tableSchema->primaryKey}; ?>"; ?></h1>
/**
* @var yii\base\View $this
* @var <?php echo ltrim($generator->modelClass, '\\'); ?> $model
*/
<?php echo "<?php"; ?> $this->widget('zii.widgets.CDetailView', array(
'data'=>$model,
'attributes'=>array(
<?php
foreach($this->tableSchema->columns as $column)
echo "\t\t'".$column->name."',\n";
$this->title = $model-><?php echo $generator->getNameAttribute(); ?>;
?>
),
)); ?>
<div class="<?php echo Inflector::camel2id(StringHelper::basename($generator->modelClass)); ?>-view">
<h1><?php echo "<?php"; ?> echo Html::encode($this->title); ?></h1>
</div>
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