Commit c9b104f0 by Qiang Xue

refactored AR relation.

parent 0e3e2698
......@@ -251,16 +251,21 @@ class ActiveQuery extends Query
$childName = null;
}
if (!isset($relations[$name])) {
if (!method_exists($model, $name)) {
$t = strtolower($name);
if (!isset($relations[$t])) {
$getter = 'get' . $name;
if (!method_exists($model, $getter)) {
throw new Exception("Unknown relation: $name");
}
$relation = $model->$getter();
if ($relation instanceof ActiveRelation) {
$relation->primaryModel = null;
$relations[$t] = $relation;
} else {
throw new Exception("Unknown relation: $name");
}
/** @var $relation ActiveRelation */
$relation = $model->$name();
$relation->primaryModel = null;
$relations[$name] = $relation;
} else {
$relation = $relations[$name];
$relation = $relations[$t];
}
if (isset($childName)) {
......
......@@ -284,18 +284,17 @@ abstract class ActiveRecord extends Model
return $this->_attributes[$name];
} elseif (isset($this->getTableSchema()->columns[$name])) {
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 {
$t = strtolower($name);
if (isset($this->_related[$t]) || $this->_related !== null && array_key_exists($t, $this->_related)) {
return $this->_related[$t];
}
$value = parent::__get($name);
if ($value instanceof ActiveRelation) {
return $this->_related[$t] = $value->multiple ? $value->all() : $value->one();
} else {
// lazy loading related records
/** @var $relation ActiveRelation */
$relation = $this->$name();
return $this->_related[$name] = $relation->multiple ? $relation->all() : $relation->one();
return $value;
}
} else {
return parent::__get($name);
}
}
......@@ -309,8 +308,6 @@ abstract class ActiveRecord extends Model
{
if (isset($this->getTableSchema()->columns[$name])) {
$this->_attributes[$name] = $value;
} elseif (method_exists($this, $name)) {
$this->_related[$name] = $value;
} else {
parent::__set($name, $value);
}
......@@ -325,11 +322,13 @@ abstract class ActiveRecord extends Model
*/
public function __isset($name)
{
if (isset($this->_attributes[$name]) || isset($this->_related[$name])) {
return true;
} elseif (isset($this->getTableSchema()->columns[$name]) || method_exists($this, $name)) {
return false;
if (isset($this->getTableSchema()->columns[$name])) {
return isset($this->_related[$name]);
} else {
$t = strtolower($name);
if (isset($this->_related[$t])) {
return true;
}
return parent::__isset($name);
}
}
......@@ -344,10 +343,13 @@ abstract class ActiveRecord extends Model
{
if (isset($this->getTableSchema()->columns[$name])) {
unset($this->_attributes[$name]);
} elseif (method_exists($this, $name)) {
unset($this->_related[$name]);
} else {
parent::__unset($name);
$t = strtolower($name);
if (isset($this->_related[$t])) {
unset($this->_related[$t]);
} else {
parent::__unset($name);
}
}
}
......@@ -399,30 +401,12 @@ abstract class ActiveRecord extends Model
}
/**
* Initializes the internal storage for the relation.
* This method is internally used by [[ActiveQuery]] when populating relation data.
* @param ActiveRelation $relation the relation object
* @param string $name
* @param mixed $value
*/
public function initRelation($relation)
public function populateRelation($name, $value)
{
$this->_related[$relation->name] = $relation->hasMany ? array() : null;
}
/**
* @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;
}
$this->_related[$name] = $value;
}
/**
......
......@@ -51,17 +51,23 @@ class ActiveRelation extends ActiveQuery
* @param string $relationName
* @param callback $callback
* @return ActiveRelation
* @throws Exception
*/
public function via($relationName, $callback = null)
{
/** @var $relation ActiveRelation */
$relation = $this->primaryModel->$relationName();
$relation->primaryModel = null;
$this->via = array($relationName, $relation);
if ($callback !== null) {
call_user_func($callback, $relation);
$getter = 'get' . $relationName;
if (method_exists($this->primaryModel, $getter)) {
$relation = $this->primaryModel->$getter();
if ($relation instanceof ActiveRelation) {
$relation->primaryModel = null;
$this->via = array($relationName, $relation);
if ($callback !== null) {
call_user_func($callback, $relation);
}
return $this;
}
}
return $this;
throw new Exception('Unknown relation: ' . $relationName);
}
/**
......@@ -106,9 +112,11 @@ class ActiveRelation extends ActiveQuery
list($viaName, $viaQuery) = $this->via;
$viaQuery->primaryModel = $this->primaryModel;
if ($viaQuery->multiple) {
$this->primaryModel->$viaName = $viaModels = $viaQuery->all();
$viaModels = $viaQuery->all();
$this->primaryModel->populateRelation($viaName, $viaModels);
} else {
$this->primaryModel->$viaName = $model = $viaQuery->one();
$model = $viaQuery->one();
$this->primaryModel->populateRelation($viaName, $model);
$viaModels = $model === null ? array() : array($model);
}
$this->filterByModels($viaModels);
......@@ -144,7 +152,11 @@ class ActiveRelation extends ActiveQuery
if (count($primaryModels) === 1 && !$this->multiple) {
$model = $this->one();
foreach ($primaryModels as $i => $primaryModel) {
$primaryModels[$i][$name] = $model;
if ($primaryModel instanceof ActiveRecord) {
$primaryModel->populateRelation($name, $model);
} else {
$primaryModels[$i][$name] = $model;
}
}
return array($model);
} else {
......@@ -158,10 +170,11 @@ class ActiveRelation extends ActiveQuery
$link = array_values(isset($viaQuery) ? $viaQuery->link : $this->link);
foreach ($primaryModels as $i => $primaryModel) {
$key = $this->getModelKey($primaryModel, $link);
if (isset($buckets[$key])) {
$primaryModels[$i][$name] = $buckets[$key];
$value = isset($buckets[$key]) ? $buckets[$key] : ($this->multiple ? array() : null);
if ($primaryModel instanceof ActiveRecord) {
$primaryModel->populateRelation($name, $value);
} else {
$primaryModels[$i][$name] = $this->multiple ? array() : null;
$primaryModels[$i][$name] = $value;
}
}
return $models;
......@@ -262,11 +275,4 @@ class ActiveRelation extends ActiveQuery
$sql = $db->getQueryBuilder()->build($this);
return $db->createCommand($sql, $this->params)->queryAll();
}
public function link($model)
{
/**
* 1. Set models
*/
}
}
......@@ -13,7 +13,7 @@ class Customer extends ActiveRecord
return 'tbl_customer';
}
public function orders()
public function getOrders()
{
return $this->hasMany('Order', array('customer_id' => 'id'));
}
......
......@@ -9,17 +9,17 @@ class Order extends ActiveRecord
return 'tbl_order';
}
public function customer()
public function getCustomer()
{
return $this->hasOne('Customer', array('id' => 'customer_id'));
}
public function orderItems()
public function getOrderItems()
{
return $this->hasMany('OrderItem', array('order_id' => 'id'));
}
public function items()
public function getItems()
{
return $this->hasMany('Item', array('id' => 'item_id'))
->via('orderItems', function($q) {
......@@ -27,7 +27,7 @@ class Order extends ActiveRecord
})->orderBy('id');
}
public function books()
public function getBooks()
{
return $this->hasMany('Item', array('id' => 'item_id'))
->viaTable('tbl_order_item', array('order_id' => 'id'))
......
......@@ -9,12 +9,12 @@ class OrderItem extends ActiveRecord
return 'tbl_order_item';
}
public function order()
public function getOrder()
{
return $this->hasOne('Order', array('id' => 'order_id'));
}
public function item()
public function getItem()
{
return $this->hasOne('Item', array('id' => 'item_id'));
}
......
......@@ -105,7 +105,7 @@ class ActiveRecordTest extends \yiiunit\MysqlTestCase
$orders = $customer->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(3, $orders[0]->id);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment