1
0

Added new (clean) yii boilerplate

This commit is contained in:
2014-05-13 12:40:42 +02:00
parent 1d6d975a16
commit 99d29b432b
1983 changed files with 653465 additions and 17 deletions

View File

@@ -0,0 +1,130 @@
<?php
class ControllerCode extends CCodeModel
{
public $controller;
public $baseClass='Controller';
public $actions='index';
public function rules()
{
return array_merge(parent::rules(), array(
array('controller, actions, baseClass', 'filter', 'filter'=>'trim'),
array('controller, baseClass', 'required'),
array('controller', 'match', 'pattern'=>'/^\w+[\w+\\/]*$/', 'message'=>'{attribute} should only contain word characters and slashes.'),
array('actions', 'match', 'pattern'=>'/^\w+[\w\s,]*$/', 'message'=>'{attribute} should only contain word characters, spaces and commas.'),
array('baseClass', 'match', 'pattern'=>'/^[a-zA-Z_][\w\\\\]*$/', 'message'=>'{attribute} should only contain word characters and backslashes.'),
array('baseClass', 'validateReservedWord', 'skipOnError'=>true),
array('baseClass, actions', 'sticky'),
));
}
public function attributeLabels()
{
return array_merge(parent::attributeLabels(), array(
'baseClass'=>'Base Class',
'controller'=>'Controller ID',
'actions'=>'Action IDs',
));
}
public function requiredTemplates()
{
return array(
'controller.php',
'view.php',
);
}
public function successMessage()
{
$link=CHtml::link('try it now', Yii::app()->createUrl($this->controller), array('target'=>'_blank'));
return "The controller has been generated successfully. You may $link.";
}
public function prepare()
{
$this->files=array();
$templatePath=$this->templatePath;
$this->files[]=new CCodeFile(
$this->controllerFile,
$this->render($templatePath.'/controller.php')
);
foreach($this->getActionIDs() as $action)
{
$this->files[]=new CCodeFile(
$this->getViewFile($action),
$this->render($templatePath.'/view.php', array('action'=>$action))
);
}
}
public function getActionIDs()
{
$actions=preg_split('/[\s,]+/',$this->actions,-1,PREG_SPLIT_NO_EMPTY);
$actions=array_unique($actions);
sort($actions);
return $actions;
}
public function getControllerClass()
{
if(($pos=strrpos($this->controller,'/'))!==false)
return ucfirst(substr($this->controller,$pos+1)).'Controller';
else
return ucfirst($this->controller).'Controller';
}
public function getModule()
{
if(($pos=strpos($this->controller,'/'))!==false)
{
$id=substr($this->controller,0,$pos);
if(($module=Yii::app()->getModule($id))!==null)
return $module;
}
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 getUniqueControllerID()
{
$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';
}
public function getViewFile($action)
{
$module=$this->getModule();
return $module->getViewPath().'/'.$this->getControllerID().'/'.$action.'.php';
}
}

View File

@@ -0,0 +1,6 @@
<?php
class ControllerGenerator extends CCodeGenerator
{
public $codeModel='gii.generators.controller.ControllerCode';
}

View File

@@ -0,0 +1,45 @@
<?php
/**
* This is the template for generating a controller class file.
* The following variables are available in this template:
* - $this: the ControllerCode object
*/
?>
<?php echo "<?php\n"; ?>
class <?php echo $this->controllerClass; ?> extends <?php echo $this->baseClass."\n"; ?>
{
<?php foreach($this->getActionIDs() as $action): ?>
public function action<?php echo ucfirst($action); ?>()
{
$this->render('<?php echo $action; ?>');
}
<?php endforeach; ?>
// Uncomment the following methods and override them if needed
/*
public function filters()
{
// return the filter configuration for this controller, e.g.:
return array(
'inlineFilterName',
array(
'class'=>'path.to.FilterClass',
'propertyName'=>'propertyValue',
),
);
}
public function actions()
{
// return external action classes, e.g.:
return array(
'action1'=>'path.to.ActionClass',
'action2'=>array(
'class'=>'path.to.AnotherActionClass',
'propertyName'=>'propertyValue',
),
);
}
*/
}

View File

@@ -0,0 +1,36 @@
<?php
/**
* This is the template for generating an action view file.
* The following variables are available in this template:
* - $this: the ControllerCode object
* - $action: the action ID
*/
?>
<?php echo "<?php\n"; ?>
/* @var $this <?php echo $this->getControllerClass(); ?> */
<?php
$label=ucwords(trim(strtolower(str_replace(array('-','_','.'),' ',preg_replace('/(?<![A-Z])[A-Z]/', ' \0', basename($this->getControllerID()))))));
if($action==='index')
{
echo "\$this->breadcrumbs=array(
'$label',
);";
}
else
{
$action=ucfirst($action);
echo "\$this->breadcrumbs=array(
'$label'=>array('/{$this->uniqueControllerID}'),
'$action',
);";
}
?>
?>
<h1><?php echo '<?php'; ?> echo $this->id . '/' . $this->action->id; ?></h1>
<p>
You may change the content of this page by modifying
the file <tt><?php echo '<?php'; ?> echo __FILE__; ?></tt>.
</p>

View File

@@ -0,0 +1,45 @@
<h1>Controller Generator</h1>
<p>This generator helps you to quickly generate a new controller class,
one or several controller actions and their corresponding views.</p>
<?php $form=$this->beginWidget('CCodeForm', array('model'=>$model)); ?>
<div class="row">
<?php echo $form->labelEx($model,'controller'); ?>
<?php echo $form->textField($model,'controller',array('size'=>65)); ?>
<div class="tooltip">
Controller ID is case-sensitive. Below are some examples:
<ul>
<li><code>post</code> generates <code>PostController.php</code></li>
<li><code>postTag</code> generates <code>PostTagController.php</code></li>
<li><code>admin/user</code> generates <code>admin/UserController.php</code>.
If the application has an <code>admin</code> module enabled,
it will generate <code>UserController</code> within the module instead.
Make sure to write module name in the correct case if it has a camelCase name.
</li>
</ul>
</div>
<?php echo $form->error($model,'controller'); ?>
</div>
<div class="row sticky">
<?php echo $form->labelEx($model,'baseClass'); ?>
<?php echo $form->textField($model,'baseClass',array('size'=>65)); ?>
<div class="tooltip">
This is the class that the new controller class will extend from.
Please make sure the class exists and can be autoloaded.
</div>
<?php echo $form->error($model,'baseClass'); ?>
</div>
<div class="row">
<?php echo $form->labelEx($model,'actions'); ?>
<?php echo $form->textField($model,'actions',array('size'=>65)); ?>
<div class="tooltip">
Action IDs are case-insensitive. Separate multiple action IDs with commas or spaces.
</div>
<?php echo $form->error($model,'actions'); ?>
</div>
<?php $this->endWidget(); ?>

View File

