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.
		
		
		
		
			
				
					556 lines
				
				17 KiB
			
		
		
			
		
	
	
					556 lines
				
				17 KiB
			| 
											12 years ago
										 | <?php
 | ||
|  | /**
 | ||
|  |  * @link http://www.yiiframework.com/
 | ||
|  |  * @copyright Copyright (c) 2008 Yii Software LLC
 | ||
|  |  * @license http://www.yiiframework.com/license/
 | ||
|  |  */
 | ||
|  | 
 | ||
|  | namespace yii\gii\generators\model;
 | ||
|  | 
 | ||
| 
											12 years ago
										 | use Yii;
 | ||
| 
											12 years ago
										 | use yii\db\ActiveRecord;
 | ||
| 
											12 years ago
										 | use yii\db\Connection;
 | ||
| 
											12 years ago
										 | use yii\db\Schema;
 | ||
| 
											12 years ago
										 | use yii\gii\CodeFile;
 | ||
| 
											12 years ago
										 | use yii\helpers\Inflector;
 | ||
| 
											12 years ago
										 | 
 | ||
| 
											12 years ago
										 | /**
 | ||
| 
											12 years ago
										 |  * This generator will generate one or multiple ActiveRecord classes for the specified database table.
 | ||
| 
											12 years ago
										 |  *
 | ||
|  |  * @author Qiang Xue <qiang.xue@gmail.com>
 | ||
|  |  * @since 2.0
 | ||
|  |  */
 | ||
|  | class Generator extends \yii\gii\Generator
 | ||
