Browse Source

refactoring AR

tags/2.0.0-beta
Qiang Xue 12 years ago
parent
commit
0cd6b346b6
  1. 83
      framework/db/ar/ActiveQuery.php
  2. 4
      framework/db/ar/ActiveRecord.php
  3. 10
      framework/db/ar/ActiveRelation.php
  4. 157
      tests/unit/framework/db/ar/ActiveRecordTest.php

83
framework/db/ar/ActiveQuery.php

@ -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;
}
}

4
framework/db/ar/ActiveRecord.php

@ -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;
}

10
framework/db/ar/ActiveRelation.php

@ -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;

157
tests/unit/framework/db/ar/ActiveRecordTest.php

@ -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']);
// }
}
Loading…
Cancel
Save