@@ -0,0 +1,248 @@
<?php
class CrudCode extends CCodeModel
{
public $model;
public $controller;
public $baseControllerClass='Controller';
private $_modelClass;
private $_table;
public function rules()
{
return array_merge(parent::rules(), array(
array('model, controller', 'filter', 'filter'=>'trim'),
array('model, controller, baseControllerClass', 'required'),
array('model', 'match', 'pattern'=>'/^\w+[\w+\\.]*$/', 'message'=>'{attribute} should only contain word characters and dots.'),
array('controller', 'match', 'pattern'=>'/^\w+[\w+\\/]*$/', 'message'=>'{attribute} should only contain word characters and slashes.'),
array('baseControllerClass', 'match', 'pattern'=>'/^[a-zA-Z_][\w\\\\]*$/', 'message'=>'{attribute} should only contain word characters and backslashes.'),
array('baseControllerClass', 'validateReservedWord', 'skipOnError'=>true),
array('model', 'validateModel'),
array('baseControllerClass', 'sticky'),
));
}
public function attributeLabels()
{
return array_merge(parent::attributeLabels(), array(
'model'=>'Model Class',
'controller'=>'Controller ID',
'baseControllerClass'=>'Base Controller Class',
));
}
public function requiredTemplates()
{
return array(
'controller.php',
);
}
public function init()
{
if(Yii::app()->db===null)
throw new CHttpException(500,'An active "db" connection is required to run this generator.');
parent::init();
}
public function successMessage()
{
$link=CHtml::link('try it now', Yii::app()->createUrl($this->controller), array('target'=>'_blank'));
return "The controller has been generated successfully. You may $link.";
}
public function validateModel($attribute,$params)
{
if($this->hasErrors('model'))
return;
$class=@Yii::import($this->model,true);
if(!is_string($class) || !$this->classExists($class))
$this->addError('model', "Class '{$this->model}' does not exist or has syntax error.");
elseif(!is_subclass_of($class,'CActiveRecord'))
$this->addError('model', "'{$this->model}' must extend from CActiveRecord.");
else
{
$table=CActiveRecord::model($class)->tableSchema;
if($table->primaryKey===null)
$this->addError('model',"Table '{$table->name}' does not have a primary key.");
elseif(is_array($table->primaryKey))
$this->addError('model',"Table '{$table->name}' has a composite primary key which is not supported by crud generator.");
else
{
$this->_modelClass=$class;
$this->_table=$table;
}
}
}
public function prepare()
{
$this->files=array();
$templatePath=$this->templatePath;
$controllerTemplateFile=$templatePath.DIRECTORY_SEPARATOR.'controller.php';
$this->files[]=new CCodeFile(
$this->controllerFile,
$this->render($controllerTemplateFile)
);
$files=scandir($templatePath);
foreach($files as $file)
{
if(is_file($templatePath.'/'.$file) && CFileHelper::getExtension($file)==='php' && $file!=='controller.php')
{
$this->files[]=new CCodeFile(
$this->viewPath.DIRECTORY_SEPARATOR.$file,
$this->render($templatePath.'/'.$file)
);
}
}
}
public function getModelClass()
{
return $this->_modelClass;
}
public function getControllerClass()
{
if(($pos=strrpos($this->controller,'/'))!==false)
return ucfirst(substr($this->controller,$pos+1)).'Controller';
else
return ucfirst($this->controller).'Controller';
}
public function getModule()
{
if(($pos=strpos($this->controller,'/'))!==false)
{
$id=substr($this->controller,0,$pos);
if(($module=Yii::app()->getModule($id))!==null)
return $module;
}
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 getUniqueControllerID()
{
$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';
}
public function getViewPath()
{
return $this->getModule()->getViewPath().'/'.$this->getControllerID();
}
public function getTableSchema()
{
return $this->_table;
}
public function generateInputLabel($modelClass,$column)
{
return "CHtml::activeLabelEx(\$model,'{$column->name}')";
}
public function generateInputField($modelClass,$column)
{
if($column->type==='boolean')
return "CHtml::activeCheckBox(\$model,'{$column->name}')";
elseif(stripos($column->dbType,'text')!==false)
return "CHtml::activeTextArea(\$model,'{$column->name}',array('rows'=>6, 'cols'=>50))";
else
{
if(preg_match('/^(password|pass|passwd|passcode)$/i',$column->name))
$inputField='activePasswordField';
else
$inputField='activeTextField';
if($column->type!=='string' || $column->size===null)
return "CHtml::{$inputField}(\$model,'{$column->name}')";
else
{
if(($size=$maxLength=$column->size)>60)
$size=60;
return "CHtml::{$inputField}(\$model,'{$column->name}',array('size'=>$size,'maxlength'=>$maxLength))";
}
}
}
public function generateActiveLabel($modelClass,$column)
{
return "\$form->labelEx(\$model,'{$column->name}')";
}
public function generateActiveField($modelClass,$column)
{
if($column->type==='boolean')
return "\$form->checkBox(\$model,'{$column->name}')";
elseif(stripos($column->dbType,'text')!==false)
return "\$form->textArea(\$model,'{$column->name}',array('rows'=>6, 'cols'=>50))";
else
{
if(preg_match('/^(password|pass|passwd|passcode)$/i',$column->name))
$inputField='passwordField';
else
$inputField='textField';
if($column->type!=='string' || $column->size===null)
return "\$form->{$inputField}(\$model,'{$column->name}')";
else
{
if(($size=$maxLength=$column->size)>60)
$size=60;
return "\$form->{$inputField}(\$model,'{$column->name}',array('size'=>$size,'maxlength'=>$maxLength))";
}
}
}
public function guessNameColumn($columns)
{
foreach($columns as $column)
{
if(!strcasecmp($column->name,'name'))
return $column->name;
}
foreach($columns as $column)
{
if(!strcasecmp($column->name,'title'))
return $column->name;
}
foreach($columns as $column)
{
if($column->isPrimaryKey)
return $column->name;
}
return 'id';
}
}

View File

@@ -0,0 +1,6 @@
<?php
class CrudGenerator extends CCodeGenerator
{
public $codeModel='gii.generators.crud.CrudCode';
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* The following variables are available in this template:
* - $this: the CrudCode object
*/
?>
<?php echo "<?php\n"; ?>
/* @var $this <?php echo $this->getControllerClass(); ?> */
/* @var $model <?php echo $this->getModelClass(); ?> */
/* @var $form CActiveForm */
?>
<div class="form">
<?php echo "<?php \$form=\$this->beginWidget('CActiveForm', array(
'id'=>'".$this->class2id($this->modelClass)."-form',
// Please note: When you enable ajax validation, make sure the corresponding
// controller action is handling ajax validation correctly.
// There is a call to performAjaxValidation() commented in generated controller code.
// See class documentation of CActiveForm for details on this.
'enableAjaxValidation'=>false,
)); ?>\n"; ?>
<p class="note">Fields with <span class="required">*</span> are required.</p>
<?php echo "<?php echo \$form->errorSummary(\$model); ?>\n"; ?>
<?php
foreach($this->tableSchema->columns as $column)
{
if($column->autoIncrement)
continue;
?>
<div class="row">
<?php echo "<?php echo ".$this->generateActiveLabel($this->modelClass,$column)."; ?>\n"; ?>
<?php echo "<?php echo ".$this->generateActiveField($this->modelClass,$column)."; ?>\n"; ?>
<?php echo "<?php echo \$form->error(\$model,'{$column->name}'); ?>\n"; ?>
</div>
<?php
}
?>
<div class="row buttons">
<?php echo "<?php echo CHtml::submitButton(\$model->isNewRecord ? 'Create' : 'Save'); ?>\n"; ?>
</div>
<?php echo "<?php \$this->endWidget(); ?>\n"; ?>
</div><!-- form -->

View File

@@ -0,0 +1,38 @@
<?php
/**
* The following variables are available in this template:
* - $this: the CrudCode object
*/
?>
<?php echo "<?php\n"; ?>
/* @var $this <?php echo $this->getControllerClass(); ?> */
/* @var $model <?php echo $this->getModelClass(); ?> */
/* @var $form CActiveForm */
?>
<div class="wide form">
<?php echo "<?php \$form=\$this->beginWidget('CActiveForm', array(
'action'=>Yii::app()->createUrl(\$this->route),
'method'=>'get',
)); ?>\n"; ?>
<?php foreach($this->tableSchema->columns as $column): ?>
<?php
$field=$this->generateInputField($this->modelClass,$column);
if(strpos($field,'password')!==false)
continue;
?>
<div class="row">
<?php echo "<?php echo \$form->label(\$model,'{$column->name}'); ?>\n"; ?>
<?php echo "<?php echo ".$this->generateActiveField($this->modelClass,$column)."; ?>\n"; ?>
</div>
<?php endforeach; ?>
<div class="row buttons">
<?php echo "<?php echo CHtml::submitButton('Search'); ?>\n"; ?>
</div>
<?php echo "<?php \$this->endWidget(); ?>\n"; ?>
</div><!-- search-form -->