|  | {
 | ||
| 
											12 years ago
										 | 	public $db = 'db';
 | ||
| 
											12 years ago
										 | 	public $ns = 'app\models';
 | ||
| 
											12 years ago
										 | 	public $tableName;
 | ||
|  | 	public $modelClass;
 | ||
| 
											12 years ago
										 | 	public $baseClass = 'yii\db\ActiveRecord';
 | ||
| 
											12 years ago
										 | 	public $generateRelations = true;
 | ||
| 
											12 years ago
										 | 	public $generateLabelsFromComments = false;
 | ||
| 
											12 years ago
										 | 
 | ||
|  | 
 | ||
| 
											12 years ago
										 | 	/**
 | ||
| 
											12 years ago
										 | 	 * @inheritdoc
 | ||
| 
											12 years ago
										 | 	 */
 | ||
| 
											12 years ago
										 | 	public function getName()
 | ||
|  | 	{
 | ||
|  | 		return 'Model Generator';
 | ||
|  | 	}
 | ||
|  | 
 | ||
| 
											12 years ago
										 | 	/**
 | ||
| 
											12 years ago
										 | 	 * @inheritdoc
 | ||
| 
											12 years ago
										 | 	 */
 | ||
| 
											12 years ago
										 | 	public function getDescription()
 | ||
|  | 	{
 | ||
| 
											12 years ago
										 | 		return 'This generator generates an ActiveRecord class for the specified database table.';
 | ||
| 
											12 years ago
										 | 	}
 | ||
| 
											12 years ago
										 | 
 | ||
| 
											12 years ago
										 | 	/**
 | ||
| 
											12 years ago
										 | 	 * @inheritdoc
 | ||
| 
											12 years ago
										 | 	 */
 | ||
| 
											12 years ago
										 | 	public function rules()
 | ||
|  | 	{
 | ||
| 
											12 years ago
										 | 		return array_merge(parent::rules(), [
 | ||
| 
											12 years ago
										 | 			[['db', 'ns', 'tableName', 'modelClass', 'baseClass'], 'filter', 'filter' => 'trim'],
 | ||
|  | 			[['db', 'ns', 'tableName', 'baseClass'], 'required'],
 | ||
|  | 			[['db', 'modelClass'], 'match', 'pattern' => '/^\w+$/', 'message' => 'Only word characters are allowed.'],
 | ||
|  | 			[['ns', 'baseClass'], 'match', 'pattern' => '/^[\w\\\\]+$/', 'message' => 'Only word characters and backslashes are allowed.'],
 | ||
|  | 			[['tableName'], 'match', 'pattern' => '/^(\w+\.)?([\w\*]+)$/', 'message' => 'Only word characters, and optionally an asterisk and/or a dot are allowed.'],
 | ||
|  | 			[['db'], 'validateDb'],
 | ||
|  | 			[['ns'], 'validateNamespace'],
 | ||
|  | 			[['tableName'], 'validateTableName'],
 | ||
|  | 			[['modelClass'], 'validateModelClass', 'skipOnEmpty' => false],
 | ||
|  | 			[['baseClass'], 'validateClass', 'params' => ['extends' => ActiveRecord::className()]],
 | ||
|  | 			[['generateRelations', 'generateLabelsFromComments'], 'boolean'],
 | ||
| 
											12 years ago
										 | 		]);
 | ||
| 
											12 years ago
										 | 	}
 | ||
|  | 
 | ||
| 
											12 years ago
										 | 	/**
 | ||
| 
											12 years ago
										 | 	 * @inheritdoc
 | ||
| 
											12 years ago
										 | 	 */
 | ||
| 
											12 years ago
										 | 	public function attributeLabels()
 | ||
|  | 	{
 | ||
| 
											12 years ago
										 | 		return [
 | ||
| 
											12 years ago
										 | 			'ns' => 'Namespace',
 | ||
|  | 			'db' => 'Database Connection ID',
 | ||
| 
											12 years ago
										 | 			'tableName' => 'Table Name',
 | ||
|  | 			'modelClass' => 'Model Class',
 | ||
|  | 			'baseClass' => 'Base Class',
 | ||
| 
											12 years ago
										 | 			'generateRelations' => 'Generate Relations',
 | ||
| 
											12 years ago
										 | 			'generateLabelsFromComments' => 'Generate Labels from DB Comments',
 | ||
| 
											12 years ago
										 | 		];
 | ||
| 
											12 years ago
										 | 	}
 | ||
|  | 
 | ||
| 
											12 years ago
										 | 	/**
 | ||
| 
											12 years ago
										 | 	 * @inheritdoc
 | ||
| 
											12 years ago
										 | 	 */
 | ||
| 
											12 years ago
										 | 	public function hints()
 | ||
|  | 	{
 | ||
| 
											12 years ago
										 | 		return [
 | ||
| 
											12 years ago
										 | 			'ns' => 'This is the namespace of the ActiveRecord class to be generated, e.g., <code>app\models</code>',
 | ||
|  | 			'db' => 'This is the ID of the DB application component.',
 | ||
|  | 			'tableName' => 'This is the name of the DB table that the new ActiveRecord class is associated with, e.g. <code>tbl_post</code>.
 | ||
|  | 				The table name may consist of the DB schema part if needed, e.g. <code>public.tbl_post</code>.
 | ||
| 
											12 years ago
										 | 				The table name may end with asterisk to match multiple table names, e.g. <code>tbl_*</code>
 | ||
| 
											12 years ago
										 | 				will match tables who name starts with <code>tbl_</code>. In this case, multiple ActiveRecord classes
 | ||
|  | 				will be generated, one for each matching table name; and the class names will be generated from
 | ||
|  | 				the matching characters. For example, table <code>tbl_post</code> will generate <code>Post</code>
 | ||
|  | 				class.',
 | ||
| 
											12 years ago
										 | 			'modelClass' => 'This is the name of the ActiveRecord class to be generated. The class name should not contain
 | ||
|  | 				the namespace part as it is specified in "Namespace". You do not need to specify the class name
 | ||
| 
											12 years ago
										 | 				if "Table Name" ends with asterisk, in which case multiple ActiveRecord classes will be generated.',
 | ||
| 
											12 years ago
										 | 			'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,
 | ||
| 
											12 years ago
										 | 				you may want to uncheck this option to accelerate the code generation process.',
 | ||
| 
											12 years ago
										 | 			'generateLabelsFromComments' => 'This indicates whether the generator should generate attribute labels
 | ||
|  | 				by using the comments of the corresponding DB columns.',
 | ||
| 
											12 years ago
										 | 		];
 | ||
| 
											12 years ago
										 | 	}
 | ||
|  | 
 | ||
| 
											12 years ago
										 | 	/**
 | ||
| 
											12 years ago
										 | 	 * @inheritdoc
 | ||
| 
											12 years ago
										 | 	 */
 | ||
| 
											12 years ago
										 | 	public function autoCompleteData()
 | ||
|  | 	{
 | ||
| 
											12 years ago
										 | 		$db = $this->getDbConnection();
 | ||
| 
											12 years ago
										 | 		if ($db !== null) {
 | ||
| 
											12 years ago
										 | 			return [
 | ||
|  | 				'tableName' => function () use ($db) {
 | ||
|  | 					return $db->getSchema()->getTableNames();
 | ||
|  | 				},
 | ||
|  | 			];
 | ||
|  | 		} else {
 | ||
|  | 			return [];
 | ||
|  | 		}
 | ||
| 
											12 years ago
										 | 	}
 | ||
|  | 
 | ||
|  | 	/**
 | ||
| 
											12 years ago
										 | 	 * @inheritdoc
 | ||
| 
											12 years ago
										 | 	 */
 | ||
| 
											12 years ago
										 | 	public function requiredTemplates()
 | ||
|  | 	{
 | ||
| 
											12 years ago
										 | 		return ['model.php'];
 | ||
| 
											12 years ago
										 | 	}
 | ||
|  | 
 | ||
| 
											12 years ago
										 | 	/**
 | ||
| 
											12 years ago
										 | 	 * @inheritdoc
 | ||
| 
											12 years ago
										 | 	 */
 | ||
| 
											12 years ago
										 | 	public function stickyAttributes()
 | ||
|  | 	{
 | ||
| 
											12 years ago
										 | 		return ['ns', 'db', 'baseClass', 'generateRelations', 'generateLabelsFromComments'];
 | ||
| 
											12 years ago
										 | 	}
 | ||
|  | 
 | ||
| 
											12 years ago
										 | 	/**
 | ||
| 
											12 years ago
										 | 	 * @inheritdoc
 | ||
| 
											12 years ago
										 | 	 */
 | ||
| 
											12 years ago
										 | 	public function generate()
 | ||
|  | 	{
 | ||
| 
											12 years ago
										 | 		$files = [];
 | ||
| 
											12 years ago
										 | 		$relations = $this->generateRelations();
 | ||
|  | 		$db = $this->getDbConnection();
 | ||
| 
											12 years ago
										 | 		foreach ($this->getTableNames() as $tableName) {
 | ||
|  | 			$className = $this->generateClassName($tableName);
 | ||
| 
											12 years ago
										 | 			$tableSchema = $db->getTableSchema($tableName);
 | ||
| 
											12 years ago
										 | 			$params = [
 | ||
| 
											12 years ago
										 | 				'tableName' => $tableName,
 | ||
| 
											12 years ago
										 | 				'className' => $className,
 | ||
| 
											12 years ago
										 | 				'tableSchema' => $tableSchema,
 | ||
|  | 				'labels' => $this->generateLabels($tableSchema),
 | ||
| 
											12 years ago
										 | 				'rules' => $this->generateRules($tableSchema),
 | ||
| 
											12 years ago
										 | 				'relations' => isset($relations[$className]) ? $relations[$className] : [],
 | ||
|  | 			];
 | ||
| 
											12 years ago
										 | 			$files[] = new CodeFile(
 | ||
| 
											12 years ago
										 | 				Yii::getAlias('@' . str_replace('\\', '/', $this->ns)) . '/' . $className . '.php',
 | ||
| 
											12 years ago
										 | 				$this->render('model.php', $params)
 | ||
| 
											12 years ago
										 | 			);
 | ||
|  | 		}
 | ||
| 
											12 years ago
										 | 
 | ||
|  | 		return $files;
 | ||
| 
											12 years ago
										 | 	}
 | ||
|  | 
 | ||
| 
											12 years ago
										 | 	/**
 | ||
|  | 	 * Generates the attribute labels for the specified table.
 | ||
|  | 	 * @param \yii\db\TableSchema $table the table schema
 | ||
|  | 	 * @return array the generated attribute labels (name => label)
 | ||
|  | 	 */
 | ||
| 
											12 years ago
										 | 	public function generateLabels($table)
 | ||
|  | 	{
 | ||
| 
											12 years ago
										 | 		$labels = [];
 | ||
| 
											12 years ago
										 | 		foreach ($table->columns as $column) {
 | ||
| 
											12 years ago
										 | 			if ($this->generateLabelsFromComments && !empty($column->comment)) {
 | ||
| 
											12 years ago
										 | 				$labels[$column->name] = $column->comment;
 | ||
| 
											12 years ago
										 | 			} elseif (!strcasecmp($column->name, 'id')) {
 | ||
|  | 				$labels[$column->name] = 'ID';
 | ||
| 
											12 years ago
										 | 			} else {
 | ||
| 
											12 years ago
										 | 				$label = Inflector::camel2words($column->name);
 | ||
| 
											12 years ago
										 | 				if (strcasecmp(substr($label, -3), ' id') === 0) {
 | ||
| 
											12 years ago
										 | 					$label = substr($label, 0, -3) . ' ID';
 | ||
| 
											12 years ago
										 | 				}
 | ||
|  | 				$labels[$column->name] = $label;
 | ||
|  | 			}
 | ||
|  | 		}
 | ||
|  | 		return $labels;
 | ||
|  | 	}
 | ||
|  | 
 | ||
| 
											12 years ago
										 | 	/**
 | ||
| 
											12 years ago
										 | 	 * Generates validation rules for the specified table.
 | ||
|  | 	 * @param \yii\db\TableSchema $table the table schema
 | ||
|  | 	 * @return array the generated validation rules
 | ||
| 
											12 years ago
										 | 	 */
 | ||
| 
											12 years ago
										 | 	public function generateRules($table)
 | ||
|  | 	{
 | ||
| 
											12 years ago
										 | 		$types = [];
 | ||
|  | 		$lengths = [];
 | ||
| 
											12 years ago
										 | 		foreach ($table->columns as $column) {
 | ||
|  | 			if ($column->autoIncrement) {
 | ||
|  | 				continue;
 | ||
|  | 			}
 | ||
| 
											12 years ago
										 | 			if (!$column->allowNull && $column->defaultValue === null) {
 | ||
|  | 				$types['required'][] = $column->name;
 | ||
| 
											12 years ago
										 | 			}
 | ||
| 
											12 years ago
										 | 			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:
 | ||
|  | 					$types['safe'][] = $column->name;
 | ||
|  | 					break;
 | ||
|  | 				default: // strings
 | ||
|  | 					if ($column->size > 0) {
 | ||
|  | 						$lengths[$column->size][] = $column->name;
 | ||
|  | 					} else {
 | ||
|  | 						$types['string'][] = $column->name;
 | ||
|  | 					}
 | ||
| 
											12 years ago
										 | 			}
 | ||
|  | 		}
 | ||
| 
											12 years ago
										 | 
 | ||
| 
											12 years ago
										 | 		$rules = [];
 | ||
| 
											12 years ago
										 | 		foreach ($types as $type => $columns) {
 | ||
| 
											12 years ago
										 | 			$rules[] = "[['" . implode("', '", $columns) . "'], '$type']";
 | ||
| 
											12 years ago
										 | 		}
 | ||
| 
											12 years ago
										 | 		foreach ($lengths as $length => $columns) {
 | ||
| 
											12 years ago
										 | 			$rules[] = "[['" . implode("', '", $columns) . "'], 'string', 'max' => $length]";
 | ||
| 
											12 years ago
										 | 		}
 | ||
|  | 
 | ||
|  | 		return $rules;
 | ||
|  | 	}
 | ||
