mirror of
https://github.com/yiisoft/yii2.git
synced 2025-11-02 21:41:19 +08:00
refactoring AR
This commit is contained in:
@ -32,7 +32,7 @@ class ActiveQuery extends BaseQuery
|
||||
* @var string the name of the column by which the query result should be indexed.
|
||||
* This is only used when the query result is returned as an array when calling [[all()]].
|
||||
*/
|
||||
public $index;
|
||||
public $indexBy;
|
||||
/**
|
||||
* @var boolean whether to return each record as an array. If false (default), an object
|
||||
* of [[modelClass]] will be created to represent each record.
|
||||
@ -170,9 +170,9 @@ class ActiveQuery extends BaseQuery
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function index($column)
|
||||
public function indexBy($column)
|
||||
{
|
||||
$this->index = $column;
|
||||
$this->indexBy = $column;
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -186,48 +186,34 @@ class ActiveQuery extends BaseQuery
|
||||
{
|
||||
$models = array();
|
||||
if ($this->asArray) {
|
||||
if ($this->index === null) {
|
||||
if ($this->indexBy === null) {
|
||||
return $rows;
|
||||
}
|
||||
foreach ($rows as $row) {
|
||||
$models[$row[$this->index]] = $row;
|
||||
$models[$row[$this->indexBy]] = $row;
|
||||
}
|
||||
} else {
|
||||
/** @var $class ActiveRecord */
|
||||
$class = $this->modelClass;
|
||||
if ($this->index === null) {
|
||||
if ($this->indexBy === null) {
|
||||
foreach ($rows as $row) {
|
||||
$models[] = $class::create($row);
|
||||
}
|
||||
} else {
|
||||
foreach ($rows as $row) {
|
||||
$model = $class::create($row);
|
||||
$models[$model->{$this->index}] = $model;
|
||||
$models[$model->{$this->indexBy}] = $model;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $models;
|
||||
}
|
||||
|
||||
protected function fetchRelatedModels(&$models, $relations)
|
||||
protected function fetchRelatedModels(&$models, $with)
|
||||
{
|
||||
// todo: normalize $relations
|
||||
$primaryModel = new $this->modelClass;
|
||||
foreach ($relations as $name => $properties) {
|
||||
if (is_integer($name)) {
|
||||
$name = $properties;
|
||||
$properties = array();
|
||||
}
|
||||
|
||||
if (!method_exists($primaryModel, $name)) {
|
||||
throw new Exception("Unknown relation: $name");
|
||||
}
|
||||
/** @var $relation ActiveRelation */
|
||||
$relation = $primaryModel->$name();
|
||||
$relation->primaryModel = null;
|
||||
foreach ($properties as $p => $v) {
|
||||
$relation->$p = $v;
|
||||
}
|
||||
$relations = $this->normalizeRelations($primaryModel, $with);
|
||||
foreach ($relations as $name => $relation) {
|
||||
if ($relation->asArray === null) {
|
||||
// inherit asArray from primary query
|
||||
$relation->asArray = $this->asArray;
|
||||
@ -235,4 +221,53 @@ class ActiveQuery extends BaseQuery
|
||||
$relation->findWith($name, $models);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ActiveRecord $model
|
||||
* @param array $with
|
||||
* @return ActiveRelation[]
|
||||
* @throws \yii\db\Exception
|
||||
*/
|
||||
protected function normalizeRelations($model, $with)
|
||||
{
|
||||
$relations = array();
|
||||
foreach ($with as $name => $options) {
|
||||
if (is_integer($name)) {
|
||||
$name = $options;
|
||||
$options = array();
|
||||
}
|
||||
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])) {
|
||||
if (!method_exists($model, $name)) {
|
||||
throw new Exception("Unknown relation: $name");
|
||||
}
|
||||
/** @var $relation ActiveRelation */
|
||||
$relation = $model->$name();
|
||||
$relation->primaryModel = null;
|
||||
$relations[$name] = $relation;
|
||||
} else {
|
||||
$relation = $relations[$name];
|
||||
}
|
||||
|
||||
if (isset($childName)) {
|
||||
if (isset($relation->with[$childName])) {
|
||||
$relation->with[$childName] = array_merge($relation->with, $options);
|
||||
} else {
|
||||
$relation->with[$childName] = $options;
|
||||
}
|
||||
} else {
|
||||
foreach ($options as $p => $v) {
|
||||
$relation->$p = $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $relations;
|
||||
}
|
||||
}
|
||||
|
||||
@ -430,8 +430,8 @@ abstract class ActiveRecord extends Model
|
||||
public function addRelatedRecord($relation, $record)
|
||||
{
|
||||
if ($relation->hasMany) {
|
||||
if ($relation->index !== null) {
|
||||
$this->_related[$relation->name][$record->{$relation->index}] = $record;
|
||||
if ($relation->indexBy !== null) {
|
||||
$this->_related[$relation->name][$record->{$relation->indexBy}] = $record;
|
||||
} else {
|
||||
$this->_related[$relation->name][] = $record;
|
||||
}
|
||||
|
||||
@ -34,18 +34,18 @@ class ActiveRelation extends ActiveQuery
|
||||
* @var ActiveRecord the primary model that this relation is associated with.
|
||||
* This is used only in lazy loading with dynamic query options.
|
||||
*/
|
||||
protected $primaryModel;
|
||||
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 they will be done automatically by Yii.
|
||||
*/
|
||||
protected $link;
|
||||
public $link;
|
||||
/**
|
||||
* @var array|ActiveRelation
|
||||
*/
|
||||
protected $via;
|
||||
public $via;
|
||||
|
||||
/**
|
||||
* @param string $relationName
|
||||
@ -178,7 +178,7 @@ class ActiveRelation extends ActiveQuery
|
||||
$linkKeys = array_keys($link);
|
||||
foreach ($models as $i => $model) {
|
||||
$key = $this->getModelKey($model, $linkKeys);
|
||||
if ($this->index !== null) {
|
||||
if ($this->indexBy !== null) {
|
||||
$buckets[$key][$i] = $model;
|
||||
} else {
|
||||
$buckets[$key][] = $model;
|
||||
@ -194,7 +194,7 @@ class ActiveRelation extends ActiveQuery
|
||||
$key2 = $this->getModelKey($viaModel, $linkValues);
|
||||
if (isset($buckets[$key2])) {
|
||||
foreach ($buckets[$key2] as $i => $bucket) {
|
||||
if ($this->index !== null) {
|
||||
if ($this->indexBy !== null) {
|
||||
$viaBuckets[$key1][$i] = $bucket;
|
||||
} else {
|
||||
$viaBuckets[$key1][] = $bucket;
|
||||
|
||||
@ -25,8 +25,7 @@ class ActiveRecordTest extends \yiiunit\MysqlTestCase
|
||||
$this->assertTrue($customer instanceof Customer);
|
||||
|
||||
// find all
|
||||
$result = Customer::find();
|
||||
$customers = $result->all();
|
||||
$customers = Customer::find()->all();
|
||||
$this->assertEquals(3, count($customers));
|
||||
$this->assertTrue($customers[0] instanceof Customer);
|
||||
$this->assertTrue($customers[1] instanceof Customer);
|
||||
@ -57,6 +56,23 @@ class ActiveRecordTest extends \yiiunit\MysqlTestCase
|
||||
'where' => 'id=1 OR id=2',
|
||||
))->value());
|
||||
$this->assertEquals(2, Customer::find()->select('COUNT(*)')->where('id=1 OR id=2')->value());
|
||||
|
||||
// asArray
|
||||
$customer = Customer::find()->where('id=2')->asArray()->one();
|
||||
$this->assertEquals(array(
|
||||
'id' => '2',
|
||||
'email' => 'user2@example.com',
|
||||
'name' => 'user2',
|
||||
'address' => 'address2',
|
||||
'status' => '1',
|
||||
), $customer);
|
||||
|
||||
// indexBy
|
||||
$customers = Customer::find()->indexBy('name')->orderBy('id')->all();
|
||||
$this->assertEquals(3, count($customers));
|
||||
$this->assertTrue($customers['user1'] instanceof Customer);
|
||||
$this->assertTrue($customers['user2'] instanceof Customer);
|
||||
$this->assertTrue($customers['user3'] instanceof Customer);
|
||||
}
|
||||
|
||||
public function testFindBySql()
|
||||
@ -167,6 +183,17 @@ class ActiveRecordTest extends \yiiunit\MysqlTestCase
|
||||
$this->assertEquals(2, $order->books[0]->id);
|
||||
}
|
||||
|
||||
public function testFindNestedRelation()
|
||||
{
|
||||
$customers = Customer::find()->with('orders', 'orders.items')->all();
|
||||
$this->assertEquals(3, count($customers));
|
||||
$this->assertEquals(1, count($customers[0]->orders));
|
||||
$this->assertEquals(2, count($customers[1]->orders));
|
||||
$this->assertEquals(0, count($customers[2]->orders));
|
||||
$this->assertEquals(2, count($customers[0]->orders[0]->items));
|
||||
$this->assertEquals(3, count($customers[1]->orders[0]->items));
|
||||
$this->assertEquals(1, count($customers[1]->orders[1]->items));
|
||||
}
|
||||
|
||||
// public function testInsert()
|
||||
// {
|
||||
@ -373,129 +400,5 @@ class ActiveRecordTest extends \yiiunit\MysqlTestCase
|
||||
// $customers = Customer::find()->orderBy('id')->index('name')->all();
|
||||
// $this->assertEquals(2, $customers['user2']['id']);
|
||||
// }
|
||||
//
|
||||
// public function testEagerLoading()
|
||||
// {
|
||||
// // has many
|
||||
// $customers = Customer::find()->with('orders')->orderBy('@.id')->all();
|
||||
// $this->assertEquals(3, count($customers));
|
||||
// $this->assertEquals(1, count($customers[0]->orders));
|
||||
// $this->assertEquals(2, count($customers[1]->orders));
|
||||
// $this->assertEquals(0, count($customers[2]->orders));
|
||||
//
|
||||
// // nested
|
||||
// $customers = Customer::find()->with('orders.customer')->orderBy('@.id')->all();
|
||||
// $this->assertEquals(3, count($customers));
|
||||
// $this->assertEquals(1, $customers[0]->orders[0]->customer->id);
|
||||
// $this->assertEquals(2, $customers[1]->orders[0]->customer->id);
|
||||
// $this->assertEquals(2, $customers[1]->orders[1]->customer->id);
|
||||
//
|
||||
// // has many via relation
|
||||
// $orders = Order::find()->with('items')->orderBy('@.id')->all();
|
||||
// $this->assertEquals(3, count($orders));
|
||||
// $this->assertEquals(1, $orders[0]->items[0]->id);
|
||||
// $this->assertEquals(2, $orders[0]->items[1]->id);
|
||||
// $this->assertEquals(3, $orders[1]->items[0]->id);
|
||||
// $this->assertEquals(4, $orders[1]->items[1]->id);
|
||||
// $this->assertEquals(5, $orders[1]->items[2]->id);
|
||||
//
|
||||
// // has many via join table
|
||||
// $orders = Order::find()->with('books')->orderBy('@.id')->all();
|
||||
// $this->assertEquals(2, count($orders));
|
||||
// $this->assertEquals(1, $orders[0]->books[0]->id);
|
||||
// $this->assertEquals(2, $orders[0]->books[1]->id);
|
||||
// $this->assertEquals(2, $orders[1]->books[0]->id);
|
||||
//
|
||||
// // has many and base limited
|
||||
// $orders = Order::find()->with('items')->orderBy('@.id')->limit(2)->all();
|
||||
// $this->assertEquals(2, count($orders));
|
||||
// $this->assertEquals(1, $orders[0]->items[0]->id);
|
||||
//
|
||||
// /// customize "with" query
|
||||
// $orders = Order::find()->with(array('items' => function($q) {
|
||||
// $q->orderBy('@.id DESC');
|
||||
// }))->orderBy('@.id')->limit(2)->all();
|
||||
// $this->assertEquals(2, count($orders));
|
||||
// $this->assertEquals(2, $orders[0]->items[0]->id);
|
||||
//
|
||||
// // findBySql with
|
||||
// $orders = Order::findBySql('SELECT * FROM tbl_order WHERE customer_id=2')->with('items')->all();
|
||||
// $this->assertEquals(2, count($orders));
|
||||
//
|
||||
// // index and array
|
||||
// $customers = Customer::find()->with('orders.customer')->orderBy('@.id')->index('id')->asArray()->all();
|
||||
// $this->assertEquals(3, count($customers));
|
||||
// $this->assertTrue(isset($customers[1], $customers[2], $customers[3]));
|
||||
// $this->assertTrue(is_array($customers[1]));
|
||||
// $this->assertEquals(1, count($customers[1]['orders']));
|
||||
// $this->assertEquals(2, count($customers[2]['orders']));
|
||||
// $this->assertEquals(0, count($customers[3]['orders']));
|
||||
// $this->assertTrue(is_array($customers[1]['orders'][0]['customer']));
|
||||
//
|
||||
// // count with
|
||||
// $this->assertEquals(3, Order::count());
|
||||
// $value = Order::count(array(
|
||||
// 'select' => array('COUNT(DISTINCT @.id, @.customer_id)'),
|
||||
// 'with' => 'books',
|
||||
// ));
|
||||
// $this->assertEquals(2, $value);
|
||||
//
|
||||
// }
|
||||
//
|
||||
// public function testLazyLoading()
|
||||
// {
|
||||
// // has one
|
||||
// $order = Order::find(3);
|
||||
// $this->assertTrue($order->customer instanceof Customer);
|
||||
// $this->assertEquals(2, $order->customer->id);
|
||||
//
|
||||
// // has many
|
||||
// $customer = Customer::find(2);
|
||||
// $orders = $customer->orders;
|
||||
// $this->assertEquals(2, count($orders));
|
||||
// $this->assertEquals(2, $orders[0]->id);
|
||||
// $this->assertEquals(3, $orders[1]->id);
|
||||
//
|
||||
// // has many via join table
|
||||
// $orders = Order::find()->orderBy('@.id')->all();
|
||||
// $this->assertEquals(3, count($orders));
|
||||
// $this->assertEquals(2, count($orders[0]->books));
|
||||
// $this->assertEquals(1, $orders[0]->books[0]->id);
|
||||
// $this->assertEquals(2, $orders[0]->books[1]->id);
|
||||
// $this->assertEquals(array(), $orders[1]->books);
|
||||
// $this->assertEquals(1, count($orders[2]->books));
|
||||
// $this->assertEquals(2, $orders[2]->books[0]->id);
|
||||
//
|
||||
// // customized relation query
|
||||
// $customer = Customer::find(2);
|
||||
// $orders = $customer->orders(array(
|
||||
// 'where' => '@.id = 3',
|
||||
// ));
|
||||
// $this->assertEquals(1, count($orders));
|
||||
// $this->assertEquals(3, $orders[0]->id);
|
||||
//
|
||||
// // original results are kept after customized query
|
||||
// $orders = $customer->orders;
|
||||
// $this->assertEquals(2, count($orders));
|
||||
// $this->assertEquals(2, $orders[0]->id);
|
||||
// $this->assertEquals(3, $orders[1]->id);
|
||||
//
|
||||
// // as array
|
||||
// $orders = $customer->orders(array(
|
||||
// 'asArray' => true,
|
||||
// ));
|
||||
// $this->assertEquals(2, count($orders));
|
||||
// $this->assertTrue(is_array($orders[0]));
|
||||
// $this->assertEquals(2, $orders[0]['id']);
|
||||
// $this->assertEquals(3, $orders[1]['id']);
|
||||
//
|
||||
// // using anonymous function to customize query condition
|
||||
// $orders = $customer->orders(function($q) {
|
||||
// $q->orderBy('@.id DESC')->asArray();
|
||||
// });
|
||||
// $this->assertEquals(2, count($orders));
|
||||
// $this->assertTrue(is_array($orders[0]));
|
||||
// $this->assertEquals(3, $orders[0]['id']);
|
||||
// $this->assertEquals(2, $orders[1]['id']);
|
||||
// }
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user