View File

@@ -0,0 +1,31 @@
<?php
/**
* The following variables are available in this template:
* - $this: the CrudCode object
*/
?>
<?php echo "<?php\n"; ?>
/* @var $this <?php echo $this->getControllerClass(); ?> */
/* @var $data <?php echo $this->getModelClass(); ?> */
?>
<div class="view">
<?php
echo "\t<b><?php echo CHtml::encode(\$data->getAttributeLabel('{$this->tableSchema->primaryKey}')); ?>:</b>\n";
echo "\t<?php echo CHtml::link(CHtml::encode(\$data->{$this->tableSchema->primaryKey}), array('view', 'id'=>\$data->{$this->tableSchema->primaryKey})); ?>\n\t<br />\n\n";
$count=0;
foreach($this->tableSchema->columns as $column)
{
if($column->isPrimaryKey)
continue;
if(++$count==7)
echo "\t<?php /*\n";
echo "\t<b><?php echo CHtml::encode(\$data->getAttributeLabel('{$column->name}')); ?>:</b>\n";
echo "\t<?php echo CHtml::encode(\$data->{$column->name}); ?>\n\t<br />\n\n";
}
if($count>=7)
echo "\t*/ ?>\n";
?>
</div>

View File

@@ -0,0 +1,73 @@
<?php
/**
* The following variables are available in this template:
* - $this: the CrudCode object
*/
?>
<?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'),
'Manage',
);\n";
?>
$this->menu=array(
array('label'=>'List <?php echo $this->modelClass; ?>', 'url'=>array('index')),
array('label'=>'Create <?php echo $this->modelClass; ?>', 'url'=>array('create')),
);
Yii::app()->clientScript->registerScript('search', "
$('.search-button').click(function(){
$('.search-form').toggle();
return false;
});
$('.search-form form').submit(function(){
$('#<?php echo $this->class2id($this->modelClass); ?>-grid').yiiGridView('update', {
data: $(this).serialize()
});
return false;
});
");
?>
<h1>Manage <?php echo $this->pluralize($this->class2name($this->modelClass)); ?></h1>
<p>
You may optionally enter a comparison operator (<b>&lt;</b>, <b>&lt;=</b>, <b>&gt;</b>, <b>&gt;=</b>, <b>&lt;&gt;</b>
or <b>=</b>) at the beginning of each of your search values to specify how the comparison should be done.
</p>
<?php echo "<?php echo CHtml::link('Advanced Search','#',array('class'=>'search-button')); ?>"; ?>
<div class="search-form" style="display:none">
<?php echo "<?php \$this->renderPartial('_search',array(
'model'=>\$model,
)); ?>\n"; ?>
</div><!-- search-form -->
<?php echo "<?php"; ?> $this->widget('zii.widgets.grid.CGridView', array(
'id'=>'<?php echo $this->class2id($this->modelClass); ?>-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
<?php
$count=0;
foreach($this->tableSchema->columns as $column)
{
if(++$count==7)
echo "\t\t/*\n";
echo "\t\t'".$column->name."',\n";
}
if($count>=7)
echo "\t\t*/\n";
?>
array(
'class'=>'CButtonColumn',
),
),
)); ?>

View File

@@ -0,0 +1,180 @@
<?php
/**
* 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
*/
?>
<?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';
/**
* @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
);
}
/**
* Specifies the access control rules.
* This method is used by the 'accessControl' filter.
* @return array access control rules
*/
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('*'),
),
);
}
/**
* Displays a particular model.
* @param integer $id the ID of the model to be displayed
*/
public function actionView($id)
{
$this->render('view',array(
'model'=>$this->loadModel($id),
));
}
/**
* Creates a new model.
* If creation is successful, the browser will be redirected to the 'view' page.
*/
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; ?>));
}
$this->render('create',array(
'model'=>$model,
));
}
/**
* Updates a particular 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
*/
public function actionUpdate($id)
{
$model=$this->loadModel($id);
// 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,
));
}
/**
* 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
*/
public function actionDelete($id)
{
$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'));
}
/**
* Lists all models.
*/
public function actionIndex()
{
$dataProvider=new CActiveDataProvider('<?php echo $this->modelClass; ?>');
$this->render('index',array(
'dataProvider'=>$dataProvider,
));
}
/**
* Manages all models.
*/
public function actionAdmin()
{
$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;
}
/**
* 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();
}
}
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* The following variables are available in this template:
* - $this: the CrudCode object
*/
?>
<?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";
?>
$this->menu=array(
array('label'=>'List <?php echo $this->modelClass; ?>', 'url'=>array('index')),
array('label'=>'Manage <?php echo $this->modelClass; ?>', 'url'=>array('admin')),
);
?>
<h1>Create <?php echo $this->modelClass; ?></h1>
<?php echo "<?php \$this->renderPartial('_form', array('model'=>\$model)); ?>"; ?>

View File

@@ -0,0 +1,29 @@
<?php
/**
* The following variables are available in this template:
* - $this: the CrudCode object
*/
?>
<?php echo "<?php\n"; ?>
/* @var $this <?php echo $this->getControllerClass(); ?> */
/* @var $dataProvider CActiveDataProvider */
<?php
$label=$this->pluralize($this->class2name($this->modelClass));
echo "\$this->breadcrumbs=array(
'$label',
);\n";
?>
$this->menu=array(
array('label'=>'Create <?php echo $this->modelClass; ?>', 'url'=>array('create')),
array('label'=>'Manage <?php echo $this->modelClass; ?>', 'url'=>array('admin')),
);
?>
<h1><?php echo $label; ?></h1>
<?php echo "<?php"; ?> $this->widget('zii.widgets.CListView', array(
'dataProvider'=>$dataProvider,
'itemView'=>'_view',
)); ?>

View File

@@ -0,0 +1,31 @@
<?php
/**
* The following variables are available in this template:
* - $this: the CrudCode object
*/
?>
<?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";
?>
$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')),
);
?>
<h1>Update <?php echo $this->modelClass." <?php echo \$model->{$this->tableSchema->primaryKey}; ?>"; ?></h1>
<?php echo "<?php \$this->renderPartial('_form', array('model'=>\$model)); ?>"; ?>

View File

@@ -0,0 +1,39 @@
<?php
/**
* The following variables are available in this template:
* - $this: the CrudCode object
*/
?>
<?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";
?>
$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')),
);
?>
<h1>View <?php echo $this->modelClass." #<?php echo \$model->{$this->tableSchema->primaryKey}; ?>"; ?></h1>
<?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";
?>
),
)); ?>

View File

