mirror of
https://github.com/yiisoft/yii2.git
synced 2025-12-10 15:50:55 +08:00
moved db interfaces and traits back to yii/db.
This commit is contained in:
@@ -7,8 +7,6 @@
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
use yii\ar\ActiveQueryInterface;
|
||||
use yii\ar\ActiveQueryTrait;
|
||||
|
||||
/**
|
||||
* ActiveQuery represents a DB query associated with an Active Record class.
|
||||
|
||||
77
framework/yii/db/ActiveQueryInterface.php
Normal file
77
framework/yii/db/ActiveQueryInterface.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* ActiveQueryInterface defines the common interface to be implemented by active record query classes.
|
||||
*
|
||||
* A class implementing this interface should also use [[ActiveQueryTrait]].
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @since 2.0
|
||||
*/
|
||||
interface ActiveQueryInterface extends QueryInterface
|
||||
{
|
||||
/**
|
||||
* Sets the [[asArray]] property.
|
||||
* @param boolean $value whether to return the query results in terms of arrays instead of Active Records.
|
||||
* @return static the query object itself
|
||||
*/
|
||||
public function asArray($value = true);
|
||||
|
||||
/**
|
||||
* Sets the [[indexBy]] property.
|
||||
* @param string|callable $column the name of the column by which the query results should be indexed by.
|
||||
* This can also be a callable (e.g. anonymous function) that returns the index value based on the given
|
||||
* row or model data. The signature of the callable should be:
|
||||
*
|
||||
* ~~~
|
||||
* // $model is an AR instance when `asArray` is false,
|
||||
* // or an array of column values when `asArray` is true.
|
||||
* function ($model)
|
||||
* {
|
||||
* // return the index value corresponding to $model
|
||||
* }
|
||||
* ~~~
|
||||
*
|
||||
* @return static the query object itself
|
||||
*/
|
||||
public function indexBy($column);
|
||||
|
||||
/**
|
||||
* Specifies the relations with which this query should be performed.
|
||||
*
|
||||
* The parameters to this method can be either one or multiple strings, or a single array
|
||||
* of relation names and the optional callbacks to customize the relations.
|
||||
*
|
||||
* A relation name can refer to a relation defined in [[modelClass]]
|
||||
* or a sub-relation that stands for a relation of a related record.
|
||||
* For example, `orders.address` means the `address` relation defined
|
||||
* in the model class corresponding to the `orders` relation.
|
||||
*
|
||||
* The followings are some usage examples:
|
||||
*
|
||||
* ~~~
|
||||
* // find customers together with their orders and country
|
||||
* Customer::find()->with('orders', 'country')->all();
|
||||
* // find customers together with their orders and the orders' shipping address
|
||||
* Customer::find()->with('orders.address')->all();
|
||||
* // find customers together with their country and orders of status 1
|
||||
* Customer::find()->with([
|
||||
* 'orders' => function($query) {
|
||||
* $query->andWhere('status = 1');
|
||||
* },
|
||||
* 'country',
|
||||
* ])->all();
|
||||
* ~~~
|
||||
*
|
||||
* @return static the query object itself
|
||||
*/
|
||||
public function with();
|
||||
}
|
||||
199
framework/yii/db/ActiveQueryTrait.php
Normal file
199
framework/yii/db/ActiveQueryTrait.php
Normal file
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* ActiveQueryTrait implements the common methods and properties for active record query classes.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @since 2.0
|
||||
*/
|
||||
trait ActiveQueryTrait
|
||||
{
|
||||
/**
|
||||
* @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 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;
|
||||
|
||||
|
||||
/**
|
||||
* PHP magic method.
|
||||
* This method allows calling static method defined in [[modelClass]] via this query object.
|
||||
* It is mainly implemented for supporting the feature of scope.
|
||||
* @param string $name the method name to be called
|
||||
* @param array $params the parameters passed to the method
|
||||
* @return mixed the method return result
|
||||
*/
|
||||
public function __call($name, $params)
|
||||
{
|
||||
if (method_exists($this->modelClass, $name)) {
|
||||
array_unshift($params, $this);
|
||||
call_user_func_array([$this->modelClass, $name], $params);
|
||||
return $this;
|
||||
} else {
|
||||
return parent::__call($name, $params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the [[asArray]] property.
|
||||
* @param boolean $value whether to return the query results in terms of arrays instead of Active Records.
|
||||
* @return static the query object itself
|
||||
*/
|
||||
public function asArray($value = true)
|
||||
{
|
||||
$this->asArray = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the relations with which this query should be performed.
|
||||
*
|
||||
* The parameters to this method can be either one or multiple strings, or a single array
|
||||
* of relation names and the optional callbacks to customize the relations.
|
||||
*
|
||||
* A relation name can refer to a relation defined in [[modelClass]]
|
||||
* or a sub-relation that stands for a relation of a related record.
|
||||
* For example, `orders.address` means the `address` relation defined
|
||||
* in the model class corresponding to the `orders` relation.
|
||||
*
|
||||
* The followings are some usage examples:
|
||||
*
|
||||
* ~~~
|
||||
* // find customers together with their orders and country
|
||||
* Customer::find()->with('orders', 'country')->all();
|
||||
* // find customers together with their orders and the orders' shipping address
|
||||
* Customer::find()->with('orders.address')->all();
|
||||
* // find customers together with their country and orders of status 1
|
||||
* Customer::find()->with([
|
||||
* 'orders' => function($query) {
|
||||
* $query->andWhere('status = 1');
|
||||
* },
|
||||
* 'country',
|
||||
* ])->all();
|
||||
* ~~~
|
||||
*
|
||||
* @return static the query object itself
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts found rows into model instances
|
||||
* @param array $rows
|
||||
* @return array|ActiveRecord[]
|
||||
*/
|
||||
private function createModels($rows)
|
||||
{
|
||||
$models = [];
|
||||
if ($this->asArray) {
|
||||
if ($this->indexBy === null) {
|
||||
return $rows;
|
||||
}
|
||||
foreach ($rows as $row) {
|
||||
if (is_string($this->indexBy)) {
|
||||
$key = $row[$this->indexBy];
|
||||
} else {
|
||||
$key = call_user_func($this->indexBy, $row);
|
||||
}
|
||||
$models[$key] = $row;
|
||||
}
|
||||
} else {
|
||||
/** @var ActiveRecord $class */
|
||||
$class = $this->modelClass;
|
||||
if ($this->indexBy === null) {
|
||||
foreach ($rows as $row) {
|
||||
$models[] = $class::create($row);
|
||||
}
|
||||
} else {
|
||||
foreach ($rows as $row) {
|
||||
$model = $class::create($row);
|
||||
if (is_string($this->indexBy)) {
|
||||
$key = $model->{$this->indexBy};
|
||||
} else {
|
||||
$key = call_user_func($this->indexBy, $model);
|
||||
}
|
||||
$models[$key] = $model;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $models;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ActiveRecord[] $models
|
||||
* @param array $with
|
||||
*/
|
||||
private function populateRelations(&$models, $with)
|
||||
{
|
||||
$primaryModel = new $this->modelClass;
|
||||
$relations = $this->normalizeRelations($primaryModel, $with);
|
||||
foreach ($relations as $name => $relation) {
|
||||
if ($relation->asArray === null) {
|
||||
// inherit asArray from primary query
|
||||
$relation->asArray = $this->asArray;
|
||||
}
|
||||
$relation->findWith($name, $models);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ActiveRecord $model
|
||||
* @param array $with
|
||||
* @return ActiveRelationInterface[]
|
||||
*/
|
||||
private function normalizeRelations($model, $with)
|
||||
{
|
||||
$relations = [];
|
||||
foreach ($with as $name => $callback) {
|
||||
if (is_integer($name)) {
|
||||
$name = $callback;
|
||||
$callback = null;
|
||||
}
|
||||
if (($pos = strpos($name, '.')) !== false) {
|
||||
// with sub-relations
|
||||
$childName = substr($name, $pos + 1);
|
||||
$name = substr($name, 0, $pos);
|
||||
} else {
|
||||
$childName = null;
|
||||
}
|
||||
|
||||
if (!isset($relations[$name])) {
|
||||
$relation = $model->getRelation($name);
|
||||
$relation->primaryModel = null;
|
||||
$relations[$name] = $relation;
|
||||
} else {
|
||||
$relation = $relations[$name];
|
||||
}
|
||||
|
||||
if (isset($childName)) {
|
||||
$relation->with[$childName] = $callback;
|
||||
} elseif ($callback !== null) {
|
||||
call_user_func($callback, $relation);
|
||||
}
|
||||
}
|
||||
return $relations;
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
use yii\ar\ActiveRelationInterface;
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\base\Model;
|
||||
use yii\base\InvalidParamException;
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
use yii\ar\ActiveRelationInterface;
|
||||
use yii\ar\ActiveRelationTrait;
|
||||
|
||||
/**
|
||||
* ActiveRelation represents a relation between two Active Record classes.
|
||||
|
||||
29
framework/yii/db/ActiveRelationInterface.php
Normal file
29
framework/yii/db/ActiveRelationInterface.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
/**
|
||||
* ActiveRelationInterface defines the common interface to be implemented by active record relation classes.
|
||||
*
|
||||
* A class implementing this interface should also use [[ActiveRelationTrait]].
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @since 2.0
|
||||
*/
|
||||
interface ActiveRelationInterface extends ActiveQueryInterface
|
||||
{
|
||||
/**
|
||||
* Specifies the relation associated with the pivot table.
|
||||
* @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]].
|
||||
* @param callable $callable a PHP callback for customizing the relation associated with the pivot table.
|
||||
* Its signature should be `function($query)`, where `$query` is the query to be customized.
|
||||
* @return static the relation object itself.
|
||||
*/
|
||||
public function via($relationName, $callable = null);
|
||||
}
|
||||
246
framework/yii/db/ActiveRelationTrait.php
Normal file
246
framework/yii/db/ActiveRelationTrait.php
Normal file
@@ -0,0 +1,246 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\db;
|
||||
|
||||
use yii\base\InvalidConfigException;
|
||||
|
||||
/**
|
||||
* ActiveRelationTrait implements the common methods and properties for active record relation classes.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @since 2.0
|
||||
*/
|
||||
trait ActiveRelationTrait
|
||||
{
|
||||
/**
|
||||
* @var boolean whether this relation should populate all query results into AR instances.
|
||||
* If false, only the first row of the results will be retrieved.
|
||||
*/
|
||||
public $multiple;
|
||||
/**
|
||||
* @var ActiveRecord the primary model that this relation is associated with.
|
||||
* This is used only in lazy loading with dynamic query options.
|
||||
*/
|
||||
public $primaryModel;
|
||||
/**
|
||||
* @var array the columns of the primary and foreign tables that establish the relation.
|
||||
* The array keys must be columns of the table for this relation, and the array values
|
||||
* must be the corresponding columns from the primary table.
|
||||
* Do not prefix or quote the column names as this will be done automatically by Yii.
|
||||
*/
|
||||
public $link;
|
||||
/**
|
||||
* @var array the query associated with the pivot table. Please call [[via()]]
|
||||
* to set this property instead of directly setting it.
|
||||
*/
|
||||
public $via;
|
||||
|
||||
/**
|
||||
* Clones internal objects.
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
// make a clone of "via" object so that the same query object can be reused multiple times
|
||||
if (is_object($this->via)) {
|
||||
$this->via = clone $this->via;
|
||||
} elseif (is_array($this->via)) {
|
||||
$this->via = [$this->via[0], clone $this->via[1]];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the relation associated with the pivot table.
|
||||
* @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]].
|
||||
* @param callable $callable a PHP callback for customizing the relation associated with the pivot table.
|
||||
* Its signature should be `function($query)`, where `$query` is the query to be customized.
|
||||
* @return static the relation object itself.
|
||||
*/
|
||||
public function via($relationName, $callable = null)
|
||||
{
|
||||
$relation = $this->primaryModel->getRelation($relationName);
|
||||
$this->via = [$relationName, $relation];
|
||||
if ($callable !== null) {
|
||||
call_user_func($callable, $relation);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the related records and populates them into the primary models.
|
||||
* This method is internally used by [[ActiveQuery]]. Do not call it directly.
|
||||
* @param string $name the relation name
|
||||
* @param array $primaryModels primary models
|
||||
* @return array the related models
|
||||
* @throws InvalidConfigException
|
||||
*/
|
||||
public function findWith($name, &$primaryModels)
|
||||
{
|
||||
if (!is_array($this->link)) {
|
||||
throw new InvalidConfigException('Invalid link: it must be an array of key-value pairs.');
|
||||
}
|
||||
|
||||
if ($this->via instanceof self) {
|
||||
// via pivot table
|
||||
/** @var ActiveRelationTrait $viaQuery */
|
||||
$viaQuery = $this->via;
|
||||
$viaModels = $viaQuery->findPivotRows($primaryModels);
|
||||
$this->filterByModels($viaModels);
|
||||
} elseif (is_array($this->via)) {
|
||||
// via relation
|
||||
/** @var ActiveRelationTrait $viaQuery */
|
||||
list($viaName, $viaQuery) = $this->via;
|
||||
$viaQuery->primaryModel = null;
|
||||
$viaModels = $viaQuery->findWith($viaName, $primaryModels);
|
||||
$this->filterByModels($viaModels);
|
||||
} else {
|
||||
$this->filterByModels($primaryModels);
|
||||
}
|
||||
|
||||
if (count($primaryModels) === 1 && !$this->multiple) {
|
||||
$model = $this->one();
|
||||
foreach ($primaryModels as $i => $primaryModel) {
|
||||
if ($primaryModel instanceof ActiveRecord) {
|
||||
$primaryModel->populateRelation($name, $model);
|
||||
} else {
|
||||
$primaryModels[$i][$name] = $model;
|
||||
}
|
||||
}
|
||||
return [$model];
|
||||
} else {
|
||||
$models = $this->all();
|
||||
if (isset($viaModels, $viaQuery)) {
|
||||
$buckets = $this->buildBuckets($models, $this->link, $viaModels, $viaQuery->link);
|
||||
} else {
|
||||
$buckets = $this->buildBuckets($models, $this->link);
|
||||
}
|
||||
|
||||
$link = array_values(isset($viaQuery) ? $viaQuery->link : $this->link);
|
||||
foreach ($primaryModels as $i => $primaryModel) {
|
||||
$key = $this->getModelKey($primaryModel, $link);
|
||||
$value = isset($buckets[$key]) ? $buckets[$key] : ($this->multiple ? [] : null);
|
||||
if ($primaryModel instanceof ActiveRecord) {
|
||||
$primaryModel->populateRelation($name, $value);
|
||||
} else {
|
||||
$primaryModels[$i][$name] = $value;
|
||||
}
|
||||
}
|
||||
return $models;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $models
|
||||
* @param array $link
|
||||
* @param array $viaModels
|
||||
* @param array $viaLink
|
||||
* @return array
|
||||
*/
|
||||
private function buildBuckets($models, $link, $viaModels = null, $viaLink = null)
|
||||
{
|
||||
$buckets = [];
|
||||
$linkKeys = array_keys($link);
|
||||
foreach ($models as $i => $model) {
|
||||
$key = $this->getModelKey($model, $linkKeys);
|
||||
if ($this->indexBy !== null) {
|
||||
$buckets[$key][$i] = $model;
|
||||
} else {
|
||||
$buckets[$key][] = $model;
|
||||
}
|
||||
}
|
||||
|
||||
if ($viaModels !== null) {
|
||||
$viaBuckets = [];
|
||||
$viaLinkKeys = array_keys($viaLink);
|
||||
$linkValues = array_values($link);
|
||||
foreach ($viaModels as $viaModel) {
|
||||
$key1 = $this->getModelKey($viaModel, $viaLinkKeys);
|
||||
$key2 = $this->getModelKey($viaModel, $linkValues);
|
||||
if (isset($buckets[$key2])) {
|
||||
foreach ($buckets[$key2] as $i => $bucket) {
|
||||
if ($this->indexBy !== null) {
|
||||
$viaBuckets[$key1][$i] = $bucket;
|
||||
} else {
|
||||
$viaBuckets[$key1][] = $bucket;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$buckets = $viaBuckets;
|
||||
}
|
||||
|
||||
if (!$this->multiple) {
|
||||
foreach ($buckets as $i => $bucket) {
|
||||
$buckets[$i] = reset($bucket);
|
||||
}
|
||||
}
|
||||
return $buckets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ActiveRecord|array $model
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
private function getModelKey($model, $attributes)
|
||||
{
|
||||
if (count($attributes) > 1) {
|
||||
$key = [];
|
||||
foreach ($attributes as $attribute) {
|
||||
$key[] = $model[$attribute];
|
||||
}
|
||||
return serialize($key);
|
||||
} else {
|
||||
$attribute = reset($attributes);
|
||||
return $model[$attribute];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $models
|
||||
*/
|
||||
private function filterByModels($models)
|
||||
{
|
||||
$attributes = array_keys($this->link);
|
||||
$values = [];
|
||||
if (count($attributes) === 1) {
|
||||
// single key
|
||||
$attribute = reset($this->link);
|
||||
foreach ($models as $model) {
|
||||
if (($value = $model[$attribute]) !== null) {
|
||||
$values[] = $value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// composite keys
|
||||
foreach ($models as $model) {
|
||||
$v = [];
|
||||
foreach ($this->link as $attribute => $link) {
|
||||
$v[$attribute] = $model[$link];
|
||||
}
|
||||
$values[] = $v;
|
||||
}
|
||||
}
|
||||
$this->andWhere(['in', $attributes, array_unique($values, SORT_REGULAR)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ActiveRecord[] $primaryModels
|
||||
* @return array
|
||||
*/
|
||||
private function findPivotRows($primaryModels)
|
||||
{
|
||||
if (empty($primaryModels)) {
|
||||
return [];
|
||||
}
|
||||
$this->filterByModels($primaryModels);
|
||||
/** @var ActiveRecord $primaryModel */
|
||||
$primaryModel = reset($primaryModels);
|
||||
return $this->asArray()->all($primaryModel->getDb());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user