|  | 
 | ||
| 
											12 years ago
										 | 	/**
 | ||
|  | 	 * @return array the generated relation declarations
 | ||
|  | 	 */
 | ||
| 
											12 years ago
										 | 	protected function generateRelations()
 | ||
|  | 	{
 | ||
| 
											12 years ago
										 | 		if (!$this->generateRelations) {
 | ||
| 
											12 years ago
										 | 			return [];
 | ||
| 
											12 years ago
										 | 		}
 | ||
|  | 
 | ||
| 
											12 years ago
										 | 		$db = $this->getDbConnection();
 | ||
|  | 
 | ||
| 
											12 years ago
										 | 		if (($pos = strpos($this->tableName, '.')) !== false) {
 | ||
|  | 			$schemaName = substr($this->tableName, 0, $pos);
 | ||
| 
											12 years ago
										 | 		} else {
 | ||
|  | 			$schemaName = '';
 | ||
| 
											12 years ago
										 | 		}
 | ||
|  | 
 | ||
| 
											12 years ago
										 | 		$relations = [];
 | ||
| 
											12 years ago
										 | 		foreach ($db->getSchema()->getTableSchemas($schemaName) as $table) {
 | ||
| 
											12 years ago
										 | 			$tableName = $table->name;
 | ||
| 
											12 years ago
										 | 			$className = $this->generateClassName($tableName);
 | ||
|  | 			foreach ($table->foreignKeys as $refs) {
 | ||
|  | 				$refTable = $refs[0];
 | ||
|  | 				unset($refs[0]);
 | ||
|  | 				$fks = array_keys($refs);
 | ||
|  | 				$refClassName = $this->generateClassName($refTable);
 | ||
|  | 
 | ||
|  | 				// Add relation for this table
 | ||
|  | 				$link = $this->generateRelationLink(array_flip($refs));
 | ||
|  | 				$relationName = $this->generateRelationName($relations, $className, $table, $fks[0], false);
 | ||
| 
											12 years ago
										 | 				$relations[$className][$relationName] = [
 | ||
| 
											12 years ago
										 | 					"return \$this->hasOne($refClassName::className(), $link);",
 | ||
|  | 					$refClassName,
 | ||
| 
											12 years ago
										 | 					false,
 | ||
| 
											12 years ago
										 | 				];
 | ||
| 
											12 years ago
										 | 
 | ||
|  | 				// Add relation for the referenced table
 | ||
|  | 				$hasMany = false;
 | ||
|  | 				foreach ($fks as $key) {
 | ||
|  | 					if (!in_array($key, $table->primaryKey, true)) {
 | ||
|  | 						$hasMany = true;
 | ||
|  | 						break;
 | ||
| 
											12 years ago
										 | 					}
 | ||
|  | 				}
 | ||
| 
											12 years ago
										 | 				$link = $this->generateRelationLink($refs);
 | ||
|  | 				$relationName = $this->generateRelationName($relations, $refClassName, $refTable, $className, $hasMany);
 | ||
| 
											12 years ago
										 | 				$relations[$refClassName][$relationName] = [
 | ||
| 
											12 years ago
										 | 					"return \$this->" . ($hasMany ? 'hasMany' : 'hasOne') . "($className::className(), $link);",
 | ||
| 
											12 years ago
										 | 					$className,
 | ||
| 
											12 years ago
										 | 					$hasMany,
 | ||
| 
											12 years ago
										 | 				];
 | ||
| 
											12 years ago
										 | 			}
 | ||
|  | 
 | ||
|  | 			if (($fks = $this->checkPivotTable($table)) === false) {
 | ||
|  | 				continue;
 | ||
| 
											12 years ago
										 | 			}
 | ||
| 
											12 years ago
										 | 			$table0 = $fks[$table->primaryKey[0]][0];
 | ||
|  | 			$table1 = $fks[$table->primaryKey[1]][0];
 | ||
|  | 			$className0 = $this->generateClassName($table0);
 | ||
|  | 			$className1 = $this->generateClassName($table1);
 | ||
|  | 
 | ||
| 
											12 years ago
										 | 			$link = $this->generateRelationLink([$fks[$table->primaryKey[1]][1] => $table->primaryKey[1]]);
 | ||
|  | 			$viaLink = $this->generateRelationLink([$table->primaryKey[0] => $fks[$table->primaryKey[0]][1]]);
 | ||
| 
											12 years ago
										 | 			$relationName = $this->generateRelationName($relations, $className0, $db->getTableSchema($table0), $table->primaryKey[1], true);
 | ||
| 
											12 years ago
										 | 			$relations[$className0][$relationName] = [
 | ||
| 
											12 years ago
										 | 				"return \$this->hasMany($className1::className(), $link)->viaTable('{$table->name}', $viaLink);",
 | ||
|  | 				$className0,
 | ||
| 
											12 years ago
										 | 				true,
 | ||
| 
											12 years ago
										 | 			];
 | ||
| 
											12 years ago
										 | 
 | ||
| 
											12 years ago
										 | 			$link = $this->generateRelationLink([$fks[$table->primaryKey[0]][1] => $table->primaryKey[0]]);
 | ||
|  | 			$viaLink = $this->generateRelationLink([$table->primaryKey[1] => $fks[$table->primaryKey[1]][1]]);
 | ||
| 
											12 years ago
										 | 			$relationName = $this->generateRelationName($relations, $className1, $db->getTableSchema($table1), $table->primaryKey[0], true);
 | ||
| 
											12 years ago
										 | 			$relations[$className1][$relationName] = [
 | ||
| 
											12 years ago
										 | 				"return \$this->hasMany($className0::className(), $link)->viaTable('{$table->name}', $viaLink);",
 | ||
|  | 				$className1,
 | ||
| 
											12 years ago
										 | 				true,
 | ||
| 
											12 years ago
										 | 			];
 | ||
| 
											12 years ago
										 | 		}
 | ||
|  | 		return $relations;
 | ||
|  | 	}
 | ||