@@ -0,0 +1,64 @@
<?php
$class=get_class($model);
Yii::app()->clientScript->registerScript('gii.crud',"
$('#{$class}_controller').change(function(){
$(this).data('changed',$(this).val()!='');
});
$('#{$class}_model').bind('keyup change', function(){
var controller=$('#{$class}_controller');
if(!controller.data('changed')) {
var id=new String($(this).val().match(/\\w*$/));
if(id.length>0)
id=id.substring(0,1).toLowerCase()+id.substring(1);
controller.val(id);
}
});
");
?>
<h1>Crud Generator</h1>
<p>This generator generates a controller and views that implement CRUD operations for the specified data model.</p>
<?php $form=$this->beginWidget('CCodeForm', array('model'=>$model)); ?>
<div class="row">
<?php echo $form->labelEx($model,'model'); ?>
<?php echo $form->textField($model,'model',array('size'=>65)); ?>
<div class="tooltip">
Model class is case-sensitive. It can be either a class name (e.g. <code>Post</code>)
or the path alias of the class file (e.g. <code>application.models.Post</code>).
Note that if the former, the class must be auto-loadable.
</div>
<?php echo $form->error($model,'model'); ?>
</div>
<div class="row">
<?php echo $form->labelEx($model,'controller'); ?>
<?php echo $form->textField($model,'controller',array('size'=>65)); ?>
<div class="tooltip">
Controller ID is case-sensitive. CRUD controllers are often named after
the model class name that they are dealing with. Below are some examples:
<ul>
<li><code>post</code> generates <code>PostController.php</code></li>
<li><code>postTag</code> generates <code>PostTagController.php</code></li>
<li><code>admin/user</code> generates <code>admin/UserController.php</code>.
If the application has an <code>admin</code> module enabled,
it will generate <code>UserController</code> (and other CRUD code)
within the module instead.
</li>
</ul>
</div>
<?php echo $form->error($model,'controller'); ?>
</div>
<div class="row sticky">
<?php echo $form->labelEx($model,'baseControllerClass'); ?>
<?php echo $form->textField($model,'baseControllerClass',array('size'=>65)); ?>
<div class="tooltip">
This is the class that the new CRUD controller class will extend from.
Please make sure the class exists and can be autoloaded.
</div>
<?php echo $form->error($model,'baseControllerClass'); ?>
</div>
<?php $this->endWidget(); ?>

View File

@@ -0,0 +1,94 @@
<?php
class FormCode extends CCodeModel
{
public $model;
public $viewPath='application.views';
public $viewName;
public $scenario;
private $_modelClass;
public function rules()
{
return array_merge(parent::rules(), array(
array('model, viewName, scenario', 'filter', 'filter'=>'trim'),
array('model, viewName, viewPath', 'required'),
array('model, viewPath', 'match', 'pattern'=>'/^\w+[\.\w+]*$/', 'message'=>'{attribute} should only contain word characters and dots.'),
array('viewName', 'match', 'pattern'=>'/^\w+[\\/\w+]*$/', 'message'=>'{attribute} should only contain word characters and slashes.'),
array('model', 'validateModel'),
array('viewPath', 'validateViewPath'),
array('scenario', 'match', 'pattern'=>'/^\w+$/', 'message'=>'{attribute} should only contain word characters.'),
array('viewPath', 'sticky'),
));
}
public function attributeLabels()
{
return array_merge(parent::attributeLabels(), array(
'model'=>'Model Class',
'viewName'=>'View Name',
'viewPath'=>'View Path',
'scenario'=>'Scenario',
));
}
public function requiredTemplates()
{
return array(
'form.php',
'action.php',
);
}
public function successMessage()
{
$output=<<<EOD
<p>The form has been generated successfully.</p>
<p>You may add the following code in an appropriate controller class to invoke the view:</p>
EOD;
$code="<?php\n".$this->render($this->templatePath.'/action.php');
return $output.highlight_string($code,true);
}
public function validateModel($attribute,$params)
{
if($this->hasErrors('model'))
return;
$class=@Yii::import($this->model,true);
if(!is_string($class) || !$this->classExists($class))
$this->addError('model', "Class '{$this->model}' does not exist or has syntax error.");
elseif(!is_subclass_of($class,'CModel'))
$this->addError('model', "'{$this->model}' must extend from CModel.");
else
$this->_modelClass=$class;
}
public function validateViewPath($attribute,$params)
{
if($this->hasErrors('viewPath'))
return;
if(Yii::getPathOfAlias($this->viewPath)===false)
$this->addError('viewPath','View Path must be a valid path alias.');
}
public function prepare()
{
$templatePath=$this->templatePath;
$this->files[]=new CCodeFile(
Yii::getPathOfAlias($this->viewPath).'/'.$this->viewName.'.php',
$this->render($templatePath.'/form.php')
);
}
public function getModelClass()
{
return $this->_modelClass;
}
public function getModelAttributes()
{
$model=new $this->_modelClass($this->scenario);
return $model->getSafeAttributeNames();
}
}

View File

@@ -0,0 +1,6 @@
<?php
class FormGenerator extends CCodeGenerator
{
public $codeModel='gii.generators.form.FormCode';
}

View File

@@ -0,0 +1,33 @@
<?php
/**
* This is the template for generating the action script for the form.
* - $this: the CrudCode object
*/
?>
<?php
$viewName=basename($this->viewName);
?>
public function action<?php echo ucfirst(trim($viewName,'_')); ?>()
{
$model=new <?php echo $this->modelClass; ?><?php echo empty($this->scenario) ? '' : "('{$this->scenario}')"; ?>;
// uncomment the following code to enable ajax-based validation
/*
if(isset($_POST['ajax']) && $_POST['ajax']==='<?php echo $this->class2id($this->modelClass); ?>-<?php echo $viewName; ?>-form')
{
echo CActiveForm::validate($model);
Yii::app()->end();
}
*/
if(isset($_POST['<?php echo $this->modelClass; ?>']))
{
$model->attributes=$_POST['<?php echo $this->modelClass; ?>'];
if($model->validate())
{
// form inputs are valid, do something here
return;
}
}
$this->render('<?php echo $viewName; ?>',array('model'=>$model));
}

View File

@@ -0,0 +1,44 @@
<?php
/**
* This is the template for generating a form script file.
* The following variables are available in this template:
* - $this: the FormCode object
*/
?>
<?php echo "<?php\n"; ?>
/* @var $this <?php echo $this->getModelClass(); ?>Controller */
/* @var $model <?php echo $this->getModelClass(); ?> */
/* @var $form CActiveForm */
?>
<div class="form">
<?php echo "<?php \$form=\$this->beginWidget('CActiveForm', array(
'id'=>'".$this->class2id($this->modelClass).'-'.basename($this->viewName)."-form',
// Please note: When you enable ajax validation, make sure the corresponding
// controller action is handling ajax validation correctly.
// See class documentation of CActiveForm for details on this,
// you need to use the performAjaxValidation()-method described there.
'enableAjaxValidation'=>false,
)); ?>\n"; ?>
<p class="note">Fields with <span class="required">*</span> are required.</p>
<?php echo "<?php echo \$form->errorSummary(\$model); ?>\n"; ?>
<?php foreach($this->getModelAttributes() as $attribute): ?>
<div class="row">
<?php echo "<?php echo \$form->labelEx(\$model,'$attribute'); ?>\n"; ?>
<?php echo "<?php echo \$form->textField(\$model,'$attribute'); ?>\n"; ?>
<?php echo "<?php echo \$form->error(\$model,'$attribute'); ?>\n"; ?>
</div>
<?php endforeach; ?>
<div class="row buttons">
<?php echo "<?php echo CHtml::submitButton('Submit'); ?>\n"; ?>
</div>
<?php echo "<?php \$this->endWidget(); ?>\n"; ?>
</div><!-- form -->

View File

