Qiang Xue
12 years ago
8 changed files with 0 additions and 1092 deletions
@ -1,601 +0,0 @@
|
||||
<?php |
||||
/** |
||||
* ActiveFinder class file. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright © 2008-2012 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\db\ar; |
||||
|
||||
use yii\base\Object; |
||||
use yii\base\VectorIterator; |
||||
use yii\db\dao\Query; |
||||
use yii\db\Exception; |
||||
|
||||
/** |
||||
* ActiveFinder.php is ... |
||||
* |
||||
* @property integer $count |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @since 2.0 |
||||
*/ |
||||
class ActiveFinder extends \yii\base\Object |
||||
{ |
||||
/** |
||||
* @var \yii\db\dao\Connection |
||||
*/ |
||||
public $connection; |
||||
|
||||
public function __construct($connection, $config = array()) |
||||
{ |
||||
$this->connection = $connection; |
||||
parent::__construct($config); |
||||
} |
||||
|
||||
/** |
||||
* @param ActiveQuery $query |
||||
*/ |
||||
public function find($query, $returnScalar = false) |
||||
{ |
||||
if (!empty($query->with)) { |
||||
return $this->findWithRelations($query, $returnScalar); |
||||
} |
||||
|
||||
if ($query->sql !== null) { |
||||
$sql = $query->sql; |
||||
} else { |
||||
$modelClass = $query->modelClass; |
||||
$tableName = $modelClass::tableName(); |
||||
if ($query->from === null) { |
||||
if ($query->tableAlias !== null) { |
||||
$tableName .= ' ' . $query->tableAlias; |
||||
} |
||||
$query->from = array($tableName); |
||||
} |
||||
$this->applyScopes($query); |
||||
$sql = $this->connection->getQueryBuilder()->build($query); |
||||
|
||||
if ($query->tableAlias !== null) { |
||||
$alias = $this->connection->quoteTableName($query->tableAlias) . '.'; |
||||
} else { |
||||
$alias = $this->connection->quoteTableName($tableName) . '.'; |
||||
} |
||||
$tokens = array( |
||||
'@.' => $alias, |
||||
$this->connection->quoteTableName('@', true) . '.' => $alias, |
||||
); |
||||
$sql = strtr($sql, $tokens); |
||||
} |
||||
$command = $this->connection->createCommand($sql, $query->params); |
||||
|
||||
if ($returnScalar) { |
||||
return $command->queryScalar(); |
||||
} else { |
||||
$rows = $command->queryAll(); |
||||
return $this->createRecords($query, $rows); |
||||
} |
||||
} |
||||
|
||||
private $_joinCount; |
||||
private $_tableAliases; |
||||
private $_hasMany; |
||||
|
||||
/** |
||||
* @param ActiveQuery $query |
||||
* @return array |
||||
*/ |
||||
protected function findWithRelations($query, $returnScalar = false) |
||||
{ |
||||
$this->_joinCount = 0; |
||||
$this->_tableAliases = array(); |
||||
$this->_hasMany = false; |
||||
$joinTree = new JoinElement($this->_joinCount++, $query, null, null); |
||||
|
||||
if ($query->sql !== null) { |
||||
$command = $this->connection->createCommand($query->sql, $query->params); |
||||
if ($returnScalar) { |
||||
return $command->queryScalar(); |
||||
} |
||||
$rows = $command->queryAll(); |
||||
$records = $this->createRecords($query, $rows); |
||||
$modelClass = $query->modelClass; |
||||
$table = $modelClass::getMetaData()->table; |
||||
foreach ($records as $record) { |
||||
$pk = array(); |
||||
foreach ($table->primaryKey as $name) { |
||||
$pk[] = $record[$name]; |
||||
} |
||||
$pk = count($pk) === 1 ? $pk[0] : serialize($pk); |
||||
$joinTree->records[$pk] = $record; |
||||
} |
||||
|
||||
$q = new ActiveQuery($modelClass); |
||||
$q->with = $query->with; |
||||
$q->tableAlias = 't'; |
||||
$q->asArray = $query->asArray; |
||||
$q->index = $query->index; |
||||
$q->select = $table->primaryKey; |
||||
$this->addPkCondition($q, $table, $rows, 't.'); |
||||
$joinTree->query = $query = $q; |
||||
} |
||||
|
||||
$this->buildJoinTree($joinTree, $query->with); |
||||
$this->initJoinTree($joinTree); |
||||
|
||||
$q = new Query; |
||||
$this->buildJoinQuery($joinTree, $q, $returnScalar); |
||||
|
||||
if ($returnScalar) { |
||||
return $q->createCommand($this->connection)->queryScalar(); |
||||
} else { |
||||
if ($this->_hasMany && ($query->limit > 0 || $query->offset > 0)) { |
||||
$this->limitQuery($query, $q); |
||||
} |
||||
$command = $q->createCommand($this->connection); |
||||
$rows = $command->queryAll(); |
||||
$joinTree->populateData($rows); |
||||
return $query->index === null ? array_values($joinTree->records) : $joinTree->records; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @param ActiveRecord $record |
||||
* @param ActiveRelation $relation |
||||
* @return array |
||||
*/ |
||||
public function findWithRecord($record, $relation) |
||||
{ |
||||
$this->_joinCount = 0; |
||||
$this->_tableAliases = array(); |
||||
$this->_hasMany = false; |
||||
$query = new ActiveQuery(get_class($record)); |
||||
$modelClass = $query->modelClass; |
||||
$table = $modelClass::getMetaData()->table; |
||||
$query->select = $table->primaryKey; |
||||
$query->limit = $relation->limit; |
||||
$query->offset = $relation->offset; |
||||
$joinTree = new JoinElement($this->_joinCount++, $query, null, null); |
||||
$child = $this->buildJoinTree($joinTree, $relation->name); |
||||
$child->query = $relation; |
||||
$child->container = null; |
||||
$this->buildJoinTree($child, $relation->with); |
||||
$this->initJoinTree($joinTree); |
||||
|
||||
$pk = $record->getPrimaryKey(true); |
||||
$this->addPkCondition($query, $table, array($pk), $query->tableAlias . '.'); |
||||
|
||||
$q = new Query; |
||||
$this->buildJoinQuery($joinTree, $q); |
||||
|
||||
if ($this->_hasMany && ($query->limit > 0 || $query->offset > 0)) { |
||||
$this->limitQuery($query, $q); |
||||
} |
||||
|
||||
$rows = $q->createCommand($this->connection)->queryAll(); |
||||
$child->populateData($rows); |
||||
|
||||
$records = $relation->index === null ? array_values($child->records) : $child->records; |
||||
if ($relation->hasMany) { |
||||
return $records; |
||||
} else { |
||||
return $records === array() ? null : reset($records); |
||||
} |
||||
} |
||||
|
||||
protected function createRecords($query, $rows) |
||||
{ |
||||
$records = array(); |
||||
if ($query->asArray) { |
||||
if ($query->index === null) { |
||||
return $rows; |
||||
} |
||||
foreach ($rows as $row) { |
||||
$records[$row[$query->index]] = $row; |
||||
} |
||||
} else { |
||||
/** @var $class ActiveRecord */ |
||||
$class = $query->modelClass; |
||||
if ($query->index === null) { |
||||
foreach ($rows as $row) { |
||||
$records[] = $class::create($row); |
||||
} |
||||
} else { |
||||
foreach ($rows as $row) { |
||||
$records[$row[$query->index]] = $class::create($row); |
||||
} |
||||
} |
||||
} |
||||
return $records; |
||||
} |
||||
|
||||
protected function applyScopes($query) |
||||
{ |
||||
/** @var $class ActiveRecord */ |
||||
$class = $query->modelClass; |
||||
$class::defaultScope($query); |
||||
if (is_array($query->scopes)) { |
||||
$model = new $class; |
||||
foreach ($query->scopes as $name => $params) { |
||||
if (is_string($params)) { |
||||
// scope name only without parameters |
||||
$name = $params; |
||||
$params = array(); |
||||
} |
||||
if (method_exists($class, $name)) { |
||||
array_unshift($params, $query); |
||||
call_user_func_array(array($model, $name), $params); |
||||
} else { |
||||
throw new Exception("$class has no scope named '$name'."); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @param JoinElement $parent |
||||
* @param array|string $with |
||||
* @param array|\Closure $config |
||||
* @return null|JoinElement |
||||
* @throws \yii\db\Exception |
||||
*/ |
||||
protected function buildJoinTree($parent, $with, $config = array()) |
||||
{ |
||||
if (empty($with)) { |
||||
return null; |
||||
} |
||||
if (is_array($with)) { |
||||
foreach ($with as $name => $value) { |
||||
if (is_array($value) || $value instanceof \Closure) { |
||||
$this->buildJoinTree($parent, $name, $value); |
||||
} else { |
||||
$this->buildJoinTree($parent, $value); |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
if (($pos = strrpos($with, '.')) !== false) { |
||||
$parent = $this->buildJoinTree($parent, substr($with, 0, $pos)); |
||||
$with = substr($with, $pos + 1); |
||||
} |
||||
|
||||
if (isset($parent->children[$with])) { |
||||
$child = $parent->children[$with]; |
||||
} else { |
||||
$modelClass = $parent->query->modelClass; |
||||
$relations = $modelClass::getMetaData()->relations; |
||||
if (!isset($relations[$with])) { |
||||
throw new Exception("$modelClass has no relation named '$with'."); |
||||
} |
||||
$relation = clone $relations[$with]; |
||||
if (is_string($relation->via)) { |
||||
// join via an existing relation |
||||
$parent2 = $this->buildJoinTree($parent, $relation->via); |
||||
if ($parent2->query->select === null) { |
||||
$parent2->query->select = false; |
||||
unset($parent2->container->relations[$parent2->query->name]); |
||||
} |
||||
$child = new JoinElement($this->_joinCount++, $relation, $parent2, $parent); |
||||
} elseif (is_array($relation->via)) { |
||||
// join via a pivoting table |
||||
$r = new ActiveRelation; |
||||
$r->name = 'vt' . $this->_joinCount; |
||||
$r->hasMany = $relation->hasMany; |
||||
|
||||
foreach ($relation->via as $name => $value) { |
||||
$r->$name = $value; |
||||
} |
||||
|
||||
$r->select = false; |
||||
if ($r->joinType === null) { |
||||
$r->joinType = $relation->joinType; |
||||
} |
||||
|
||||
$parent2 = new JoinElement($this->_joinCount++, $r, $parent, $parent); |
||||
$child = new JoinElement($this->_joinCount++, $relation, $parent2, $parent); |
||||
|
||||
} else { |
||||
$child = new JoinElement($this->_joinCount++, $relation, $parent, $parent); |
||||
} |
||||
} |
||||
|
||||
if ($config instanceof \Closure) { |
||||
$config($child->query); |
||||
} else { |
||||
foreach ($config as $name => $value) { |
||||
$child->query->$name = $value; |
||||
} |
||||
} |
||||
|
||||
return $child; |
||||
} |
||||
|
||||
/** |
||||
* @param JoinElement $element |
||||
*/ |
||||
protected function initJoinTree($element) |
||||
{ |
||||
if ($element->query->tableAlias !== null) { |
||||
$alias = $element->query->tableAlias; |
||||
} elseif ($element->query instanceof ActiveRelation) { |
||||
$alias = $element->query->name; |
||||
} else { |
||||
$alias = 't'; |
||||
} |
||||
if ($element->query instanceof ActiveRelation) { |
||||
if ($element->query->hasMany) { |
||||
$this->_hasMany = true; |
||||
} |
||||
if ($element->parent->query->asArray !== null && $element->query->asArray === null) { |
||||
$element->query->asArray = $element->parent->query->asArray; |
||||
} |
||||
} |
||||
$count = 0; |
||||
while (isset($this->_tableAliases[$alias])) { |
||||
$alias = 't' . $count++; |
||||
} |
||||
$this->_tableAliases[$alias] = true; |
||||
$element->query->tableAlias = $alias; |
||||
|
||||
if ($element->records !== array()) { |
||||
$this->applyScopes($element->query); |
||||
} |
||||
|
||||
if ($element->container !== null && $element->query->asArray === null) { |
||||
$element->query->asArray = $element->container->query->asArray; |
||||
} |
||||
|
||||
foreach ($element->children as $child) { |
||||
$this->initJoinTree($child); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @param JoinElement $element |
||||
* @param \yii\db\dao\Query $query |
||||
*/ |
||||
protected function buildJoinQuery($element, $query, $keepSelect = false) |
||||
{ |
||||
if ($element->parent) { |
||||
$prefixes = array( |
||||
'@.' => $element->query->tableAlias . '.', |
||||
'?.' => $element->parent->query->tableAlias . '.', |
||||
); |
||||
$quotedPrefixes = array( |
||||
'@.' => $this->connection->quoteTableName($element->query->tableAlias, true) . '.', |
||||
'?.' => $this->connection->quoteTableName($element->parent->query->tableAlias, true) . '.', |
||||
); |
||||
} else { |
||||
$prefixes = array( |
||||
'@.' => $element->query->tableAlias . '.', |
||||
); |
||||
$quotedPrefixes = array( |
||||
'@.' => $this->connection->quoteTableName($element->query->tableAlias, true) . '.', |
||||
); |
||||
$query->limit = $element->query->limit; |
||||
$query->offset = $element->query->offset; |
||||
} |
||||
|
||||
$qb = $this->connection->getQueryBuilder(); |
||||
|
||||
if ($keepSelect) { |
||||
if (!empty($element->query->select)) { |
||||
$select = $element->query->select; |
||||
if (is_string($select)) { |
||||
$select = explode(',', $select); |
||||
} |
||||
foreach ($select as $column) { |
||||
$query->select[] = strtr(trim($column), $prefixes); |
||||
} |
||||
} |
||||
} else { |
||||
foreach ($this->buildSelect($element, $element->query->select) as $column) { |
||||
$query->select[] = strtr($column, $prefixes); |
||||
} |
||||
} |
||||
|
||||
if ($element->query instanceof ActiveQuery) { |
||||
if ($element->query->from === null) { |
||||
$modelClass = $element->query->modelClass; |
||||
$tableName = $modelClass::tableName(); |
||||
if ($element->query->tableAlias !== null) { |
||||
$tableName .= ' ' . $element->query->tableAlias; |
||||
} |
||||
$query->from = array($tableName); |
||||
} else { |
||||
$query->from = $element->query->from; |
||||
} |
||||
} |
||||
|
||||
if (($where = $qb->buildCondition($element->query->where)) !== '') { |
||||
$query->andWhere(strtr($where, $quotedPrefixes)); |
||||
} |
||||
|
||||
if (($having = $qb->buildCondition($element->query->having)) !== '') { |
||||
$query->andHaving(strtr($having, $quotedPrefixes)); |
||||
} |
||||
|
||||
if ($element->query instanceof ActiveRelation) { |
||||
$joinType = $element->query->joinType ?: 'LEFT JOIN'; |
||||
if ($element->query->modelClass !== null) { |
||||
$modelClass = $element->query->modelClass; |
||||
$tableName = $this->connection->quoteTableName($modelClass::tableName()); |
||||
} else { |
||||
$tableName = $this->connection->quoteTableName($element->query->table); |
||||
} |
||||
$tableAlias = $this->connection->quoteTableName($element->query->tableAlias); |
||||
$join = "$joinType $tableName $tableAlias"; |
||||
$on = ''; |
||||
if (is_array($element->query->link)) { |
||||
foreach ($element->query->link as $pk => $fk) { |
||||
$pk = $quotedPrefixes['@.'] . $this->connection->quoteColumnName($pk, true); |
||||
$fk = $quotedPrefixes['?.'] . $this->connection->quoteColumnName($fk, true); |
||||
if ($on !== '') { |
||||
$on .= ' AND '; |
||||
} |
||||
$on .= "$pk = $fk"; |
||||
} |
||||
} |
||||
if ($element->query->on !== null) { |
||||
$condition = strtr($qb->buildCondition($element->query->on), $quotedPrefixes); |
||||
if ($on !== '') { |
||||
$on .= " AND ($condition)"; |
||||
} else { |
||||
$on = $condition; |
||||
} |
||||
} |
||||
if ($on !== '') { |
||||
$join .= ' ON ' . $on; |
||||
} |
||||
$query->join[] = $join; |
||||
} |
||||
|
||||
if ($element->query->join !== null) { |
||||
if (is_array($element->query->join)) { |
||||
foreach ($element->query->join as $join) { |
||||
if (is_array($join) && isset($join[2])) { |
||||
$join[2] = strtr($join[2], $quotedPrefixes); |
||||
} |
||||
$query->join[] = $join; |
||||
} |
||||
} else { |
||||
$query->join[] = strtr($element->query->join, $quotedPrefixes); |
||||
} |
||||
} |
||||
|
||||
if ($element->query->orderBy !== null) { |
||||
if (!is_array($element->query->orderBy)) { |
||||
$element->query->orderBy = preg_split('/\s*,\s*/', trim($element->query->orderBy), -1, PREG_SPLIT_NO_EMPTY); |
||||
} |
||||
foreach ($element->query->orderBy as $order) { |
||||
$query->orderBy[] = strtr($order, $prefixes); |
||||
} |
||||
} |
||||
|
||||
if ($element->query->groupBy !== null) { |
||||
if (!is_array($element->query->groupBy)) { |
||||
$element->query->groupBy = preg_split('/\s*,\s*/', trim($element->query->groupBy), -1, PREG_SPLIT_NO_EMPTY); |
||||
} |
||||
foreach ($element->query->groupBy as $group) { |
||||
$query->groupBy[] = strtr($group, $prefixes); |
||||
} |
||||
} |
||||
|
||||
if ($element->query->params !== null) { |
||||
$query->addParams($element->query->params); |
||||
} |
||||
|
||||
foreach ($element->children as $child) { |
||||
$this->buildJoinQuery($child, $query, $keepSelect); |
||||
} |
||||
} |
||||
|
||||
protected function buildSelect($element, $select) |
||||
{ |
||||
if ($select === false) { |
||||
return array(); |
||||
} |
||||
$modelClass = $element->query->modelClass; |
||||
$table = $modelClass::getMetaData()->table; |
||||
$columns = array(); |
||||
$columnCount = 0; |
||||
$prefix = $element->query->tableAlias; |
||||
|
||||
foreach ($table->primaryKey as $column) { |
||||
$alias = "c{$element->id}_" . ($columnCount++); |
||||
$columns[] = "$prefix.$column AS $alias"; |
||||
$element->pkAlias[$column] = $alias; |
||||
$element->columnAliases[$alias] = $column; |
||||
} |
||||
|
||||
if (empty($select) || $select === '*') { |
||||
foreach ($table->columns as $column) { |
||||
if (!isset($element->pkAlias[$column->name])) { |
||||
$alias = "c{$element->id}_" . ($columnCount++); |
||||
$columns[] = "$prefix.{$column->name} AS $alias"; |
||||
$element->columnAliases[$alias] = $column->name; |
||||
} |
||||
} |
||||
} else { |
||||
if (is_string($select)) { |
||||
$select = explode(',', $select); |
||||
} |
||||
foreach ($select as $column) { |
||||
$column = trim($column); |
||||
if (preg_match('/^(.*?)\s+AS\s+(\w+)$/im', $column, $matches)) { |
||||
// if the column is already aliased |
||||
$element->columnAliases[$matches[2]] = $matches[2]; |
||||
$columns[] = $column; |
||||
} elseif (!isset($element->pkAlias[$column])) { |
||||
$alias = "c{$element->id}_" . ($columnCount++); |
||||
if (strpos($column, '(') !== false) { |
||||
$columns[] = "$column AS $alias"; |
||||
} else { |
||||
$columns[] = "$prefix.$column AS $alias"; |
||||
} |
||||
$element->columnAliases[$alias] = $column; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// determine the actual index column(s) |
||||
if ($element->query->index !== null) { |
||||
$index = array_search($element->query->index, $element->columnAliases); |
||||
} |
||||
if (empty($index)) { |
||||
$index = $element->pkAlias; |
||||
if (count($index) === 1) { |
||||
$index = reset($element->pkAlias); |
||||
} |
||||
} |
||||
$element->key = $index; |
||||
|
||||
return $columns; |
||||
} |
||||
|
||||
protected function limitQuery($activeQuery, $query) |
||||
{ |
||||
$q = clone $query; |
||||
$modelClass = $activeQuery->modelClass; |
||||
$table = $modelClass::getMetaData()->table; |
||||
$q->select = array(); |
||||
foreach ($table->primaryKey as $name) { |
||||
$q->select[] = $alias = $activeQuery->tableAlias . '.' . $name; |
||||
} |
||||
$q->distinct = true; |
||||
$rows = $q->createCommand($this->connection)->queryAll(); |
||||
$prefix = $activeQuery->tableAlias . '.'; |
||||
$this->addPkCondition($query, $table, $rows, $prefix); |
||||
$query->limit = $query->offset = null; |
||||
} |
||||
|
||||
protected function addPkCondition($query, $table, $rows, $prefix) |
||||
{ |
||||
if (count($table->primaryKey) === 1 && count($rows) > 1) { |
||||
$name = $table->primaryKey[0]; |
||||
$values = array(); |
||||
foreach ($rows as $row) { |
||||
$values[] = $table->columns[$name]->typecast($row[$name]); |
||||
} |
||||
$query->andWhere(array('in', $prefix . $name, $values)); |
||||
} else { |
||||
$ors = array('or'); |
||||
foreach ($rows as $row) { |
||||
$hash = array(); |
||||
foreach ($table->primaryKey as $name) { |
||||
$value = $table->columns[$name]->typecast($row[$name]); |
||||
if (is_string($value)) { |
||||
$value = $this->connection->quoteValue($value); |
||||
} |
||||
$hash[$prefix . $name] = $value; |
||||
} |
||||
$ors[] = $hash; |
||||
} |
||||
$query->andWhere($ors); |
||||
} |
||||
} |
||||
} |
@ -1,103 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace yii\db\ar; |
||||
|
||||
use yii\db\Exception; |
||||
use yii\db\dao\TableSchema; |
||||
|
||||
/** |
||||
* ActiveMetaData represents the meta-data for an Active Record class. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @since 2.0 |
||||
*/ |
||||
class ActiveMetaData |
||||
{ |
||||
/** |
||||
* @var ActiveMetaData[] list of ActiveMetaData instances indexed by the model class names |
||||
*/ |
||||
public static $instances; |
||||
/** |
||||
* @var TableSchema the table schema information |
||||
*/ |
||||
public $table; |
||||
/** |
||||
* @var string the model class name |
||||
*/ |
||||
public $modelClass; |
||||
/** |
||||
* @var ActiveRecord the model instance that can be used to access non-static methods |
||||
*/ |
||||
public $model; |
||||
|
||||
/** |
||||
* Returns an instance of ActiveMetaData for the specified model class. |
||||
* Note that each model class only has a single ActiveMetaData instance. |
||||
* This method will only create the ActiveMetaData instance if it is not previously |
||||
* done so for the specified model class. |
||||
* @param string $modelClass the model class name. Make sure the class name do NOT have a leading backslash "\". |
||||
* @param boolean $refresh whether to recreate the ActiveMetaData instance. Defaults to false. |
||||
* @return ActiveMetaData the ActiveMetaData instance for the specified model class. |
||||
*/ |
||||
public static function getInstance($modelClass, $refresh = false) |
||||
{ |
||||
if (isset(self::$instances[$modelClass]) && !$refresh) { |
||||
return self::$instances[$modelClass]; |
||||
} else { |
||||
return self::$instances[$modelClass] = new self($modelClass); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Constructor. |
||||
* @param string $modelClass the model class name |
||||
*/ |
||||
public function __construct($modelClass) |
||||
{ |
||||
$this->modelClass = $modelClass; |
||||
$this->model = new $modelClass; |
||||
$tableName = $this->model->tableName(); |
||||
$this->table = $this->model->getDbConnection()->getDriver()->getTableSchema($tableName); |
||||
if ($this->table === null) { |
||||
throw new Exception("Unable to find table '$tableName' for ActiveRecord class '$modelClass'."); |
||||
} |
||||
$primaryKey = $this->model->primaryKey(); |
||||
if ($primaryKey !== $this->table->primaryKey) { |
||||
$this->table->fixPrimaryKey($primaryKey); |
||||
} elseif ($primaryKey === null) { |
||||
throw new Exception("The table '$tableName' for ActiveRecord class '$modelClass' does not have a primary key."); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Adds a relation. |
||||
* |
||||
* $config is an array with three elements: |
||||
* relation type, the related active record class and the foreign key. |
||||
* |
||||
* @throws Exception |
||||
* @param string $name $name Name of the relation. |
||||
* @param array $config $config Relation parameters. |
||||
* @return void |
||||
*/ |
||||
public function addRelation($name, $config) |
||||
{ |
||||
if (preg_match('/^(\w+)\s*:\s*\\\\?([\w\\\\]+)(\[\])?$/', $name, $matches)) { |
||||
if (is_string($config)) { |
||||
$config = array('on' => $config); |
||||
} |
||||
$relation = new ActiveRelation($config); |
||||
$relation->name = $matches[1]; |
||||
$modelClass = $matches[2]; |
||||
if (strpos($modelClass, '\\') !== false) { |
||||
$relation->modelClass = ltrim($modelClass, '\\'); |
||||
} else { |
||||
$relation->modelClass = dirname($this->modelClass) . '\\' . $modelClass; |
||||
} |
||||
$relation->hasMany = isset($matches[3]); |
||||
$this->relations[$relation->name] = $relation; |
||||
} else { |
||||
throw new Exception("{$this->modelClass} has an invalid relation: $name"); |
||||
} |
||||
} |
||||
} |
@ -1,107 +0,0 @@
|
||||
<?php |
||||
/** |
||||
* ActiveFinder class file. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright © 2008-2012 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\db\ar; |
||||
|
||||
use yii\db\dao\BaseQuery; |
||||
|
||||
/** |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @since 2.0 |
||||
*/ |
||||
class BaseActiveQuery extends BaseQuery |
||||
{ |
||||
/** |
||||
* @var string the name of the ActiveRecord class. |
||||
*/ |
||||
public $modelClass; |
||||
/** |
||||
* @var array list of relations that this query should be performed with |
||||
*/ |
||||
public $with; |
||||
/** |
||||
* @var string the table alias to be used for query |
||||
*/ |
||||
public $tableAlias; |
||||
/** |
||||
* @var string the name of the column that the result should be indexed by. |
||||
* This is only useful when the query result is returned as an array. |
||||
*/ |
||||
public $index; |
||||
/** |
||||
* @var boolean whether to return each record as an array. If false (default), an object |
||||
* of [[modelClass]] will be created to represent each record. |
||||
*/ |
||||
public $asArray; |
||||
/** |
||||
* @var array list of scopes that should be applied to this query |
||||
*/ |
||||
public $scopes; |
||||
|
||||
public function asArray($value = true) |
||||
{ |
||||
$this->asArray = $value; |
||||
return $this; |
||||
} |
||||
|
||||
public function with() |
||||
{ |
||||
$this->with = func_get_args(); |
||||
if (isset($this->with[0]) && is_array($this->with[0])) { |
||||
// the parameter is given as an array |
||||
$this->with = $this->with[0]; |
||||
} |
||||
return $this; |
||||
} |
||||
|
||||
public function index($column) |
||||
{ |
||||
$this->index = $column; |
||||
return $this; |
||||
} |
||||
|
||||
public function tableAlias($value) |
||||
{ |
||||
$this->tableAlias = $value; |
||||
return $this; |
||||
} |
||||
|
||||
public function scopes($names) |
||||
{ |
||||
$this->scopes = $names; |
||||
return $this; |
||||
} |
||||
|
||||
protected function createModels($rows) |
||||
{ |
||||
$models = array(); |
||||
if ($this->asArray) { |
||||
if ($this->index === null) { |
||||
return $rows; |
||||
} |
||||
foreach ($rows as $row) { |
||||
$models[$row[$this->index]] = $row; |
||||
} |
||||
} else { |
||||
/** @var $class ActiveRecord */ |
||||
$class = $this->modelClass; |
||||
if ($this->index === null) { |
||||
foreach ($rows as $row) { |
||||
$models[] = $class::create($row); |
||||
} |
||||
} else { |
||||
foreach ($rows as $row) { |
||||
$models[$row[$this->index]] = $class::create($row); |
||||
} |
||||
} |
||||
} |
||||
return $models; |
||||
} |
||||
} |
@ -1,8 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace yii\db\ar; |
||||
|
||||
class HasManyRelation extends Relation |
||||
{ |
||||
public $link; |
||||
} |
@ -1,8 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace yii\db\ar; |
||||
|
||||
class HasOneRelation extends Relation |
||||
{ |
||||
public $link; |
||||
} |
@ -1,173 +0,0 @@
|
||||
<?php |
||||
/** |
||||
* JoinElement class file. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright © 2008-2012 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\db\ar; |
||||
|
||||
use yii\base\VectorIterator; |
||||
use yii\db\dao\Query; |
||||
use yii\db\Exception; |
||||
|
||||
|
||||
class JoinElement |
||||
{ |
||||
/** |
||||
* @var integer ID of this join element |
||||
*/ |
||||
public $id; |
||||
/** |
||||
* @var BaseActiveQuery |
||||
*/ |
||||
public $query; |
||||
/** |
||||
* @var JoinElement the parent element that this element needs to join with |
||||
*/ |
||||
public $parent; |
||||
public $container; |
||||
/** |
||||
* @var JoinElement[] the child elements that need to join with this element |
||||
*/ |
||||
public $children = array(); |
||||
/** |
||||
* @var JoinElement[] the child elements that have corresponding relations declared in the AR class of this element |
||||
*/ |
||||
public $relations = array(); |
||||
/** |
||||
* @var array column aliases (alias => original name) |
||||
*/ |
||||
public $columnAliases = array(); |
||||
/** |
||||
* @var array primary key column aliases (original name => alias) |
||||
*/ |
||||
public $pkAlias = array(); |
||||
/** |
||||
* @var string|array the column(s) used for index the query results |
||||
*/ |
||||
public $key; |
||||
/** |
||||
* @var array query results for this element (PK value => AR instance or data array) |
||||
*/ |
||||
public $records = array(); |
||||
public $related = array(); |
||||
|
||||
/** |
||||
* @param integer $id |
||||
* @param ActiveRelation|ActiveQuery $query |
||||
* @param null|JoinElement $parent |
||||
* @param null|JoinElement $container |
||||
*/ |
||||
public function __construct($id, $query, $parent, $container) |
||||
{ |
||||
$this->id = $id; |
||||
$this->query = $query; |
||||
if ($parent !== null) { |
||||
$this->parent = $parent; |
||||
$this->container = $container; |
||||
$parent->children[$query->name] = $this; |
||||
if ($query->select !== false) { |
||||
$container->relations[$query->name] = $this; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public function populateData($rows) |
||||
{ |
||||
if ($this->container === null) { |
||||
foreach ($rows as $row) { |
||||
if (($key = $this->getKeyValue($row)) !== null && !isset($this->records[$key])) { |
||||
$this->records[$key] = $this->createRecord($row); |
||||
} |
||||
} |
||||
} else { |
||||
foreach ($rows as $row) { |
||||
$key = $this->getKeyValue($row); |
||||
$containerKey = $this->container->getKeyValue($row); |
||||
if ($key === null || $containerKey === null || isset($this->related[$containerKey][$key])) { |
||||
continue; |
||||
} |
||||
$this->related[$containerKey][$key] = true; |
||||
if ($this->query->asArray) { |
||||
if (isset($this->records[$key])) { |
||||
if ($this->query->hasMany) { |
||||
if ($this->query->index !== null) { |
||||
$this->container->records[$containerKey][$this->query->name][$key] =& $this->records[$key]; |
||||
} else { |
||||
$this->container->records[$containerKey][$this->query->name][] =& $this->records[$key]; |
||||
} |
||||
} else { |
||||
$this->container->records[$containerKey][$this->query->name] =& $this->records[$key]; |
||||
} |
||||
} else { |
||||
$record = $this->createRecord($row); |
||||
if ($this->query->hasMany) { |
||||
if ($this->query->index !== null) { |
||||
$this->container->records[$containerKey][$this->query->name][$key] = $record; |
||||
$this->records[$key] =& $this->container->records[$containerKey][$this->query->name][$key]; |
||||
} else { |
||||
$count = count($this->container->records[$containerKey][$this->query->name]); |
||||
$this->container->records[$containerKey][$this->query->name][] = $record; |
||||
$this->records[$key] =& $this->container->records[$containerKey][$this->query->name][$count]; |
||||
} |
||||
} else { |
||||
$this->container->records[$containerKey][$this->query->name] = $record; |
||||
$this->records[$key] =& $this->container->records[$containerKey][$this->query->name]; |
||||
} |
||||
} |
||||
} else { |
||||
if (isset($this->records[$key])) { |
||||
$record = $this->records[$key]; |
||||
} else { |
||||
$this->records[$key] = $record = $this->createRecord($row); |
||||
} |
||||
$this->container->records[$containerKey]->addRelatedRecord($this->query, $record); |
||||
} |
||||
} |
||||
} |
||||
|
||||
foreach ($this->relations as $child) { |
||||
$child->populateData($rows); |
||||
} |
||||
} |
||||
|
||||
protected function getKeyValue($row) |
||||
{ |
||||
if (is_array($this->key)) { |
||||
$key = array(); |
||||
foreach ($this->key as $alias) { |
||||
if (!isset($row[$alias])) { |
||||
return null; |
||||
} |
||||
$key[] = $row[$alias]; |
||||
} |
||||
return serialize($key); |
||||
} else { |
||||
return $row[$this->key]; |
||||
} |
||||
} |
||||
|
||||
protected function createRecord($row) |
||||
{ |
||||
$record = array(); |
||||
foreach ($this->columnAliases as $alias => $name) { |
||||
$record[$name] = $row[$alias]; |
||||
} |
||||
if ($this->query->asArray) { |
||||
foreach ($this->relations as $child) { |
||||
$record[$child->query->name] = $child->query->hasMany ? array() : null; |
||||
} |
||||
} else { |
||||
$modelClass = $this->query->modelClass; |
||||
$record = $modelClass::create($record); |
||||
foreach ($this->relations as $child) { |
||||
$record->{$child->query->name} = $child->query->hasMany ? array() : null; |
||||
} |
||||
} |
||||
return $record; |
||||
} |
||||
} |
@ -1,10 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace yii\db\ar; |
||||
|
||||
class ManyManyRelation extends Relation |
||||
{ |
||||
public $joinTable; |
||||
public $leftLink; |
||||
public $rightLink; |
||||
} |
@ -1,82 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace yii\db\ar; |
||||
|
||||
class Relation extends ActiveQuery |
||||
{ |
||||
protected $multiple = false; |
||||
public $parentClass; |
||||
/** |
||||
* @var array |
||||
*/ |
||||
public $link; |
||||
/** |
||||
* @var ActiveQuery |
||||
*/ |
||||
public $via; |
||||
|
||||
public function findWith($name, &$parentRecords) |
||||
{ |
||||
if (empty($this->link) || !is_array($this->link)) { |
||||
throw new \yii\base\Exception('invalid link'); |
||||
} |
||||
$this->addLinkCondition($parentRecords); |
||||
$records = $this->find(); |
||||
|
||||
/** @var array $map mapping key(s) to index of $parentRecords */ |
||||
$index = $this->buildRecordIndex($parentRecords, array_values($this->link)); |
||||
$this->initRecordRelation($parentRecords, $name); |
||||
foreach ($records as $record) { |
||||
$key = $this->getRecordKey($record, array_keys($this->link)); |
||||
if (isset($index[$key])) { |
||||
$parentRecords[$map[$key]][$name] = $record; |
||||
} |
||||
} |
||||
} |
||||
|
||||
protected function getRecordKey($record, $attributes) |
||||
{ |
||||
if (isset($attributes[1])) { |
||||
$key = array(); |
||||
foreach ($attributes as $attribute) { |
||||
$key[] = is_array($record) ? $record[$attribute] : $record->$attribute; |
||||
} |
||||
return serialize($key); |
||||
} else { |
||||
$attribute = $attributes[0]; |
||||
return is_array($record) ? $record[$attribute] : $record->$attribute; |
||||
} |
||||
} |
||||
|
||||
protected function buildRecordIndex($records, $attributes) |
||||
{ |
||||
$map = array(); |
||||
foreach ($records as $i => $record) { |
||||
$map[$this->getRecordKey($record, $attributes)] = $i; |
||||
} |
||||
return $map; |
||||
} |
||||
|
||||
protected function addLinkCondition($parentRecords) |
||||
{ |
||||
$attributes = array_keys($this->link); |
||||
$values = array(); |
||||
if (isset($links[1])) { |
||||
// composite keys |
||||
foreach ($parentRecords as $record) { |
||||
$v = array(); |
||||
foreach ($this->link as $attribute => $link) { |
||||
$v[$attribute] = is_array($record) ? $record[$link] : $record->$link; |
||||
} |
||||
$values[] = $v; |
||||
} |
||||
} else { |
||||
// single key |
||||
$attribute = $this->link[$links[0]]; |
||||
foreach ($parentRecords as $record) { |
||||
$values[] = is_array($record) ? $record[$attribute] : $record->$attribute; |
||||
} |
||||
} |
||||
$this->andWhere(array('in', $attributes, $values)); |
||||
} |
||||
} |
Loading…
Reference in new issue