|  | 
 | ||
| 
											12 years ago
										 | 	/**
 | ||
| 
											12 years ago
										 | 	 * Generates the link parameter to be used in generating the relation declaration.
 | ||
|  | 	 * @param array $refs reference constraint
 | ||
|  | 	 * @return string the generated link parameter.
 | ||
| 
											12 years ago
										 | 	 */
 | ||
| 
											12 years ago
										 | 	protected function generateRelationLink($refs)
 | ||
| 
											12 years ago
										 | 	{
 | ||
| 
											12 years ago
										 | 		$pairs = [];
 | ||
| 
											12 years ago
										 | 		foreach ($refs as $a => $b) {
 | ||
|  | 			$pairs[] = "'$a' => '$b'";
 | ||
|  | 		}
 | ||
| 
											12 years ago
										 | 		return '[' . implode(', ', $pairs) . ']';
 | ||
| 
											12 years ago
										 | 	}
 | ||
|  | 
 | ||
|  | 	/**
 | ||
| 
											12 years ago
										 | 	 * Checks if the given table is a pivot table.
 | ||
|  | 	 * For simplicity, this method only deals with the case where the pivot contains two PK columns,
 | ||
|  | 	 * each referencing a column in a different table.
 | ||
|  | 	 * @param \yii\db\TableSchema the table being checked
 | ||
|  | 	 * @return array|boolean the relevant foreign key constraint information if the table is a pivot table,
 | ||
|  | 	 * or false if the table is not a pivot table.
 | ||
| 
											12 years ago
										 | 	 */
 | ||
