Browse Source

refactored AR relation.

tags/2.0.0-beta
Qiang Xue 12 years ago
parent
commit
c9b104f0a1
  1. 17
      framework/db/ActiveQuery.php
  2. 62
      framework/db/ActiveRecord.php
  3. 34
      framework/db/ActiveRelation.php
  4. 2
      tests/unit/data/ar/Customer.php
  5. 8
      tests/unit/data/ar/Order.php
  6. 4
      tests/unit/data/ar/OrderItem.php
  7. 2
      tests/unit/framework/db/ActiveRecordTest.php

17
framework/db/ActiveQuery.php

@ -251,16 +251,21 @@ class ActiveQuery extends Query
$childName = null; $childName = null;
} }
if (!isset($relations[$name])) { $t = strtolower($name);
if (!method_exists($model, $name)) { if (!isset($relations[$t])) {
$getter = 'get' . $name;
if (!method_exists($model, $getter)) {
throw new Exception("Unknown relation: $name"); throw new Exception("Unknown relation: $name");
} }
/** @var $relation ActiveRelation */ $relation = $model->$getter();
$relation = $model->$name(); if ($relation instanceof ActiveRelation) {
$relation->primaryModel = null; $relation->primaryModel = null;
$relations[$name] = $relation; $relations[$t] = $relation;
} else { } else {
$relation = $relations[$name]; throw new Exception("Unknown relation: $name");
}
} else {
$relation = $relations[$t];
} }
if (isset($childName)) { if (isset($childName)) {

62
framework/db/ActiveRecord.php

@ -284,18 +284,17 @@ abstract class ActiveRecord extends Model
return $this->_attributes[$name]; return $this->_attributes[$name];
} elseif (isset($this->getTableSchema()->columns[$name])) { } elseif (isset($this->getTableSchema()->columns[$name])) {
return null; return null;
} elseif (method_exists($this, $name)) {
// related records
if (isset($this->_related[$name]) || isset($this->_related) && array_key_exists($name, $this->_related)) {
return $this->_related[$name];
} else { } else {
// lazy loading related records $t = strtolower($name);
/** @var $relation ActiveRelation */ if (isset($this->_related[$t]) || $this->_related !== null && array_key_exists($t, $this->_related)) {
$relation = $this->$name(); return $this->_related[$t];
return $this->_related[$name] = $relation->multiple ? $relation->all() : $relation->one();
} }
$value = parent::__get($name);
if ($value instanceof ActiveRelation) {
return $this->_related[$t] = $value->multiple ? $value->all() : $value->one();
} else { } else {
return parent::__get($name); return $value;
}
} }
} }
@ -309,8 +308,6 @@ abstract class ActiveRecord extends Model
{ {
if (isset($this->getTableSchema()->columns[$name])) { if (isset($this->getTableSchema()->columns[$name])) {
$this->_attributes[$name] = $value; $this->_attributes[$name] = $value;
} elseif (method_exists($this, $name)) {
$this->_related[$name] = $value;
} else { } else {
parent::__set($name, $value); parent::__set($name, $value);
} }
@ -325,11 +322,13 @@ abstract class ActiveRecord extends Model
*/ */
public function __isset($name) public function __isset($name)
{ {
if (isset($this->_attributes[$name]) || isset($this->_related[$name])) { if (isset($this->getTableSchema()->columns[$name])) {
return true; return isset($this->_related[$name]);
} elseif (isset($this->getTableSchema()->columns[$name]) || method_exists($this, $name)) {
return false;
} else { } else {
$t = strtolower($name);
if (isset($this->_related[$t])) {
return true;
}
return parent::__isset($name); return parent::__isset($name);
} }
} }
@ -344,12 +343,15 @@ abstract class ActiveRecord extends Model
{ {
if (isset($this->getTableSchema()->columns[$name])) { if (isset($this->getTableSchema()->columns[$name])) {
unset($this->_attributes[$name]); unset($this->_attributes[$name]);
} elseif (method_exists($this, $name)) { } else {
unset($this->_related[$name]); $t = strtolower($name);
if (isset($this->_related[$t])) {
unset($this->_related[$t]);
} else { } else {
parent::__unset($name); parent::__unset($name);
} }
} }
}
/** /**
* Declares a `has-one` relation. * Declares a `has-one` relation.
@ -399,30 +401,12 @@ abstract class ActiveRecord extends Model
} }
/** /**
* Initializes the internal storage for the relation. * @param string $name
* This method is internally used by [[ActiveQuery]] when populating relation data. * @param mixed $value
* @param ActiveRelation $relation the relation object
*/ */
public function initRelation($relation) public function populateRelation($name, $value)
{ {
$this->_related[$relation->name] = $relation->hasMany ? array() : null; $this->_related[$name] = $value;
}
/**
* @param ActiveRelation $relation
* @param ActiveRecord $record
*/
public function addRelatedRecord($relation, $record)
{
if ($relation->hasMany) {
if ($relation->indexBy !== null) {
$this->_related[$relation->name][$record->{$relation->indexBy}] = $record;
} else {
$this->_related[$relation->name][] = $record;
}
} else {
$this->_related[$relation->name] = $record;
}
} }
/** /**

34
framework/db/ActiveRelation.php

@ -51,11 +51,14 @@ class ActiveRelation extends ActiveQuery
* @param string $relationName * @param string $relationName
* @param callback $callback * @param callback $callback
* @return ActiveRelation * @return ActiveRelation
* @throws Exception
*/ */
public function via($relationName, $callback = null) public function via($relationName, $callback = null)
{ {
/** @var $relation ActiveRelation */ $getter = 'get' . $relationName;
$relation = $this->primaryModel->$relationName(); if (method_exists($this->primaryModel, $getter)) {
$relation = $this->primaryModel->$getter();
if ($relation instanceof ActiveRelation) {
$relation->primaryModel = null; $relation->primaryModel = null;
$this->via = array($relationName, $relation); $this->via = array($relationName, $relation);
if ($callback !== null) { if ($callback !== null) {
@ -63,6 +66,9 @@ class ActiveRelation extends ActiveQuery
} }
return $this; return $this;
} }
}
throw new Exception('Unknown relation: ' . $relationName);
}
/** /**
* @param string $tableName * @param string $tableName
@ -106,9 +112,11 @@ class ActiveRelation extends ActiveQuery
list($viaName, $viaQuery) = $this->via; list($viaName, $viaQuery) = $this->via;
$viaQuery->primaryModel = $this->primaryModel; $viaQuery->primaryModel = $this->primaryModel;
if ($viaQuery->multiple) { if ($viaQuery->multiple) {
$this->primaryModel->$viaName = $viaModels = $viaQuery->all(); $viaModels = $viaQuery->all();
$this->primaryModel->populateRelation($viaName, $viaModels);
} else { } else {
$this->primaryModel->$viaName = $model = $viaQuery->one(); $model = $viaQuery->one();
$this->primaryModel->populateRelation($viaName, $model);
$viaModels = $model === null ? array() : array($model); $viaModels = $model === null ? array() : array($model);
} }
$this->filterByModels($viaModels); $this->filterByModels($viaModels);
@ -144,8 +152,12 @@ class ActiveRelation extends ActiveQuery
if (count($primaryModels) === 1 && !$this->multiple) { if (count($primaryModels) === 1 && !$this->multiple) {
$model = $this->one(); $model = $this->one();
foreach ($primaryModels as $i => $primaryModel) { foreach ($primaryModels as $i => $primaryModel) {
if ($primaryModel instanceof ActiveRecord) {
$primaryModel->populateRelation($name, $model);
} else {
$primaryModels[$i][$name] = $model; $primaryModels[$i][$name] = $model;
} }
}
return array($model); return array($model);
} else { } else {
$models = $this->all(); $models = $this->all();
@ -158,10 +170,11 @@ class ActiveRelation extends ActiveQuery
$link = array_values(isset($viaQuery) ? $viaQuery->link : $this->link); $link = array_values(isset($viaQuery) ? $viaQuery->link : $this->link);
foreach ($primaryModels as $i => $primaryModel) { foreach ($primaryModels as $i => $primaryModel) {
$key = $this->getModelKey($primaryModel, $link); $key = $this->getModelKey($primaryModel, $link);
if (isset($buckets[$key])) { $value = isset($buckets[$key]) ? $buckets[$key] : ($this->multiple ? array() : null);
$primaryModels[$i][$name] = $buckets[$key]; if ($primaryModel instanceof ActiveRecord) {
$primaryModel->populateRelation($name, $value);
} else { } else {
$primaryModels[$i][$name] = $this->multiple ? array() : null; $primaryModels[$i][$name] = $value;
} }
} }
return $models; return $models;
@ -262,11 +275,4 @@ class ActiveRelation extends ActiveQuery
$sql = $db->getQueryBuilder()->build($this); $sql = $db->getQueryBuilder()->build($this);
return $db->createCommand($sql, $this->params)->queryAll(); return $db->createCommand($sql, $this->params)->queryAll();
} }
public function link($model)
{
/**
* 1. Set models
*/
}
} }

