From 2a152cb55503de0b69375eff171a88e342570314 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Wed, 21 Aug 2013 06:13:18 -0400 Subject: [PATCH] model generator WIP --- framework/yii/gii/Generator.php | 15 +- .../yii/gii/generators/controller/Generator.php | 1 - framework/yii/gii/generators/model/Generator.php | 202 +++++++++++---------- framework/yii/gii/generators/model/form.php | 4 +- .../yii/gii/generators/model/templates/model.php | 152 +++------------- 5 files changed, 134 insertions(+), 240 deletions(-) diff --git a/framework/yii/gii/Generator.php b/framework/yii/gii/Generator.php index eb7b696..9071518 100644 --- a/framework/yii/gii/Generator.php +++ b/framework/yii/gii/Generator.php @@ -294,11 +294,10 @@ abstract class Generator extends Model } /** - * Validates an attribute to make sure it is not taking a PHP reserved keyword. - * @param string $attribute the attribute to be validated - * @param array $params validation parameters + * @param string $value the attribute to be validated + * @return boolean whether the value is a reserved PHP keyword. */ - public function validateReservedWord($attribute, $params) + public function isReservedKeyword($value) { static $keywords = array( '__class__', @@ -381,12 +380,6 @@ abstract class Generator extends Model 'while', 'xor', ); - $value = $this->$attribute; - foreach (explode('\\', strtolower($value)) as $name) { - if (in_array($name, $keywords)) { - $this->addError($attribute, $this->getAttributeLabel($attribute) . ' cannot take a reserved PHP keyword.'); - return; - } - } + return in_array(strtolower($value), $keywords, true); } } diff --git a/framework/yii/gii/generators/controller/Generator.php b/framework/yii/gii/generators/controller/Generator.php index df7c129..c57b2b2 100644 --- a/framework/yii/gii/generators/controller/Generator.php +++ b/framework/yii/gii/generators/controller/Generator.php @@ -65,7 +65,6 @@ class Generator extends \yii\gii\Generator array('controller', 'match', 'pattern' => '/^[a-z\\-\\/]*$/', 'message' => 'Only a-z, dashes (-) and slashes (/) are allowed.'), array('actions', 'match', 'pattern' => '/^[a-z\\-,\\s]*$/', 'message' => 'Only a-z, dashes (-), spaces and commas are allowed.'), array('baseClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'), - array('baseClass', 'validateReservedWord'), array('ns', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'), )); } diff --git a/framework/yii/gii/generators/model/Generator.php b/framework/yii/gii/generators/model/Generator.php index e60dac7..6253dc3 100644 --- a/framework/yii/gii/generators/model/Generator.php +++ b/framework/yii/gii/generators/model/Generator.php @@ -9,7 +9,9 @@ namespace yii\gii\generators\model; use Yii; use yii\base\InvalidConfigException; +use yii\db\Connection; use yii\gii\CodeFile; +use yii\helpers\Inflector; /** * @@ -19,17 +21,13 @@ use yii\gii\CodeFile; class Generator extends \yii\gii\Generator { public $db = 'db'; + public $ns = 'app\models'; public $tableName; public $modelClass; public $baseClass = '\yii\db\ActiveRecord'; - public $buildRelations = true; + public $generateRelations = 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 getName() { @@ -44,29 +42,30 @@ class Generator extends \yii\gii\Generator public function rules() { return array_merge(parent::rules(), array( - array('tablePrefix, baseClass, tableName, modelClass, modelPath, connectionId', 'filter', 'filter' => 'trim'), - array('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('db, ns, tableName, modelClass, baseClass', 'filter', 'filter' => 'trim'), + array('db, ns, tableName, baseClass', 'required'), + array('db, modelClass', 'match', 'pattern' => '/^\w+$/', 'message' => 'Only word characters are allowed.'), + array('ns, baseClass', 'match', 'pattern' => '/^[\w\\\\]+$/', 'message' => 'Only word characters and backslashes are allowed.'), + array('tableName', 'match', 'pattern' => '/^(\w+\.)?[\w\.\*]+$/', 'message' => 'Only word characters, asterisks and dot are allowed.'), + array('db', 'validateDb'), + array('ns', 'validateNamespace'), array('tableName', 'validateTableName'), - 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'), - array('baseClass, modelClass', 'validateReservedWord'), + array('modelClass', 'validateModelClass'), array('baseClass', 'validateBaseClass'), + array('generateRelations, commentsAsLabels', 'boolean'), )); } public function attributeLabels() { return array( - 'tablePrefix' => 'Table Prefix', + 'ns' => 'Namespace', + 'db' => 'Database Connection ID', 'tableName' => 'Table Name', - 'modelPath' => 'Model Path', 'modelClass' => 'Model Class', 'baseClass' => 'Base Class', - 'buildRelations' => 'Build Relations', + 'generateRelations' => 'Generate Relations', 'commentsAsLabels' => 'Use Column Comments as Attribute Labels', - 'connectionId' => 'Database Connection', ); } @@ -79,14 +78,17 @@ class Generator extends \yii\gii\Generator public function stickyAttributes() { - return array('tablePrefix', 'modelPath', 'baseClass', 'buildRelations', 'commentsAsLabels'); + return array('ns', 'db', 'baseClass', 'generateRelations', 'commentsAsLabels'); + } + + public function getDbConnection() + { + return Yii::$app->{$this->db}; } public function generate() { - if (($db = Yii::$app->{$this->db}) === null) { - throw new InvalidConfigException('The "db" property must refer to a valid DB connection.'); - } + $db = $this->getDbConnection(); if (($pos = strrpos($this->tableName, '.')) !== false) { $schema = substr($this->tableName, 0, $pos); @@ -96,23 +98,20 @@ class Generator extends \yii\gii\Generator $tableName = $this->tableName; } if (strpos($tableName, '*') !== false) { - $tables = $db->getSchema()->getTableSchemas($schema); + $tables = $db->getSchema()->getTableNames($schema); } else { $tables = array($db->getTableSchema($this->tableName, true)); } $files = array(); - $relations = $this->generateRelations(); foreach ($tables as $table) { $className = $this->generateClassName($table->name); $params = array( 'tableName' => $schema === '' ? $tableName : $schema . '.' . $tableName, - 'modelClass' => $className, + 'className' => $className, 'columns' => $table->columns, 'labels' => $this->generateLabels($table), - 'rules' => $this->generateRules($table), - 'relations' => isset($this->relations[$className]) ? $this->relations[$className] : array(), ); $files[] = new CodeFile( Yii::getAlias($this->modelPath) . '/' . $className . '.php', @@ -123,55 +122,6 @@ class Generator extends \yii\gii\Generator return $files; } - 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" @@ -187,38 +137,23 @@ class Generator extends \yii\gii\Generator } } - 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); + $db = $this->getDbConnection(); + return $db->getSchema()->getTable($tableName, true); } public function generateLabels($table) { $labels = array(); foreach ($table->columns as $column) { - if ($this->commentsAsLabels && $column->comment) { + if ($this->commentsAsLabels && !empty($column->comment)) { $labels[$column->name] = $column->comment; } else { - $label = ucwords(trim(strtolower(str_replace(array('-', '_'), ' ', preg_replace('/(?name))))); - $label = preg_replace('/\s+/', ' ', $label); + $label = Inflector::camel2words($column->name); if (strcasecmp(substr($label, -3), ' id') === 0) { - $label = substr($label, 0, -3); - } - if ($label === 'Id') { - $label = 'ID'; + $label = substr($label, 0, -3) . ' ID'; } - $label = str_replace("'", "\\'", $label); $labels[$column->name] = $label; } } @@ -306,7 +241,7 @@ class Generator extends \yii\gii\Generator protected function generateRelations() { - if (!$this->buildRelations) { + if (!$this->generateRelations) { return array(); } @@ -445,10 +380,79 @@ class Generator extends \yii\gii\Generator return $name; } - public function validateConnectionId($attribute, $params) + public function validateDb() { - 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.'); + 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.'); + } + } + + public function validateNamespace() + { + } + + public function validateModelClass() + { + if ($this->isReservedKeyword($this->modelClass)) { + $this->addError('modelClass', 'The name is a reserved PHP keyword.'); + } + if (strpos($this->tableName, '*') === false && $this->modelClass == '') { + $this->addError('modelClass', 'Model Class cannot be blank.'); + } + } + + 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); + } 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; + } + } + } + } 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) . "."); + } + } + + 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."); } } } diff --git a/framework/yii/gii/generators/model/form.php b/framework/yii/gii/generators/model/form.php index d5ffd0b..3aa9ffe 100644 --- a/framework/yii/gii/generators/model/form.php +++ b/framework/yii/gii/generators/model/form.php @@ -7,6 +7,8 @@ echo $form->field($generator, 'tableName'); echo $form->field($generator, 'modelClass'); +echo $form->field($generator, 'ns'); echo $form->field($generator, 'baseClass'); -echo $form->field($generator, 'buildRelations')->checkbox(); +echo $form->field($generator, 'db'); +echo $form->field($generator, 'generateRelations')->checkbox(); echo $form->field($generator, 'commentsAsLabels')->checkbox(); diff --git a/framework/yii/gii/generators/model/templates/model.php b/framework/yii/gii/generators/model/templates/model.php index 7e4b148..18ed2fe 100644 --- a/framework/yii/gii/generators/model/templates/model.php +++ b/framework/yii/gii/generators/model/templates/model.php @@ -1,7 +1,14 @@ CDbColumnSchema) @@ -9,51 +16,29 @@ * - $rules: list of validation rules * - $relations: list of relations (name=>relation declaration) */ + +$pos = strrpos($className, '\\'); +$ns = ltrim(substr($className, 0, $pos), '\\'); +$className = substr($className, $pos + 1); + +echo " - + +namespace ; /** * This is the model class for table "". * - * The followings are the available columns in table '': - - * @property type.' $'.$column->name."\n"; ?> - - + * Attributes: * - * The followings are the available model relations: -$relation): ?> - * @property + + * @property phpType} \${$column->name}\n"; ?> - */ -class extends baseClass."\n"; ?> +class extends baseClass, '\\') . "\n"; ?> { /** - * @return string the associated database table name + * @inheritdoc */ public function tableName() { @@ -61,103 +46,14 @@ class extends baseClass."\n"; ?> } /** - * @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( - - - - // The following rule is used by search(). - // @todo Please remove those attributes that should not be searched. - array('', '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( -$relation): ?> - $relation,\n"; ?> - - ); - } - - /** - * @return array customized attribute labels (name=>label) + * @inheritdoc */ public function attributeLabels() { return array( -$label): ?> - '$label',\n"; ?> + $label): ?> + '" . addslashes($label) . "',\n"; ?> ); } - - /** - * 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; - -$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, - )); - } - - - /** - * @return CDbConnection the database connection used for this class - */ - public function getDbConnection() - { - return Yii::app()->; - } - - - /** - * 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 the static model class - */ - public static function model($className=__CLASS__) - { - return parent::model($className); - } }