| 
											12 years ago
										 | 	protected function checkPivotTable($table)
 | ||
| 
											12 years ago
										 | 	{
 | ||
| 
											12 years ago
										 | 		$pk = $table->primaryKey;
 | ||
|  | 		if (count($pk) !== 2) {
 | ||
|  | 			return false;
 | ||
|  | 		}
 | ||
| 
											12 years ago
										 | 		$fks = [];
 | ||
| 
											12 years ago
										 | 		foreach ($table->foreignKeys as $refs) {
 | ||
|  | 			if (count($refs) === 2) {
 | ||
|  | 				if (isset($refs[$pk[0]])) {
 | ||
| 
											12 years ago
										 | 					$fks[$pk[0]] = [$refs[0], $refs[$pk[0]]];
 | ||
| 
											12 years ago
										 | 				} elseif (isset($refs[$pk[1]])) {
 | ||
| 
											12 years ago
										 | 					$fks[$pk[1]] = [$refs[0], $refs[$pk[1]]];
 | ||
| 
											12 years ago
										 | 				}
 | ||
|  | 			}
 | ||
|  | 		}
 | ||
|  | 		if (count($fks) === 2 && $fks[$pk[0]][0] !== $fks[$pk[1]][0]) {
 | ||
|  | 			return $fks;
 | ||
| 
											12 years ago
										 | 		} else {
 | ||
| 
											12 years ago
										 | 			return false;
 | ||
| 
											12 years ago
										 | 		}
 | ||
| 
											12 years ago
										 | 	}
 | ||