@@ -0,0 +1,49 @@
<h1>Form Generator</h1>
<p>This generator generates a view script file that displays a form to collect input for the specified model class.</p>
<?php $form=$this->beginWidget('CCodeForm', array('model'=>$model)); ?>
<div class="row">
<?php echo $form->labelEx($model,'model'); ?>
<?php echo $form->textField($model,'model', array('size'=>65)); ?>
<div class="tooltip">
Model class is case-sensitive. It can be either a class name (e.g. <code>Post</code>)
or the path alias of the class file (e.g. <code>application.models.LoginForm</code>).
Note that if the former, the class must be auto-loadable.
</div>
<?php echo $form->error($model,'model'); ?>
</div>
<div class="row">
<?php echo $form->labelEx($model,'viewName'); ?>
<?php echo $form->textField($model,'viewName', array('size'=>65)); ?>
<div class="tooltip">
This refers to the name of the view script to be generated, for example,
<code>site/contact</code>, <code>user/login</code>. The actual view script file will be generated
under the View Path specified below.
</div>
<?php echo $form->error($model,'viewName'); ?>
</div>
<div class="row sticky">
<?php echo $form->labelEx($model,'viewPath'); ?>
<?php echo $form->textField($model,'viewPath', array('size'=>65)); ?>
<div class="tooltip">
This refers to the directory that the new view script file should be generated under.
It should be specified in the form of a path alias, for example, <code>application.views</code>,
<code>mymodule.views</code>.
</div>
<?php echo $form->error($model,'viewPath'); ?>
</div>
<div class="row">
<?php echo $form->labelEx($model,'scenario'); ?>
<?php echo $form->textField($model,'scenario', array('size'=>65)); ?>
<div class="tooltip">
This refers to the scenario in which the model should be used to collect user input.
For example, a <code>User</code> model can be used in both <code>login</code> and <code>register</code> scenarios.
To create a form for the login purpose, the scenario should be specified as <code>login</code>.
Leave this empty if the model does not need to differentiate scenarios.
</div>
<?php echo $form->error($model,'scenario'); ?>
</div>
<?php $this->endWidget(); ?>

View File

