Yii2 framework backup
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

502 lines
14 KiB

<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\gii\generators\crud;
use Yii;
use yii\db\ActiveRecord;
use yii\db\BaseActiveRecord;
use yii\db\Schema;
use yii\gii\CodeFile;
use yii\helpers\Inflector;
use yii\web\Controller;
/**
* Generates CRUD
*
* @property array $columnNames Model column names. This property is read-only.
* @property string $controllerID The controller ID (without the module ID prefix). This property is
* read-only.
* @property array $searchAttributes Searchable attributes. This property is read-only.
* @property boolean|\yii\db\TableSchema $tableSchema This property is read-only.
* @property string $viewPath The action view file path. This property is read-only.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Generator extends \yii\gii\Generator
{
public $modelClass;
public $moduleID;
public $controllerClass;
public $baseControllerClass = 'yii\web\Controller';
public $indexWidgetType = 'grid';
public $searchModelClass;
/**
* @inheritdoc
*/
public function getName()
{
return 'CRUD Generator';
}
/**
* @inheritdoc
*/
public function getDescription()
{
return 'This generator generates a controller and views that implement CRUD (Create, Read, Update, Delete)
operations for the specified data model.';
}
/**
* @inheritdoc
*/
public function rules()
{
return array_merge(parent::rules(), [
[['moduleID', 'controllerClass', 'modelClass', 'searchModelClass', 'baseControllerClass'], 'filter', 'filter' => 'trim'],
[['modelClass', 'searchModelClass', 'controllerClass', 'baseControllerClass', 'indexWidgetType'], 'required'],
[['searchModelClass'], 'compare', 'compareAttribute' => 'modelClass', 'operator' => '!==', 'message' => 'Search Model Class must not be equal to Model Class.'],
[['modelClass', 'controllerClass', 'baseControllerClass', 'searchModelClass'], 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'],
[['modelClass'], 'validateClass', 'params' => ['extends' => BaseActiveRecord::className()]],
[['baseControllerClass'], 'validateClass', 'params' => ['extends' => Controller::className()]],
[['controllerClass'], 'match', 'pattern' => '/Controller$/', 'message' => 'Controller class name must be suffixed with "Controller".'],
[['controllerClass', 'searchModelClass'], 'validateNewClass'],
[['indexWidgetType'], 'in', 'range' => ['grid', 'list']],
[['modelClass'], 'validateModelClass'],
[['moduleID'], 'validateModuleID'],
]);
}
/**
* @inheritdoc
*/
public function attributeLabels()
{
return array_merge(parent::attributeLabels(), [
'modelClass' => 'Model Class',
'moduleID' => 'Module ID',
'controllerClass' => 'Controller Class',
'baseControllerClass' => 'Base Controller Class',
'indexWidgetType' => 'Widget Used in Index Page',
'searchModelClass' => 'Search Model Class',
]);
}
/**
* @inheritdoc
*/
public function hints()
{
return [
'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>.',
'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>',
'searchModelClass' => 'This is the name of the search model class to be generated. You should provide a fully
qualified namespaced class name, e.g., <code>app\models\search\PostSearch</code>.',
];
}
/**
* @inheritdoc
*/
public function requiredTemplates()
{
return ['controller.php'];
}
/**
* @inheritdoc
*/
public function stickyAttributes()
{
return ['baseControllerClass', 'moduleID', 'indexWidgetType'];
}
/**
* Checks if model class is valid
*/
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).");
}
}
/**
* Checks if model ID is valid
*/
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
*/
public function generate()
{
$controllerFile = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->controllerClass, '\\')) . '.php');
$searchModel = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->searchModelClass, '\\') . '.php'));
$files = [
new CodeFile($controllerFile, $this->render('controller.php')),
new CodeFile($searchModel, $this->render('search.php')),
];
$viewPath = $this->getViewPath();
$templatePath = $this->getTemplatePath() . '/views';
foreach (scandir($templatePath) as $file) {
if (is_file($templatePath . '/' . $file) && pathinfo($file, PATHINFO_EXTENSION) === 'php') {
$files[] = new CodeFile("$viewPath/$file", $this->render("views/$file"));
}
}
return $files;
}
/**
* @return string the controller ID (without the module ID prefix)
*/
public function getControllerID()
{
$pos = strrpos($this->controllerClass, '\\');
$class = substr(substr($this->controllerClass, $pos + 1), 0, -10);
return Inflector::camel2id($class);
}
/**
* @return string the action view file path
*/
public function getViewPath()
{
$module = empty($this->moduleID) ? Yii::$app : Yii::$app->getModule($this->moduleID);
return $module->getViewPath() . '/' . $this->getControllerID() ;
}
public function getNameAttribute()
{
foreach ($this->getColumnNames() as $name) {
if (!strcasecmp($name, 'name') || !strcasecmp($name, 'title')) {
return $name;
}
}
/** @var \yii\db\ActiveRecord $class */
$class = $this->modelClass;
$pk = $class::primaryKey();
return $pk[0];
}
/**
* Generates code for active field
* @param string $attribute
* @return string
*/
public function generateActiveField($attribute)
{
$tableSchema = $this->getTableSchema();
if ($tableSchema === false || !isset($tableSchema->columns[$attribute])) {
if (preg_match('/^(password|pass|passwd|passcode)$/i', $attribute)) {
return "\$form->field(\$model, '$attribute')->passwordInput()";
} else {
return "\$form->field(\$model, '$attribute')";
}
}
$column = $tableSchema->columns[$attribute];
if ($column->phpType === 'boolean') {
return "\$form->field(\$model, '$attribute')->checkbox()";
} elseif ($column->type === 'text') {
return "\$form->field(\$model, '$attribute')->textarea(['rows' => 6])";
} else {
if (preg_match('/^(password|pass|passwd|passcode)$/i', $column->name)) {
$input = 'passwordInput';
} else {
$input = 'textInput';
}
if ($column->phpType !== 'string' || $column->size === null) {
return "\$form->field(\$model, '$attribute')->$input()";
} else {
return "\$form->field(\$model, '$attribute')->$input(['maxlength' => $column->size])";
}
}
}
/**
* Generates code for active search field
* @param string $attribute
* @return string
*/
public function generateActiveSearchField($attribute)
{
$tableSchema = $this->getTableSchema();
if ($tableSchema === false) {
return "\$form->field(\$model, '$attribute')";
}
$column = $tableSchema->columns[$attribute];
if ($column->phpType === 'boolean') {
return "\$form->field(\$model, '$attribute')->checkbox()";
} else {
return "\$form->field(\$model, '$attribute')";
}
}
/**
* Generates column format
* @param \yii\db\ColumnSchema $column
* @return string
*/
public function generateColumnFormat($column)
{
if ($column->phpType === 'boolean') {
return 'boolean';
} elseif ($column->type === 'text') {
return 'ntext';
} elseif (stripos($column->name, 'time') !== false && $column->phpType === 'integer') {
return 'datetime';
} elseif (stripos($column->name, 'email') !== false) {
return 'email';
} elseif (stripos($column->name, 'url') !== false) {
return 'url';
} else {
return 'text';
}
}
/**
* Generates validation rules for the search model.
* @return array the generated validation rules
*/
public function generateSearchRules()
{
if (($table = $this->getTableSchema()) === false) {
return ["[['" . implode("', '", $this->getColumnNames()) . "'], 'safe']"];
}
$types = [];
foreach ($table->columns as $column) {
switch ($column->type) {
case Schema::TYPE_SMALLINT:
case Schema::TYPE_INTEGER:
case Schema::TYPE_BIGINT:
$types['integer'][] = $column->name;
break;
case Schema::TYPE_BOOLEAN:
$types['boolean'][] = $column->name;
break;
case Schema::TYPE_FLOAT:
case Schema::TYPE_DECIMAL:
case Schema::TYPE_MONEY:
$types['number'][] = $column->name;
break;
case Schema::TYPE_DATE:
case Schema::TYPE_TIME:
case Schema::TYPE_DATETIME:
case Schema::TYPE_TIMESTAMP:
default:
$types['safe'][] = $column->name;
break;
}
}
$rules = [];
foreach ($types as $type => $columns) {
$rules[] = "[['" . implode("', '", $columns) . "'], '$type']";
}
return $rules;
}
/**
* @return array searchable attributes
*/
public function getSearchAttributes()
{
return $this->getColumnNames();
}
/**
* Generates the attribute labels for the search model.
* @return array the generated attribute labels (name => label)
*/
public function generateSearchLabels()
{
/** @var \yii\base\Model $model */
$model = new $this->modelClass();
$attributeLabels = $model->attributeLabels();
$labels = [];
foreach ($this->getColumnNames() as $name) {
if (isset($attributeLabels[$name])) {
$labels[$name] = $attributeLabels[$name];
} else {
if (!strcasecmp($name, 'id')) {
$labels[$name] = 'ID';
} else {
$label = Inflector::camel2words($name);
if (strcasecmp(substr($label, -3), ' id') === 0) {
$label = substr($label, 0, -3) . ' ID';
}
$labels[$name] = $label;
}
}
}
return $labels;
}
/**
* Generates search conditions
* @return array
*/
public function generateSearchConditions()
{
$columns = [];
if (($table = $this->getTableSchema()) === false) {
$class = $this->modelClass;
/** @var \yii\base\Model $model */
$model = new $class();
foreach ($model->attributes() as $attribute) {
$columns[$attribute] = 'unknown';
}
} else {
foreach ($table->columns as $column) {
$columns[$column->name] = $column->type;
}
}
$conditions = [];
foreach ($columns as $column => $type) {
switch ($type) {
case Schema::TYPE_SMALLINT:
case Schema::TYPE_INTEGER:
case Schema::TYPE_BIGINT:
case Schema::TYPE_BOOLEAN:
case Schema::TYPE_FLOAT:
case Schema::TYPE_DECIMAL:
case Schema::TYPE_MONEY:
case Schema::TYPE_DATE:
case Schema::TYPE_TIME:
case Schema::TYPE_DATETIME:
case Schema::TYPE_TIMESTAMP:
$conditions[] = "\$this->addCondition(\$query, '{$column}');";
break;
default:
$conditions[] = "\$this->addCondition(\$query, '{$column}', true);";
break;
}
}
return $conditions;
}
/**
* Generates URL parameters
* @return string
*/
public function generateUrlParams()
{
/** @var ActiveRecord $class */
$class = $this->modelClass;
$pks = $class::primaryKey();
if (count($pks) === 1) {
return "'id' => \$model->{$pks[0]}";
} else {
$params = [];
foreach ($pks as $pk) {
$params[] = "'$pk' => \$model->$pk";
}
return implode(', ', $params);
}
}
/**
* Generates action parameters
* @return string
*/
public function generateActionParams()
{
/** @var ActiveRecord $class */
$class = $this->modelClass;
$pks = $class::primaryKey();
if (count($pks) === 1) {
return '$id';
} else {
return '$' . implode(', $', $pks);
}
}
/**
* Generates parameter tags for phpdoc
* @return array parameter tags for phpdoc
*/
public function generateActionParamComments()
{
/** @var ActiveRecord $class */
$class = $this->modelClass;
$pks = $class::primaryKey();
if (($table = $this->getTableSchema()) === false) {
$params = [];
foreach ($pks as $pk) {
$params[] = '@param ' . (substr(strtolower($pk), -2) == 'id' ? 'integer' : 'string') . ' $' . $pk;
}
return $params;
}
if (count($pks) === 1) {
return ['@param ' . $table->columns[$pks[0]]->phpType . ' $id'];
} else {
$params = [];
foreach ($pks as $pk) {
$params[] = '@param ' . $table->columns[$pk]->phpType . ' $' . $pk;
}
return $params;
}
}
/**
* Returns table schema for current model class or false if it is not an active record
* @return boolean|\yii\db\TableSchema
*/
public function getTableSchema()
{
/** @var ActiveRecord $class */
$class = $this->modelClass;
if (is_subclass_of($class, 'yii\db\ActiveRecord')) {
return $class::getTableSchema();
} else {
return false;
}
}
/**
* @return array model column names
*/
public function getColumnNames()
{
/** @var ActiveRecord $class */
$class = $this->modelClass;
if (is_subclass_of($class, 'yii\db\ActiveRecord')) {
return $class::getTableSchema()->getColumnNames();
} else {
/** @var \yii\base\Model $model */
$model = new $class();
return $model->attributes();
}
}
}