| 
											12 years ago
										 | 
 | ||
| 
											12 years ago
										 | 	/**
 | ||
|  | 	 * Generate a relation name for the specified table and a base name.
 | ||
|  | 	 * @param array $relations the relations being generated currently.
 | ||
|  | 	 * @param string $className the class name that will contain the relation declarations
 | ||
|  | 	 * @param \yii\db\TableSchema $table the table schema
 | ||
|  | 	 * @param string $key a base name that the relation name may be generated from
 | ||
|  | 	 * @param boolean $multiple whether this is a has-many relation
 | ||
|  | 	 * @return string the relation name
 | ||
|  | 	 */
 | ||
|  | 	protected function generateRelationName($relations, $className, $table, $key, $multiple)
 | ||
|  | 	{
 | ||
|  | 		if (strcasecmp(substr($key, -2), 'id') === 0 && strcasecmp($key, 'id')) {
 | ||
|  | 			$key = rtrim(substr($key, 0, -2), '_');
 | ||
| 
											12 years ago
										 | 		}
 | ||
| 
											12 years ago
										 | 		if ($multiple) {
 | ||
|  | 			$key = Inflector::pluralize($key);
 | ||
| 
											12 years ago
										 | 		}
 | ||
| 
											12 years ago
										 | 		$name = $rawName = Inflector::id2camel($key, '_');
 | ||
| 
											12 years ago
										 | 		$i = 0;
 | ||
| 
											12 years ago
										 | 		while (isset($table->columns[lcfirst($name)])) {
 | ||
| 
											12 years ago
										 | 			$name = $rawName . ($i++);
 | ||
|  | 		}
 | ||
| 
											12 years ago
										 | 		while (isset($relations[$className][lcfirst($name)])) {
 | ||
| 
											12 years ago
										 | 			$name = $rawName . ($i++);
 | ||
|  | 		}
 | ||
| 
											12 years ago
										 | 
 | ||
|  | 		return $name;
 | ||
|  | 	}
 | ||
|  | 
 | ||
| 
											12 years ago
										 | 	/**
 | ||
|  | 	 * Validates the [[db]] attribute.
 | ||
|  | 	 */
 | ||
| 
											12 years ago
										 | 	public function validateDb()
 | ||
