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.
		
		
		
		
		
			
		
			
				
					
					
						
							436 lines
						
					
					
						
							12 KiB
						
					
					
				
			
		
		
	
	
							436 lines
						
					
					
						
							12 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; | |
|  | |
| /** | |
|  * | |
|  * @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; | |
|  | |
| 	public function getName() | |
| 	{ | |
| 		return 'CRUD Generator'; | |
| 	} | |
|  | |
| 	public function getDescription() | |
| 	{ | |
| 		return 'This generator generates a controller and views that implement CRUD (Create, Read, Update, Delete) | |
| 			operations for the specified data model.'; | |
| 	} | |
|  | |
| 	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'], | |
| 		]); | |
| 	} | |
|  | |
| 	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 class representing the data being collected in the search form. | |
| 			 	A fully qualified namespaced class name is required, e.g., <code>app\models\search\PostSearch</code>.', | |
| 		]; | |
| 	} | |
|  | |
| 	public function requiredTemplates() | |
| 	{ | |
| 		return ['controller.php']; | |
| 	} | |
|  | |
| 	/** | |
| 	 * @inheritdoc | |
| 	 */ | |
| 	public function stickyAttributes() | |
| 	{ | |
| 		return ['baseControllerClass', 'moduleID', 'indexWidgetType']; | |
| 	} | |
|  | |
| 	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 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]; | |
| 	} | |
|  | |
| 	/** | |
| 	 * @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])"; | |
| 			} | |
| 		} | |
| 	} | |
|  | |
| 	/** | |
| 	 * @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')"; | |
| 		} | |
| 	} | |
|  | |
| 	/** | |
| 	 * @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; | |
| 	} | |
|  | |
| 	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() | |
| 	{ | |
| 		$labels = []; | |
| 		foreach ($this->getColumnNames() as $name) { | |
| 			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; | |
| 	} | |
|  | |
| 	public function generateSearchConditions() | |
| 	{ | |
| 		$columns = []; | |
| 		if (($table = $this->getTableSchema()) === false) { | |
| 			$class = $this->modelClass; | |
| 			$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; | |
| 	} | |
|  | |
| 	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); | |
| 		} | |
| 	} | |
|  | |
| 	public function generateActionParams() | |
| 	{ | |
| 		/** @var ActiveRecord $class */ | |
| 		$class = $this->modelClass; | |
| 		$pks = $class::primaryKey(); | |
| 		if (count($pks) === 1) { | |
| 			return '$id'; | |
| 		} else { | |
| 			return '$' . implode(', $', $pks); | |
| 		} | |
| 	} | |
|  | |
| 	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; | |
| 		} | |
| 	} | |
|  | |
| 	public function getTableSchema() | |
| 	{ | |
| 		/** @var ActiveRecord $class */ | |
| 		$class = $this->modelClass; | |
| 		if (is_subclass_of($class, 'yii\db\ActiveRecord')) { | |
| 			return $class::getTableSchema(); | |
| 		} else { | |
| 			return false; | |
| 		} | |
| 	} | |
|  | |
| 	public function getColumnNames() | |
| 	{ | |
| 		/** @var ActiveRecord $class */ | |
| 		$class = $this->modelClass; | |
| 		if (is_subclass_of($class, 'yii\db\ActiveRecord')) { | |
| 			return $class::getTableSchema()->getColumnNames(); | |
| 		} else { | |
| 			$model = new $class(); | |
| 			return $model->attributes(); | |
| 		} | |
| 	} | |
|  | |
| }
 | |
| 
 |