@@ -0,0 +1,431 @@
<?php
class ModelCode extends CCodeModel
{
public $connectionId='db';
public $tablePrefix;
public $tableName;
public $modelClass;
public $modelPath='application.models';
public $baseClass='CActiveRecord';
public $buildRelations=true;
public $commentsAsLabels=false;
/**
* @var array list of candidate relation code. The array are indexed by AR class names and relation names.
* Each element represents the code of the one relation in one AR class.
*/
protected $relations;
public function rules()
{
return array_merge(parent::rules(), array(
array('tablePrefix, baseClass, tableName, modelClass, modelPath, connectionId', 'filter', 'filter'=>'trim'),
array('connectionId, tableName, modelPath, baseClass', 'required'),
array('tablePrefix, tableName, modelPath', 'match', 'pattern'=>'/^(\w+[\w\.]*|\*?|\w+\.\*)$/', 'message'=>'{attribute} should only contain word characters, dots, and an optional ending asterisk.'),
array('connectionId', 'validateConnectionId', 'skipOnError'=>true),
array('tableName', 'validateTableName', 'skipOnError'=>true),
array('tablePrefix, modelClass', 'match', 'pattern'=>'/^[a-zA-Z_]\w*$/', 'message'=>'{attribute} should only contain word characters.'),
array('baseClass', 'match', 'pattern'=>'/^[a-zA-Z_][\w\\\\]*$/', 'message'=>'{attribute} should only contain word characters and backslashes.'),
array('modelPath', 'validateModelPath', 'skipOnError'=>true),
array('baseClass, modelClass', 'validateReservedWord', 'skipOnError'=>true),
array('baseClass', 'validateBaseClass', 'skipOnError'=>true),
array('connectionId, tablePrefix, modelPath, baseClass, buildRelations, commentsAsLabels', 'sticky'),
));
}
public function attributeLabels()
{
return array_merge(parent::attributeLabels(), array(
'tablePrefix'=>'Table Prefix',
'tableName'=>'Table Name',
'modelPath'=>'Model Path',
'modelClass'=>'Model Class',
'baseClass'=>'Base Class',
'buildRelations'=>'Build Relations',
'commentsAsLabels'=>'Use Column Comments as Attribute Labels',
'connectionId'=>'Database Connection',
));
}
public function requiredTemplates()
{
return array(
'model.php',
);
}
public function init()
{
if(Yii::app()->{$this->connectionId}===null)
throw new CHttpException(500,'A valid database connection is required to run this generator.');
$this->tablePrefix=Yii::app()->{$this->connectionId}->tablePrefix;
parent::init();
}
public function prepare()
{
if(($pos=strrpos($this->tableName,'.'))!==false)
{
$schema=substr($this->tableName,0,$pos);
$tableName=substr($this->tableName,$pos+1);
}
else
{
$schema='';
$tableName=$this->tableName;
}
if($tableName[strlen($tableName)-1]==='*')
{
$tables=Yii::app()->{$this->connectionId}->schema->getTables($schema);
if($this->tablePrefix!='')
{
foreach($tables as $i=>$table)
{
if(strpos($table->name,$this->tablePrefix)!==0)
unset($tables[$i]);
}
}
}
else
$tables=array($this->getTableSchema($this->tableName));
$this->files=array();
$templatePath=$this->templatePath;
$this->relations=$this->generateRelations();
foreach($tables as $table)
{
$tableName=$this->removePrefix($table->name);
$className=$this->generateClassName($table->name);
$params=array(
'tableName'=>$schema==='' ? $tableName : $schema.'.'.$tableName,
'modelClass'=>$className,
'columns'=>$table->columns,
'labels'=>$this->generateLabels($table),
'rules'=>$this->generateRules($table),
'relations'=>isset($this->relations[$className]) ? $this->relations[$className] : array(),
'connectionId'=>$this->connectionId,
);
$this->files[]=new CCodeFile(
Yii::getPathOfAlias($this->modelPath).'/'.$className.'.php',
$this->render($templatePath.'/model.php', $params)
);
}
}
public function validateTableName($attribute,$params)
{
if($this->hasErrors())
return;
$invalidTables=array();
$invalidColumns=array();
if($this->tableName[strlen($this->tableName)-1]==='*')
{
if(($pos=strrpos($this->tableName,'.'))!==false)
$schema=substr($this->tableName,0,$pos);
else
$schema='';
$this->modelClass='';
$tables=Yii::app()->{$this->connectionId}->schema->getTables($schema);
foreach($tables as $table)
{
if($this->tablePrefix=='' || strpos($table->name,$this->tablePrefix)===0)
{
if(in_array(strtolower($table->name),self::$keywords))
$invalidTables[]=$table->name;
if(($invalidColumn=$this->checkColumns($table))!==null)
$invalidColumns[]=$invalidColumn;
}
}
}
else
{
if(($table=$this->getTableSchema($this->tableName))===null)
$this->addError('tableName',"Table '{$this->tableName}' does not exist.");
if($this->modelClass==='')
$this->addError('modelClass','Model Class cannot be blank.');
if(!$this->hasErrors($attribute) && ($invalidColumn=$this->checkColumns($table))!==null)
$invalidColumns[]=$invalidColumn;
}
if($invalidTables!=array())
$this->addError('tableName', 'Model class cannot take a reserved PHP keyword! Table name: '.implode(', ', $invalidTables).".");
if($invalidColumns!=array())
$this->addError('tableName', 'Column names that does not follow PHP variable naming convention: '.implode(', ', $invalidColumns).".");
}
/*
* Check that all database field names conform to PHP variable naming rules
* For example mysql allows field name like "2011aa", but PHP does not allow variable like "$model->2011aa"
* @param CDbTableSchema $table the table schema object
* @return string the invalid table column name. Null if no error.
*/
public function checkColumns($table)
{
foreach($table->columns as $column)
{
if(!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/',$column->name))
return $table->name.'.'.$column->name;
}
}
public function validateModelPath($attribute,$params)
{
if(Yii::getPathOfAlias($this->modelPath)===false)
$this->addError('modelPath','Model Path must be a valid path alias.');
}
public function validateBaseClass($attribute,$params)
{
$class=@Yii::import($this->baseClass,true);
if(!is_string($class) || !$this->classExists($class))
$this->addError('baseClass', "Class '{$this->baseClass}' does not exist or has syntax error.");
elseif($class!=='CActiveRecord' && !is_subclass_of($class,'CActiveRecord'))
$this->addError('baseClass', "'{$this->model}' must extend from CActiveRecord.");
}
public function getTableSchema($tableName)
{
$connection=Yii::app()->{$this->connectionId};
return $connection->getSchema()->getTable($tableName, $connection->schemaCachingDuration!==0);
}
public function generateLabels($table)
{
$labels=array();
foreach($table->columns as $column)
{
if($this->commentsAsLabels && $column->comment)
$labels[$column->name]=$column->comment;
else
{
$label=ucwords(trim(strtolower(str_replace(array('-','_'),' ',preg_replace('/(?<![A-Z])[A-Z]/', ' \0', $column->name)))));
$label=preg_replace('/\s+/',' ',$label);
if(strcasecmp(substr($label,-3),' id')===0)
$label=substr($label,0,-3);
if($label==='Id')
$label='ID';
$label=str_replace("'","\\'",$label);
$labels[$column->name]=$label;
}
}
return $labels;
}
public function generateRules($table)
{
$rules=array();
$required=array();
$integers=array();
$numerical=array();
$length=array();
$safe=array();
foreach($table->columns as $column)
{
if($column->autoIncrement)
continue;
$r=!$column->allowNull && $column->defaultValue===null;
if($r)
$required[]=$column->name;
if($column->type==='integer')
$integers[]=$column->name;
elseif($column->type==='double')
$numerical[]=$column->name;
elseif($column->type==='string' && $column->size>0)
$length[$column->size][]=$column->name;
elseif(!$column->isPrimaryKey && !$r)
$safe[]=$column->name;
}
if($required!==array())
$rules[]="array('".implode(', ',$required)."', 'required')";
if($integers!==array())
$rules[]="array('".implode(', ',$integers)."', 'numerical', 'integerOnly'=>true)";
if($numerical!==array())
$rules[]="array('".implode(', ',$numerical)."', 'numerical')";
if($length!==array())
{
foreach($length as $len=>$cols)
$rules[]="array('".implode(', ',$cols)."', 'length', 'max'=>$len)";
}
if($safe!==array())
$rules[]="array('".implode(', ',$safe)."', 'safe')";
return $rules;
}
public function getRelations($className)
{
return isset($this->relations[$className]) ? $this->relations[$className] : array();
}
protected function removePrefix($tableName,$addBrackets=true)
{
if($addBrackets && Yii::app()->{$this->connectionId}->tablePrefix=='')
return $tableName;
$prefix=$this->tablePrefix!='' ? $this->tablePrefix : Yii::app()->{$this->connectionId}->tablePrefix;
if($prefix!='')
{
if($addBrackets && Yii::app()->{$this->connectionId}->tablePrefix!='')
{
$prefix=Yii::app()->{$this->connectionId}->tablePrefix;
$lb='{{';
$rb='}}';
}
else
$lb=$rb='';
if(($pos=strrpos($tableName,'.'))!==false)
{
$schema=substr($tableName,0,$pos);
$name=substr($tableName,$pos+1);
if(strpos($name,$prefix)===0)
return $schema.'.'.$lb.substr($name,strlen($prefix)).$rb;
}
elseif(strpos($tableName,$prefix)===0)
return $lb.substr($tableName,strlen($prefix)).$rb;
}
return $tableName;
}
protected function generateRelations()
{
if(!$this->buildRelations)
return array();
$schemaName='';
if(($pos=strpos($this->tableName,'.'))!==false)
$schemaName=substr($this->tableName,0,$pos);
$relations=array();
foreach(Yii::app()->{$this->connectionId}->schema->getTables($schemaName) as $table)
{
if($this->tablePrefix!='' && strpos($table->name,$this->tablePrefix)!==0)
continue;
$tableName=$table->name;
if ($this->isRelationTable($table))
{
$pks=$table->primaryKey;
$fks=$table->foreignKeys;
$table0=$fks[$pks[0]][0];
$table1=$fks[$pks[1]][0];
$className0=$this->generateClassName($table0);
$className1=$this->generateClassName($table1);
$unprefixedTableName=$this->removePrefix($tableName);
$relationName=$this->generateRelationName($table0, $table1, true);
$relations[$className0][$relationName]="array(self::MANY_MANY, '$className1', '$unprefixedTableName($pks[0], $pks[1])')";
$relationName=$this->generateRelationName($table1, $table0, true);
$i=1;
$rawName=$relationName;
while(isset($relations[$className1][$relationName]))
$relationName=$rawName.$i++;
$relations[$className1][$relationName]="array(self::MANY_MANY, '$className0', '$unprefixedTableName($pks[1], $pks[0])')";
}
else
{
$className=$this->generateClassName($tableName);
foreach ($table->foreignKeys as $fkName => $fkEntry)
{
// Put table and key name in variables for easier reading
$refTable=$fkEntry[0]; // Table name that current fk references to
$refKey=$fkEntry[1]; // Key in that table being referenced
$refClassName=$this->generateClassName($refTable);
// Add relation for this table
$relationName=$this->generateRelationName($tableName, $fkName, false);
$relations[$className][$relationName]="array(self::BELONGS_TO, '$refClassName', '$fkName')";
// Add relation for the referenced table
$relationType=$table->primaryKey === $fkName ? 'HAS_ONE' : 'HAS_MANY';
$relationName=$this->generateRelationName($refTable, $this->removePrefix($tableName,false), $relationType==='HAS_MANY');
$i=1;
$rawName=$relationName;
while(isset($relations[$refClassName][$relationName]))
$relationName=$rawName.($i++);
$relations[$refClassName][$relationName]="array(self::$relationType, '$className', '$fkName')";
}
}
}
return $relations;
}
/**
* Checks if the given table is a "many to many" pivot table.
* Their PK has 2 fields, and both of those fields are also FK to other separate tables.
* @param CDbTableSchema table to inspect
* @return boolean true if table matches description of helpter table.
*/
protected function isRelationTable($table)
{
$pk=$table->primaryKey;
return (count($pk) === 2 // we want 2 columns
&& isset($table->foreignKeys[$pk[0]]) // pk column 1 is also a foreign key
&& isset($table->foreignKeys[$pk[1]]) // pk column 2 is also a foriegn key
&& $table->foreignKeys[$pk[0]][0] !== $table->foreignKeys[$pk[1]][0]); // and the foreign keys point different tables
}
protected function generateClassName($tableName)
{
if($this->tableName===$tableName || ($pos=strrpos($this->tableName,'.'))!==false && substr($this->tableName,$pos+1)===$tableName)
return $this->modelClass;
$tableName=$this->removePrefix($tableName,false);
if(($pos=strpos($tableName,'.'))!==false) // remove schema part (e.g. remove 'public2.' from 'public2.post')
$tableName=substr($tableName,$pos+1);
$className='';
foreach(explode('_',$tableName) as $name)
{
if($name!=='')
$className.=ucfirst($name);
}
return $className;
}
/**
* Generate a name for use as a relation name (inside relations() function in a model).
* @param string the name of the table to hold the relation
* @param string the foreign key name
* @param boolean whether the relation would contain multiple objects
* @return string the relation name
*/
protected function generateRelationName($tableName, $fkName, $multiple)
{
if(strcasecmp(substr($fkName,-2),'id')===0 && strcasecmp($fkName,'id'))
$relationName=rtrim(substr($fkName, 0, -2),'_');
else
$relationName=$fkName;
$relationName[0]=strtolower($relationName);
if($multiple)
$relationName=$this->pluralize($relationName);
$names=preg_split('/_+/',$relationName,-1,PREG_SPLIT_NO_EMPTY);
if(empty($names)) return $relationName; // unlikely
for($name=$names[0], $i=1;$i<count($names);++$i)
$name.=ucfirst($names[$i]);
$rawName=$name;
$table=Yii::app()->{$this->connectionId}->schema->getTable($tableName);
$i=0;
while(isset($table->columns[$name]))
$name=$rawName.($i++);
return $name;
}
public function validateConnectionId($attribute, $params)
{
if(Yii::app()->hasComponent($this->connectionId)===false || !(Yii::app()->getComponent($this->connectionId) instanceof CDbConnection))
$this->addError('connectionId','A valid database connection is required to run this generator.');
}
}

View File

@@ -0,0 +1,25 @@
<?php
class ModelGenerator extends CCodeGenerator
{
public $codeModel='gii.generators.model.ModelCode';
/**
* Provides autocomplete table names
* @param string $db the database connection component id
* @return string the json array of tablenames that contains the entered term $q
*/
public function actionGetTableNames($db)
{
if(Yii::app()->getRequest()->getIsAjaxRequest())
{
$all = array();
if(!empty($db) && Yii::app()->hasComponent($db)!==false && (Yii::app()->getComponent($db) instanceof CDbConnection))
$all=array_keys(Yii::app()->{$db}->schema->getTables());
echo json_encode($all);
}
else
throw new CHttpException(404,'The requested page does not exist.');
}
}

View File

@@ -0,0 +1,163 @@
<?php
/**
* This is the template for generating the model class of a specified table.
* - $this: the ModelCode object
* - $tableName: the table name for this class (prefix is already removed if necessary)
* - $modelClass: the model class name
* - $columns: list of table columns (name=>CDbColumnSchema)
* - $labels: list of attribute labels (name=>label)
* - $rules: list of validation rules
* - $relations: list of relations (name=>relation declaration)
*/
?>
<?php echo "<?php\n"; ?>
/**
* This is the model class for table "<?php echo $tableName; ?>".
*
* The followings are the available columns in table '<?php echo $tableName; ?>':
<?php foreach($columns as $column): ?>
* @property <?php echo $column->type.' $'.$column->name."\n"; ?>
<?php endforeach; ?>
<?php if(!empty($relations)): ?>
*
* The followings are the available model relations:
<?php foreach($relations as $name=>$relation): ?>
* @property <?php
if (preg_match("~^array\(self::([^,]+), '([^']+)', '([^']+)'\)$~", $relation, $matches))
{
$relationType = $matches[1];
$relationModel = $matches[2];
switch($relationType){
case 'HAS_ONE':
echo $relationModel.' $'.$name."\n";
break;
case 'BELONGS_TO':
echo $relationModel.' $'.$name."\n";
break;
case 'HAS_MANY':
echo $relationModel.'[] $'.$name."\n";
break;
case 'MANY_MANY':
echo $relationModel.'[] $'.$name."\n";
break;
default:
echo 'mixed $'.$name."\n";
}
}
?>
<?php endforeach; ?>
<?php endif; ?>
*/
class <?php echo $modelClass; ?> extends <?php echo $this->baseClass."\n"; ?>
{
/**
* @return string the associated database table name
*/
public function tableName()
{
return '<?php echo $tableName; ?>';
}
/**
* @return array validation rules for model attributes.
*/
public function rules()
{
// NOTE: you should only define rules for those attributes that
// will receive user inputs.
return array(
<?php foreach($rules as $rule): ?>
<?php echo $rule.",\n"; ?>
<?php endforeach; ?>
// The following rule is used by search().
// @todo Please remove those attributes that should not be searched.
array('<?php echo implode(', ', array_keys($columns)); ?>', 'safe', 'on'=>'search'),
);
}
/**
* @return array relational rules.
*/
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
<?php foreach($relations as $name=>$relation): ?>
<?php echo "'$name' => $relation,\n"; ?>
<?php endforeach; ?>
);
}
/**
* @return array customized attribute labels (name=>label)
*/
public function attributeLabels()
{
return array(
<?php foreach($labels as $name=>$label): ?>
<?php echo "'$name' => '$label',\n"; ?>
<?php endforeach; ?>
);
}
/**
* Retrieves a list of models based on the current search/filter conditions.
*
* Typical usecase:
* - Initialize the model fields with values from filter form.
* - Execute this method to get CActiveDataProvider instance which will filter
* models according to data in model fields.
* - Pass data provider to CGridView, CListView or any similar widget.
*
* @return CActiveDataProvider the data provider that can return the models
* based on the search/filter conditions.
*/
public function search()
{
// @todo Please modify the following code to remove attributes that should not be searched.
$criteria=new CDbCriteria;
<?php
foreach($columns as $name=>$column)
{
if($column->type==='string')
{
echo "\t\t\$criteria->compare('$name',\$this->$name,true);\n";
}
else
{
echo "\t\t\$criteria->compare('$name',\$this->$name);\n";
}
}
?>
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
<?php if($connectionId!='db'):?>
/**
* @return CDbConnection the database connection used for this class
*/
public function getDbConnection()
{
return Yii::app()-><?php echo $connectionId ?>;
}
<?php endif?>
/**
* Returns the static model of the specified AR class.
* Please note that you should have this exact method in all your CActiveRecord descendants!
* @param string $className active record class name.
* @return <?php echo $modelClass; ?> the static model class
*/
public static function model($className=__CLASS__)
{
return parent::model($className);
}
}