| 
											12 years ago
										 | 	{
 | ||
| 
											12 years ago
										 | 		if (Yii::$app->hasComponent($this->db) === false) {
 | ||
|  | 			$this->addError('db', 'There is no application component named "db".');
 | ||
|  | 		} elseif (!Yii::$app->getComponent($this->db) instanceof Connection) {
 | ||
|  | 			$this->addError('db', 'The "db" application component must be a DB connection instance.');
 | ||
| 
											12 years ago
										 | 		}
 | ||
|  | 	}
 | ||
|  | 
 | ||
| 
											12 years ago
										 | 	/**
 | ||
|  | 	 * Validates the [[ns]] attribute.
 | ||
|  | 	 */
 | ||
| 
											12 years ago
										 | 	public function validateNamespace()
 | ||
|  | 	{
 | ||
| 
											12 years ago
										 | 		$this->ns = ltrim($this->ns, '\\');
 | ||
|  | 		$path = Yii::getAlias('@' . str_replace('\\', '/', $this->ns), false);
 | ||
| 
											12 years ago
										 | 		if ($path === false) {
 | ||
|  | 			$this->addError('ns', 'Namespace must be associated with an existing directory.');
 | ||
|  | 		}
 | ||
| 
											12 years ago
										 | 	}
 | ||
|  | 
 | ||
| 
											12 years ago
										 | 	/**
 | ||
|  | 	 * Validates the [[modelClass]] attribute.
 | ||
|  | 	 */
 | ||
| 
											12 years ago
										 | 	public function validateModelClass()
 | ||
|  | 	{
 | ||
|  | 		if ($this->isReservedKeyword($this->modelClass)) {
 | ||
| 
											12 years ago
										 | 			$this->addError('modelClass', 'Class name cannot be a reserved PHP keyword.');
 | ||
| 
											12 years ago
										 | 		}
 | ||
| 
											12 years ago
										 | 		if (substr($this->tableName, -1) !== '*' && $this->modelClass == '') {
 | ||
|  | 			$this->addError('modelClass', 'Model Class cannot be blank if table name does not end with asterisk.');
 | ||
| 
											12 years ago
										 | 		}
 | ||
|  | 	}
 | ||
|  | 
 | ||
| 
											12 years ago
										 | 	/**
 | ||
|  | 	 * Validates the [[tableName]] attribute.
 | ||
|  | 	 */
 | ||
| 
											12 years ago
										 | 	public function validateTableName()
 | ||
|  | 	{
 | ||
| 
											12 years ago
										 | 		if (($pos = strpos($this->tableName, '*')) !== false && substr($this->tableName, -1) !== '*') {
 | ||
|  | 			$this->addError('tableName', 'Asterisk is only allowed as the last character.');
 | ||
| 
											12 years ago
										 | 			return;
 | ||
|  | 		}
 | ||
| 
											12 years ago
										 | 		$tables = $this->getTableNames();
 | ||
|  | 		if (empty($tables)) {
 | ||
| 
											12 years ago
										 | 			$this->addError('tableName', "Table '{$this->tableName}' does not exist.");
 | ||
| 
											12 years ago
										 | 		} else {
 | ||
|  | 			foreach ($tables as $table) {
 | ||
|  | 				$class = $this->generateClassName($table);
 | ||
|  | 				if ($this->isReservedKeyword($class)) {
 | ||
| 
											12 years ago
										 | 					$this->addError('tableName', "Table '$table' will generate a class which is a reserved PHP keyword.");
 | ||
| 
											12 years ago
										 | 					break;
 | ||
|  | 				}
 | ||
|  | 			}
 | ||
|  | 		}
 | ||
|  | 	}
 | ||
| 
											12 years ago
										 | 
 | ||
| 
											12 years ago
										 | 	private $_tableNames;
 | ||
|  | 	private $_classNames;
 | ||
|  | 
 | ||
| 
											12 years ago
										 | 	/**
 | ||
|  | 	 * @return array the table names that match the pattern specified by [[tableName]].
 | ||
|  | 	 */
 | ||
| 
											12 years ago
										 | 	protected function getTableNames()
 | ||
