Commit 95bc36c2 by Qiang Xue

refactored generators.

parent a9e71d55
......@@ -294,6 +294,28 @@ abstract class Generator extends Model
}
/**
* An inline validator that checks if the attribute value refers to an existing class name.
* If the `extends` option is specified, it will also check if the class is a child class
* of the class represented by the `extends` option.
* @param string $attribute the attribute being validated
* @param array $params the validation options
*/
public function validateClass($attribute, $params)
{
try {
if (class_exists($this->$attribute)) {
if (isset($params['extends']) && !is_subclass_of($this->$attribute, $params['extends'])) {
$this->addError($attribute, "'{$this->$attribute}' must extend from {$params['extends']} or its child class.");
}
} else {
$this->addError($attribute, "Class '{$this->$attribute}' does not exist or has syntax error.");
}
} catch (\Exception $e) {
$this->addError($attribute, "Class '{$this->$attribute}' does not exist or has syntax error.");
}
}
/**
* @param string $value the attribute to be validated
* @return boolean whether the value is a reserved PHP keyword.
*/
......
......@@ -63,7 +63,7 @@ class Generator extends \yii\gii\Generator
array('modelClass, viewName, scenarioName, viewPath', 'filter', 'filter' => 'trim'),
array('modelClass, viewName, viewPath', 'required'),
array('modelClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'),
array('modelClass', 'validateModel'),
array('modelClass', 'validateClass', 'extends' => Model::className()),
array('viewName', 'match', 'pattern' => '/^\w+[\\-\\/\w]*$/', 'message' => 'Only word characters, dashes and slashes are allowed.'),
array('viewPath', 'match', 'pattern' => '/^@?\w+[\\-\\/\w]*$/', 'message' => 'Only word characters, dashes, slashes and @ are allowed.'),
array('viewPath', 'validateViewPath'),
......@@ -130,25 +130,6 @@ EOD;
}
/**
* Validates the model class to make sure it exists and is valid.
*/
public function validateModel()
{
try {
if (class_exists($this->modelClass)) {
if (!is_subclass_of($this->modelClass, Model::className())) {
$this->addError('modelClass', "'{$this->modelClass}' must extend from Model or its child class.");
}
} else {
$this->addError('modelClass', "Class '{$this->modelClass}' does not exist or has syntax error.");
}
} catch (\Exception $e) {
$this->addError('modelClass', "Class '{$this->modelClass}' does not exist or has syntax error.");
return;
}
}
/**
* Validates [[viewPath]] to make sure it is a valid path or path alias and exists.
*/
public function validateViewPath()
......
......@@ -8,6 +8,7 @@
namespace yii\gii\generators\model;
use Yii;
use yii\db\ActiveRecord;
use yii\db\Connection;
use yii\gii\CodeFile;
use yii\helpers\Inflector;
......@@ -50,7 +51,7 @@ class Generator extends \yii\gii\Generator
array('ns', 'validateNamespace'),
array('tableName', 'validateTableName'),
array('modelClass', 'validateModelClass'),
array('baseClass', 'validateBaseClass'),
array('baseClass', 'validateClass', 'params' => array('extends' => ActiveRecord::className())),
array('generateRelations, generateLabelsFromComments', 'boolean'),
));
}
......@@ -83,7 +84,7 @@ class Generator extends \yii\gii\Generator
'baseClass' => 'This is the base class of the new ActiveRecord class. It should be a fully qualified namespaced class name.',
'generateRelations' => 'This indicates whether the generator should generate relations based on
foreign key constraints it detects in the database. Note that if your database contains too many tables,
you may want to uncheck this option to accelerate the code generation process.',
you may want to uncheck this option to accelerate the code generation proc ess.',
'generateLabelsFromComments' => 'This indicates whether the generator should generate attribute labels
by using the comments of the corresponding DB columns.',
);
......@@ -101,6 +102,9 @@ class Generator extends \yii\gii\Generator
return array('ns', 'db', 'baseClass', 'generateRelations', 'generateLabelsFromComments');
}
/**
* @return Connection
*/
public function getDbConnection()
{
return Yii::$app->{$this->db};
......@@ -108,33 +112,18 @@ class Generator extends \yii\gii\Generator
public function generate()
{
$db = $this->getDbConnection();
if (($pos = strrpos($this->tableName, '.')) !== false) {
$schema = substr($this->tableName, 0, $pos);
$tableName = substr($this->tableName, $pos + 1);
} else {
$schema = '';
$tableName = $this->tableName;
}
if (strpos($tableName, '*') !== false) {
$tables = $db->getSchema()->getTableNames($schema);
} else {
$tables = array($db->getTableSchema($this->tableName, true));
}
$files = array();
foreach ($tables as $table) {
$className = $this->generateClassName($table->name);
foreach ($this->getTableNames() as $tableName) {
$className = $this->generateClassName($tableName);
$tableSchema = $this->getTableSchema($tableName);
$params = array(
'tableName' => $schema === '' ? $tableName : $schema . '.' . $tableName,
'tableName' => $tableName,
'className' => $className,
'columns' => $table->columns,
'labels' => $this->generateLabels($table),
'tableSchema' => $tableSchema,
'labels' => $this->generateLabels($tableSchema),
);
$files[] = new CodeFile(
Yii::getAlias($this->modelPath) . '/' . $className . '.php',
Yii::getAlias('@' . $this->ns) . '/' . $className . '.php',
$this->render('model.php', $params)
);
}
......@@ -142,25 +131,9 @@ class Generator extends \yii\gii\Generator
return $files;
}
/*
* 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 getTableSchema($tableName)
{
$db = $this->getDbConnection();
return $db->getSchema()->getTable($tableName, true);
return $this->getDbConnection()->getTableSchema($tableName, true);
}
public function generateLabels($table)
......@@ -403,18 +376,22 @@ class Generator extends \yii\gii\Generator
public function validateDb()
{
if (Yii::$app->hasComponent($this->db) === false || !(Yii::$app->getComponent($this->db) instanceof Connection)) {
$this->addError('db', 'A valid database connection is required to run this generator.');
$this->addError('db', 'Database Connection ID must refer to a valid application component.');
}
}
public function validateNamespace()
{
$path = Yii::getAlias('@' . ltrim($this->ns, '\\'), false);
if ($path === false) {
$this->addError('ns', 'Namespace must be associated with an existing directory.');
}
}
public function validateModelClass()
{
if ($this->isReservedKeyword($this->modelClass)) {
$this->addError('modelClass', 'The name is a reserved PHP keyword.');
$this->addError('modelClass', 'Class name cannot be a reserved PHP keyword.');
}
if (strpos($this->tableName, '*') === false && $this->modelClass == '') {
$this->addError('modelClass', 'Model Class cannot be blank.');
......@@ -423,56 +400,41 @@ class Generator extends \yii\gii\Generator
public function validateTableName()
{
$invalidTables = array();
$invalidColumns = array();
if ($this->tableName[strlen($this->tableName) - 1] === '*') {
if (($pos = strrpos($this->tableName, '.')) !== false) {
$schema = substr($this->tableName, 0, $pos);
$tables = $this->getTableNames();
if (empty($tables)) {
$this->addError('tableName', "Table '{$this->tableName}' does not exist.'");
} else {
$schema = '';
}
$this->modelClass = '';
$tables = $this->getDbConnection()->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;
$class = $this->generateClassName($table);
if ($this->isReservedKeyword($class)) {
$this->addError('tableName', "Table '$table' would generate a class which is a reserved PHP keyword.");
break;
}
}
}
} 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;
}
protected function getTableNames()
{
$db = $this->getDbConnection();
$tableNames = array();
if ($this->tableName[strlen($this->tableName) - 1] === '*') {
if (($pos = strrpos($this->tableName, '.')) !== false) {
$schema = substr($this->tableName, 0, $pos);
$pattern = '/' . str_replace('*', '\w+', substr($this->tableName, $pos + 1)) . '/';
} else {
$schema = '';
$pattern = '/' . str_replace('*', '\w+', $this->tableName) . '/';
}
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) . ".");
foreach ($db->schema->getTableNames($schema) as $table) {
if (preg_match($pattern, $table)) {
$tableNames[] = $schema === '' ? $table : ($schema . '.' . $table);
}
}
public function validateBaseClass()
{
$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.");
} elseif (($table = $db->getTableSchema($this->tableName, true)) !== null) {
$tableNames[] = $this->tableName;
}
return $tableNames;
}
}
......@@ -5,7 +5,7 @@
* @var yii\gii\generators\form\Generator $generator
*/
echo $form->field($generator, 'tableName');
echo $form->field($generator, 'tableNamess');
echo $form->field($generator, 'modelClass');
echo $form->field($generator, 'ns');
echo $form->field($generator, 'baseClass');
......
......@@ -6,12 +6,12 @@
* @var yii\gii\generators\model\Generator $generator
* @var string $tableName
* @var string $className
* @var yii\db\ColumnSchema[] $columns
* @var yii\db\TableSchema $tableSchema
* @var string[] $labels
*
* - $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)
* - $tableSchema: 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)
......@@ -31,7 +31,7 @@ namespace <?php echo $ns; ?>;
*
* Attributes:
*
<?php foreach ($columns as $column): ?>
<?php foreach ($tableSchema->columns as $column): ?>
* @property <?php echo "{$column->phpType} \${$column->name}\n"; ?>
<?php endforeach; ?>
*/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment