From 30c52fad9610f79e8e2afa06990b15a2bcda9a2d Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 4 Jan 2013 12:05:24 -0500 Subject: [PATCH] finished draft implementation of new AR. --- framework/db/ar/ActiveQuery.php | 29 +++++------ framework/db/ar/ActiveRecord.php | 2 +- framework/db/ar/ActiveRelation.php | 86 ++++++++++++++++++++++++-------- framework/test/TestCase.php | 2 + tests/unit/data/ar/Customer.php | 2 +- tests/unit/data/ar/Order.php | 5 +- tests/unit/framework/base/ObjectTest.php | 57 ++++++--------------- 7 files changed, 101 insertions(+), 82 deletions(-) diff --git a/framework/db/ar/ActiveQuery.php b/framework/db/ar/ActiveQuery.php index beb728a..0490c46 100644 --- a/framework/db/ar/ActiveQuery.php +++ b/framework/db/ar/ActiveQuery.php @@ -66,14 +66,15 @@ class ActiveQuery extends BaseQuery { $command = $this->createCommand(); $rows = $command->queryAll(); - if ($rows === array()) { + if ($rows !== array()) { + $models = $this->createModels($rows); + if (!empty($this->with)) { + $this->fetchRelatedModels($models, $this->with); + } + return $models; + } else { return array(); } - $models = $this->createModels($rows); - if (!empty($this->with)) { - $this->fetchRelatedModels($models, $this->with); - } - return $models; } /** @@ -86,11 +87,7 @@ class ActiveQuery extends BaseQuery { $command = $this->createCommand(); $row = $command->queryRow(); - if ($row === false) { - return false; - } elseif ($this->asArray) { - return $row; - } else { + if ($row !== false && !$this->asArray) { /** @var $class ActiveRecord */ $class = $this->modelClass; $model = $class::create($row); @@ -100,6 +97,8 @@ class ActiveQuery extends BaseQuery $model = $models[0]; } return $model; + } else { + return $row === false ? null : $row; } } @@ -126,17 +125,13 @@ class ActiveQuery extends BaseQuery /** * Creates a DB command that can be used to execute this query. - * @param Connection $db the database connection used to generate the SQL statement. - * If this parameter is not given, the `db` application component will be used. * @return Command the created DB command instance. */ - public function createCommand($db = null) + public function createCommand() { /** @var $modelClass ActiveRecord */ $modelClass = $this->modelClass; - if ($db === null) { - $db = $modelClass::getDbConnection(); - } + $db = $modelClass::getDbConnection(); if ($this->sql === null) { if ($this->from === null) { $tableName = $modelClass::tableName(); diff --git a/framework/db/ar/ActiveRecord.php b/framework/db/ar/ActiveRecord.php index 70566a9..3aa4478 100644 --- a/framework/db/ar/ActiveRecord.php +++ b/framework/db/ar/ActiveRecord.php @@ -878,7 +878,7 @@ abstract class ActiveRecord extends Model public static function create($row) { $record = static::instantiate($row); - $columns = static::model()->getTableSchema()->columns; + $columns = static::getTableSchema()->columns; foreach ($row as $name => $value) { if (isset($columns[$name])) { $record->_attributes[$name] = $value; diff --git a/framework/db/ar/ActiveRelation.php b/framework/db/ar/ActiveRelation.php index 6f10e71..c7fcc13 100644 --- a/framework/db/ar/ActiveRelation.php +++ b/framework/db/ar/ActiveRelation.php @@ -59,12 +59,21 @@ class ActiveRelation extends ActiveQuery return $this; } - public function createCommand($db = null) + public function createCommand() { if ($this->primaryModel !== null) { - $this->filterByPrimaryModels(array($this->primaryModel)); + if ($this->via !== null) { + /** @var $viaQuery ActiveRelation */ + $viaName = $this->via; + $viaQuery = $this->$viaName(); + $primaryModels = array($this->primaryModel); + $viaModels = $viaQuery->findWith($viaName, $primaryModels); + $this->filterByModels($viaModels); + } else { + $this->filterByModels(array($this->primaryModel)); + } } - return parent::createCommand($db); + return parent::createCommand(); } public function findWith($name, &$primaryModels) @@ -74,44 +83,81 @@ class ActiveRelation extends ActiveQuery } if ($this->via !== null) { + /** @var $viaQuery ActiveRelation */ + $viaName = $this->via; + $viaQuery = $this->$viaName(); + $viaModels = $viaQuery->findWith($viaName, $primaryModels); + $this->filterByModels($viaModels); + } else { + $this->filterByModels($primaryModels); } - $this->filterByPrimaryModels($primaryModels); - if (count($primaryModels) === 1 && !$this->multiple) { + $model = $this->one(); foreach ($primaryModels as $i => $primaryModel) { - $primaryModels[$i][$name] = $this->one(); + $primaryModels[$i][$name] = $model; } + return array($model); } else { $models = $this->all(); - $this->bindModels($name, $primaryModels, $models); + if (isset($viaModels, $viaQuery)) { + $buckets = $this->buildBuckets($models, $this->link, $viaModels, $viaQuery->link); + } else { + $buckets = $this->buildBuckets($models, $this->link); + } + + foreach ($primaryModels as $i => $primaryModel) { + if (isset($viaQuery)) { + $key = $this->getModelKey($primaryModel, array_values($viaQuery->link)); + } else { + $key = $this->getModelKey($primaryModel, array_values($this->link)); + } + if (isset($buckets[$key])) { + $primaryModels[$i][$name] = $buckets[$key]; + } else { + $primaryModels[$i][$name] = $this->multiple ? array() : null; + } + } + return $models; } } - protected function bindModels($name, &$primaryModels, $models) + protected function buildBuckets($models, $link, $viaModels = null, $viaLink = null) { $buckets = array(); foreach ($models as $i => $model) { - $key = $this->getModelKey($model, array_keys($this->link)); + $key = $this->getModelKey($model, array_keys($link)); if ($this->index !== null) { $buckets[$key][$i] = $model; } else { $buckets[$key][] = $model; } } + + if ($viaModels !== null) { + $viaBuckets = array(); + foreach ($viaModels as $viaModel) { + $key1 = $this->getModelKey($viaModel, array_keys($viaLink)); + $key2 = $this->getModelKey($viaModel, array_values($link)); + if (isset($buckets[$key2])) { + foreach ($buckets[$key2] as $i => $bucket) { + if ($this->index !== null) { + $viaBuckets[$key1][$i] = $bucket; + } else { + $viaBuckets[$key1][] = $bucket; + } + } + } + } + $buckets = $viaBuckets; + } + if (!$this->multiple) { foreach ($buckets as $i => $bucket) { $buckets[$i] = reset($bucket); } } - foreach ($primaryModels as $i => $primaryModel) { - $key = $this->getModelKey($primaryModel, array_values($this->link)); - if (isset($buckets[$key])) { - $primaryModels[$i][$name] = $buckets[$key]; - } else { - $primaryModels[$i][$name] = $this->multiple ? array() : null; - } - } + return $buckets; } protected function getModelKey($model, $attributes) @@ -128,13 +174,13 @@ class ActiveRelation extends ActiveQuery } } - protected function filterByPrimaryModels($primaryModels) + protected function filterByModels($models) { $attributes = array_keys($this->link); $values = array(); if (isset($links[1])) { // composite keys - foreach ($primaryModels as $model) { + foreach ($models as $model) { $v = array(); foreach ($this->link as $attribute => $link) { $v[$attribute] = is_array($model) ? $model[$link] : $model->$link; @@ -144,7 +190,7 @@ class ActiveRelation extends ActiveQuery } else { // single key $attribute = $this->link[$links[0]]; - foreach ($primaryModels as $model) { + foreach ($models as $model) { $values[] = is_array($model) ? $model[$attribute] : $model->$attribute; } } diff --git a/framework/test/TestCase.php b/framework/test/TestCase.php index 7f1a7ce..365387b 100644 --- a/framework/test/TestCase.php +++ b/framework/test/TestCase.php @@ -10,7 +10,9 @@ namespace yii\test; require_once('PHPUnit/Runner/Version.php'); +spl_autoload_unregister(array('YiiBase','autoload')); require_once('PHPUnit/Autoload.php'); +spl_autoload_register(array('YiiBase','autoload')); // put yii's autoloader at the end /** * TestCase is the base class for all test case classes. diff --git a/tests/unit/data/ar/Customer.php b/tests/unit/data/ar/Customer.php index 71dab97..05b8de1 100644 --- a/tests/unit/data/ar/Customer.php +++ b/tests/unit/data/ar/Customer.php @@ -22,7 +22,7 @@ class Customer extends ActiveRecord * @param ActiveQuery $query * @return ActiveQuery */ - public function active($query) + public static function active($query) { return $query->andWhere(array('status' => self::STATUS_ACTIVE)); } diff --git a/tests/unit/data/ar/Order.php b/tests/unit/data/ar/Order.php index b43e158..52645de 100644 --- a/tests/unit/data/ar/Order.php +++ b/tests/unit/data/ar/Order.php @@ -22,8 +22,9 @@ class Order extends ActiveRecord public function items() { return $this->hasMany('Item', array('id' => 'item_id')) - ->via('orderItems') - ->orderBy('id'); + ->via('orderItems', function($q) { + // additional query configuration + })->orderBy('id'); } public function books() diff --git a/tests/unit/framework/base/ObjectTest.php b/tests/unit/framework/base/ObjectTest.php index 0557140..99e5384 100644 --- a/tests/unit/framework/base/ObjectTest.php +++ b/tests/unit/framework/base/ObjectTest.php @@ -7,7 +7,7 @@ class Foo extends \yii\base\Object public $prop; } -class Bar extends \yii\base\Component implements \yii\base\Initable +class Bar extends \yii\base\Component { public $prop1; public $prop2; @@ -41,30 +41,6 @@ class ObjectTest extends \yiiunit\TestCase $this->object = null; } - public function testNewInstance() - { - $foo = Foo::newInstance(array( - 'prop' => array( - 'test' => 'test', - ), - )); - - $this->assertEquals('test', $foo->prop['test']); - - $bar = Bar::newInstance(array(), 10, 20); - $this->assertEquals(30, $bar->prop1); - $this->assertEquals(null, $bar->prop2); - $this->assertEquals(3, $bar->prop3); - - $bar = Bar::newInstance(array( - 'prop2' => 'x', - 'prop3' => 400, - ), 100, 200); - $this->assertEquals(300, $bar->prop1); - $this->assertEquals('x', $bar->prop2); - $this->assertEquals(3, $bar->prop3); - } - public function testHasProperty() { $this->assertTrue($this->object->hasProperty('Text'), "Component hasn't property Text"); @@ -88,19 +64,19 @@ class ObjectTest extends \yiiunit\TestCase public function testGetProperty() { - $this->assertTrue('default'===$this->object->Text); - $this->setExpectedException('yii\base\Exception'); - $value2=$this->object->Caption; + $this->assertTrue('default' === $this->object->Text); + $this->setExpectedException('yii\base\BadPropertyException'); + $value2 = $this->object->Caption; } public function testSetProperty() { - $value='new value'; - $this->object->Text=$value; - $text=$this->object->Text; - $this->assertTrue($value===$this->object->Text); - $this->setExpectedException('yii\base\Exception'); - $this->object->NewMember=$value; + $value = 'new value'; + $this->object->Text = $value; + $text = $this->object->Text; + $this->assertTrue($value === $this->object->Text); + $this->setExpectedException('yii\base\BadPropertyException'); + $this->object->NewMember = $value; } public function testIsset() @@ -112,7 +88,7 @@ class ObjectTest extends \yiiunit\TestCase $this->assertFalse(isset($this->object->Text)); $this->assertFalse(!empty($this->object->Text)); - $this->object->Text=''; + $this->object->Text = ''; $this->assertTrue(isset($this->object->Text)); $this->assertTrue(empty($this->object->Text)); } @@ -131,20 +107,19 @@ class NewObject extends \yii\base\Component public function setText($value) { - $this->_text=$value; + $this->_text = $value; } public function getObject() { - if(!$this->_object) - { - $this->_object=new self; - $this->_object->_text='object text'; + if (!$this->_object) { + $this->_object = new self; + $this->_object->_text = 'object text'; } return $this->_object; } - public function exprEvaluator($p1,$comp) + public function exprEvaluator($p1, $comp) { return "Hello $p1"; }