|  | 	{
 | ||
| 
											12 years ago
										 | 		if ($this->_tableNames !== null) {
 | ||
|  | 			return $this->_tableNames;
 | ||
|  | 		}
 | ||
| 
											12 years ago
										 | 		$db = $this->getDbConnection();
 | ||
| 
											12 years ago
										 | 		if ($db === null) {
 | ||
| 
											12 years ago
										 | 			return [];
 | ||
| 
											12 years ago
										 | 		}
 | ||
| 
											12 years ago
										 | 		$tableNames = [];
 | ||
| 
											12 years ago
										 | 		if (strpos($this->tableName, '*') !== false) {
 | ||
| 
											12 years ago
										 | 			if (($pos = strrpos($this->tableName, '.')) !== false) {
 | ||
|  | 				$schema = substr($this->tableName, 0, $pos);
 | ||
| 
											12 years ago
										 | 				$pattern = '/^' . str_replace('*', '\w+', substr($this->tableName, $pos + 1)) . '$/';
 | ||
| 
											12 years ago
										 | 			} else {
 | ||
|  | 				$schema = '';
 | ||
| 
											12 years ago
										 | 				$pattern = '/^' . str_replace('*', '\w+', $this->tableName) . '$/';
 | ||
| 
											12 years ago
										 | 			}
 | ||
|  | 
 | ||
| 
											12 years ago
										 | 			foreach ($db->schema->getTableNames($schema) as $table) {
 | ||
|  | 				if (preg_match($pattern, $table)) {
 | ||
|  | 					$tableNames[] = $schema === '' ? $table : ($schema . '.' . $table);
 | ||
| 
											12 years ago
										 | 				}
 | ||
|  | 			}
 | ||
| 
											12 years ago
										 | 		} elseif (($table = $db->getTableSchema($this->tableName, true)) !== null) {
 | ||
|  | 			$tableNames[] = $this->tableName;
 | ||
| 
											12 years ago
										 | 			$this->_classNames[$this->tableName] = $this->modelClass;
 | ||
|  | 		}
 | ||
|  | 		return $this->_tableNames = $tableNames;
 | ||
|  | 	}
 | ||
|  | 
 | ||
| 
											12 years ago
										 | 	/**
 | ||
|  | 	 * Generates a class name from the specified table name.
 | ||
|  | 	 * @param string $tableName the table name (which may contain schema prefix)
 | ||
|  | 	 * @return string the generated class name
 | ||
|  | 	 */
 | ||
| 
											12 years ago
										 | 	protected function generateClassName($tableName)
 | ||
|  | 	{
 | ||
|  | 		if (isset($this->_classNames[$tableName])) {
 | ||
|  | 			return $this->_classNames[$tableName];
 | ||
|  | 		}
 | ||
|  | 
 | ||
|  | 		if (($pos = strrpos($tableName, '.')) !== false) {
 | ||
|  | 			$tableName = substr($tableName, $pos + 1);
 | ||
|  | 		}
 | ||
|  | 
 | ||
|  | 		$db = $this->getDbConnection();
 | ||
| 
											12 years ago
										 | 		$patterns = [];
 | ||
| 
											12 years ago
										 | 		if (strpos($this->tableName, '*') !== false) {
 | ||
|  | 			$pattern = $this->tableName;
 | ||
|  | 			if (($pos = strrpos($pattern, '.')) !== false) {
 | ||
|  | 				$pattern = substr($pattern, $pos + 1);
 | ||
|  | 			}
 | ||
|  | 			$patterns[] = '/^' . str_replace('*', '(\w+)', $pattern) . '$/';
 | ||
|  | 		}
 | ||
|  | 		if (!empty($db->tablePrefix)) {
 | ||
| 
											12 years ago
										 | 			$patterns[] = "/^{$db->tablePrefix}(.*?)$/";
 | ||
|  | 			$patterns[] = "/^(.*?){$db->tablePrefix}$/";
 | ||
| 
											12 years ago
										 | 		} else {
 | ||
|  | 			$patterns[] = "/^tbl_(.*?)$/";
 | ||
|  | 		}
 | ||
|  | 
 | ||
|  | 		$className = $tableName;
 | ||
|  | 		foreach ($patterns as $pattern) {
 | ||
|  | 			if (preg_match($pattern, $tableName, $matches)) {
 | ||
|  | 				$className = $matches[1];
 | ||
| 
											12 years ago
										 | 				break;
 | ||
| 
											12 years ago
										 | 			}
 | ||
| 
											12 years ago
										 | 		}
 | ||
| 
											12 years ago
										 | 		return $this->_classNames[$tableName] = Inflector::id2camel($className, '_');
 | ||
| 
											12 years ago
										 | 	}
 | ||
| 
											12 years ago
										 | 
 | ||
|  | 	/**
 | ||
|  | 	 * @return Connection the DB connection as specified by [[db]].
 | ||
|  | 	 */
 | ||
|  | 	protected function getDbConnection()
 | ||
|  | 	{
 | ||
|  | 		return Yii::$app->{$this->db};
 | ||
|  | 	}
 | ||
| 
											12 years ago
										 | }
 |