2
tests/unit/data/ar/Customer.php

@ -13,7 +13,7 @@ class Customer extends ActiveRecord
return 'tbl_customer'; return 'tbl_customer';
} }
public function orders() public function getOrders()
{ {
return $this->hasMany('Order', array('customer_id' => 'id')); return $this->hasMany('Order', array('customer_id' => 'id'));
} }

8
tests/unit/data/ar/Order.php

@ -9,17 +9,17 @@ class Order extends ActiveRecord
return 'tbl_order'; return 'tbl_order';
} }
public function customer() public function getCustomer()
{ {
return $this->hasOne('Customer', array('id' => 'customer_id')); return $this->hasOne('Customer', array('id' => 'customer_id'));
} }
public function orderItems() public function getOrderItems()
{ {
return $this->hasMany('OrderItem', array('order_id' => 'id')); return $this->hasMany('OrderItem', array('order_id' => 'id'));
} }
public function items() public function getItems()
{ {
return $this->hasMany('Item', array('id' => 'item_id')) return $this->hasMany('Item', array('id' => 'item_id'))
->via('orderItems', function($q) { ->via('orderItems', function($q) {
@ -27,7 +27,7 @@ class Order extends ActiveRecord
})->orderBy('id'); })->orderBy('id');
} }
public function books() public function getBooks()
{ {
return $this->hasMany('Item', array('id' => 'item_id')) return $this->hasMany('Item', array('id' => 'item_id'))
->viaTable('tbl_order_item', array('order_id' => 'id')) ->viaTable('tbl_order_item', array('order_id' => 'id'))

4
tests/unit/data/ar/OrderItem.php

@ -9,12 +9,12 @@ class OrderItem extends ActiveRecord
return 'tbl_order_item'; return 'tbl_order_item';
} }
public function order() public function getOrder()
{ {
return $this->hasOne('Order', array('id' => 'order_id')); return $this->hasOne('Order', array('id' => 'order_id'));
} }
public function item() public function getItem()
{ {
return $this->hasOne('Item', array('id' => 'item_id')); return $this->hasOne('Item', array('id' => 'item_id'));
} }

2
tests/unit/framework/db/ActiveRecordTest.php

@ -105,7 +105,7 @@ class ActiveRecordTest extends \yiiunit\MysqlTestCase
$orders = $customer->orders; $orders = $customer->orders;
$this->assertEquals(2, count($orders)); $this->assertEquals(2, count($orders));
$orders = $customer->orders()->where('id=3')->all(); $orders = $customer->getOrders()->where('id=3')->all();
$this->assertEquals(1, count($orders)); $this->assertEquals(1, count($orders));
$this->assertEquals(3, $orders[0]->id); $this->assertEquals(3, $orders[0]->id);
} }

Loading…
Cancel
Save