View File

@@ -0,0 +1,149 @@
<?php
$class=get_class($model);
Yii::app()->clientScript->registerScript('gii.model',"
$('#{$class}_connectionId').change(function(){
var tableName=$('#{$class}_tableName');
tableName.autocomplete('option', 'source', []);
$.ajax({
url: '".Yii::app()->getUrlManager()->createUrl('gii/model/getTableNames')."',
data: {db: this.value},
dataType: 'json'
}).done(function(data){
tableName.autocomplete('option', 'source', data);
});
});
$('#{$class}_modelClass').change(function(){
$(this).data('changed',$(this).val()!='');
});
$('#{$class}_tableName').bind('keyup change', function(){
var model=$('#{$class}_modelClass');
var tableName=$(this).val();
if(tableName.substring(tableName.length-1)!='*') {
$('.form .row.model-class').show();
}
else {
$('#{$class}_modelClass').val('');
$('.form .row.model-class').hide();
}
if(!model.data('changed')) {
var i=tableName.lastIndexOf('.');
if(i>=0)
tableName=tableName.substring(i+1);
var tablePrefix=$('#{$class}_tablePrefix').val();
if(tablePrefix!='' && tableName.indexOf(tablePrefix)==0)
tableName=tableName.substring(tablePrefix.length);
var modelClass='';
$.each(tableName.split('_'), function() {
if(this.length>0)
modelClass+=this.substring(0,1).toUpperCase()+this.substring(1);
});
model.val(modelClass);
}
});
$('.form .row.model-class').toggle($('#{$class}_tableName').val().substring($('#{$class}_tableName').val().length-1)!='*');
");
?>
<h1>Model Generator</h1>
<p>This generator generates a model class for the specified database table.</p>
<?php $form=$this->beginWidget('CCodeForm', array('model'=>$model)); ?>
<div class="row sticky">
<?php echo $form->labelEx($model, 'connectionId')?>
<?php echo $form->textField($model, 'connectionId', array('size'=>65))?>
<div class="tooltip">
The database component that should be used.
</div>
<?php echo $form->error($model,'connectionId'); ?>
</div>
<div class="row sticky">
<?php echo $form->labelEx($model,'tablePrefix'); ?>
<?php echo $form->textField($model,'tablePrefix', array('size'=>65)); ?>
<div class="tooltip">
This refers to the prefix name that is shared by all database tables.
Setting this property mainly affects how model classes are named based on
the table names. For example, a table prefix <code>tbl_</code> with a table name <code>tbl_post</code>
will generate a model class named <code>Post</code>.
<br/>
Leave this field empty if your database tables do not use common prefix.
</div>
<?php echo $form->error($model,'tablePrefix'); ?>
</div>
<div class="row">
<?php echo $form->labelEx($model,'tableName'); ?>
<?php $this->widget('zii.widgets.jui.CJuiAutoComplete',array(
'model'=>$model,
'attribute'=>'tableName',
'name'=>'tableName',
'source'=>Yii::app()->hasComponent($model->connectionId) ? array_keys(Yii::app()->{$model->connectionId}->schema->getTables()) : array(),
'options'=>array(
'minLength'=>'0',
'focus'=>new CJavaScriptExpression('function(event,ui) {
$("#'.CHtml::activeId($model,'tableName').'").val(ui.item.label).change();
return false;
}')
),
'htmlOptions'=>array(
'id'=>CHtml::activeId($model,'tableName'),
'size'=>'65',
'data-tooltip'=>'#tableName-tooltip'
),
)); ?>
<div class="tooltip" id="tableName-tooltip">
This refers to the table name that a new model class should be generated for
(e.g. <code>tbl_user</code>). It can contain schema name, if needed (e.g. <code>public.tbl_post</code>).
You may also enter <code>*</code> (or <code>schemaName.*</code> for a particular DB schema)
to generate a model class for EVERY table.
</div>
<?php echo $form->error($model,'tableName'); ?>
</div>
<div class="row model-class">
<?php echo $form->label($model,'modelClass',array('required'=>true)); ?>
<?php echo $form->textField($model,'modelClass', array('size'=>65)); ?>
<div class="tooltip">
This is the name of the model class to be generated (e.g. <code>Post</code>, <code>Comment</code>).
It is case-sensitive.
</div>
<?php echo $form->error($model,'modelClass'); ?>
</div>
<div class="row sticky">
<?php echo $form->labelEx($model,'baseClass'); ?>
<?php echo $form->textField($model,'baseClass',array('size'=>65)); ?>
<div class="tooltip">
This is the class that the new model class will extend from.
Please make sure the class exists and can be autoloaded.
</div>
<?php echo $form->error($model,'baseClass'); ?>
</div>
<div class="row sticky">
<?php echo $form->labelEx($model,'modelPath'); ?>
<?php echo $form->textField($model,'modelPath', array('size'=>65)); ?>
<div class="tooltip">
This refers to the directory that the new model class file should be generated under.
It should be specified in the form of a path alias, for example, <code>application.models</code>.
</div>
<?php echo $form->error($model,'modelPath'); ?>
</div>
<div class="row">
<?php echo $form->labelEx($model,'buildRelations'); ?>
<?php echo $form->checkBox($model,'buildRelations'); ?>
<div class="tooltip">
Whether relations should be generated for the model class.
In order to generate relations, full scan of the whole database is needed.
You should disable this option if your database contains too many tables.
</div>
<?php echo $form->error($model,'buildRelations'); ?>
</div>
<div class="row">
<?php echo $form->labelEx($model,'commentsAsLabels'); ?>
<?php echo $form->checkBox($model,'commentsAsLabels'); ?>
<div class="tooltip">
Whether comments specified for the table columns should be used as the new model's attribute labels.
In case your RDBMS doesn't support feature of commenting columns or column comment wasn't set,
column name would be used as the attribute name base.
</div>
<?php echo $form->error($model,'commentsAsLabels'); ?>
</div>
<?php $this->endWidget(); ?>

View File

@@ -0,0 +1,94 @@
<?php
class ModuleCode extends CCodeModel
{
public $moduleID;
public function rules()
{
return array_merge(parent::rules(), array(
array('moduleID', 'filter', 'filter'=>'trim'),
array('moduleID', 'required'),
array('moduleID', 'match', 'pattern'=>'/^\w+$/', 'message'=>'{attribute} should only contain word characters.'),
));
}
public function attributeLabels()
{
return array_merge(parent::attributeLabels(), array(
'moduleID'=>'Module ID',
));
}
public function successMessage()
{
if(Yii::app()->hasModule($this->moduleID))
return 'The module has been generated successfully. You may '.CHtml::link('try it now', Yii::app()->createUrl($this->moduleID), array('target'=>'_blank')).'.';
$output=<<<EOD
<p>The module has been generated successfully.</p>
<p>To access the module, you need to modify the application configuration as follows:</p>
EOD;
$code=<<<EOD
<?php
return array(
'modules'=>array(
'{$this->moduleID}',
),
......
);
EOD;
return $output.highlight_string($code,true);
}
public function prepare()
{
$this->files=array();
$templatePath=$this->templatePath;
$modulePath=$this->modulePath;
$moduleTemplateFile=$templatePath.DIRECTORY_SEPARATOR.'module.php';
$this->files[]=new CCodeFile(
$modulePath.'/'.$this->moduleClass.'.php',
$this->render($moduleTemplateFile)
);
$files=CFileHelper::findFiles($templatePath,array(
'exclude'=>array(
'.svn',
'.gitignore'
),
));
foreach($files as $file)
{
if($file!==$moduleTemplateFile)
{
if(CFileHelper::getExtension($file)==='php')
$content=$this->render($file);
elseif(basename($file)==='.yii') // an empty directory
{
$file=dirname($file);
$content=null;
}
else
$content=file_get_contents($file);
$this->files[]=new CCodeFile(
$modulePath.substr($file,strlen($templatePath)),
$content
);
}
}
}
public function getModuleClass()
{
return ucfirst($this->moduleID).'Module';
}
public function getModulePath()
{
return Yii::app()->modulePath.DIRECTORY_SEPARATOR.$this->moduleID;
}
}

View File

@@ -0,0 +1,6 @@
<?php
class ModuleGenerator extends CCodeGenerator
{
public $codeModel='gii.generators.module.ModuleCode';
}

View File

@@ -0,0 +1,9 @@
<?php echo "<?php\n"; ?>
class DefaultController extends Controller
{
public function actionIndex()
{
$this->render('index');
}
}

View File

@@ -0,0 +1,28 @@
<?php echo "<?php\n"; ?>
class <?php echo $this->moduleClass; ?> extends CWebModule
{
public function init()
{
// this method is called when the module is being created
// you may place code here to customize the module or the application
// import the module-level models and components
$this->setImport(array(
'<?php echo $this->moduleID; ?>.models.*',
'<?php echo $this->moduleID; ?>.components.*',
));
}
public function beforeControllerAction($controller, $action)
{
if(parent::beforeControllerAction($controller, $action))
{
// this method is called before any module controller action is performed
// you may place customized code here
return true;
}
else
return false;
}
}

View File

@@ -0,0 +1,17 @@
<?php echo "<?php\n"; ?>
/* @var $this DefaultController */
$this->breadcrumbs=array(
$this->module->id,
);
?>
<h1><?php echo "<?php"; ?> echo $this->uniqueId . '/' . $this->action->id; ?></h1>
<p>
This is the view content for action "<?php echo "<?php"; ?> echo $this->action->id; ?>".
The action belongs to the controller "<?php echo "<?php"; ?> echo get_class($this); ?>"
in the "<?php echo "<?php"; ?> echo $this->module->id; ?>" module.
</p>
<p>
You may customize this page by editing <tt><?php echo "<?php"; ?> echo __FILE__; ?></tt>
</p>

View File

@@ -0,0 +1,19 @@
<h1>Module Generator</h1>
<p>This generator helps you to generate the skeleton code needed by a Yii module.</p>
<?php $form=$this->beginWidget('CCodeForm', array('model'=>$model)); ?>
<div class="row">
<?php echo $form->labelEx($model,'moduleID'); ?>
<?php echo $form->textField($model,'moduleID',array('size'=>65)); ?>
<div class="tooltip">
Module ID is case-sensitive. It should only contain word characters.
The generated module class will be named after the module ID.
For example, a module ID <code>forum</code> will generate the module class
<code>ForumModule</code>.
</div>
<?php echo $form->error($model,'moduleID'); ?>
</div>
<?php $this->endWidget(); ?>