From 5ddc1ba18a553662d81a1f5f40b87f75db1162a2 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sun, 25 Mar 2012 11:01:01 -0400 Subject: [PATCH] Finished AR query. --- framework/db/ar/ActiveFinder.php | 34 ++++++---- framework/db/ar/ActiveRecord.php | 3 +- framework/db/ar/JoinElement.php | 4 +- tests/unit/framework/db/ar/ActiveRecordTest.php | 86 +++++++++++++------------ 4 files changed, 71 insertions(+), 56 deletions(-) diff --git a/framework/db/ar/ActiveFinder.php b/framework/db/ar/ActiveFinder.php index 3e739b9..163e842 100644 --- a/framework/db/ar/ActiveFinder.php +++ b/framework/db/ar/ActiveFinder.php @@ -19,7 +19,6 @@ use yii\db\Exception; * ActiveFinder.php is ... * todo: lazy loading * todo: clean up joinOnly and select=false - * todo: records for index != null, asArray = true * todo: refactor code * todo: count with * todo: findBySql and lazy loading cannot apply scopes for primary table @@ -84,14 +83,15 @@ class ActiveFinder extends \yii\base\Object } $this->applyScopes($query); $sql = $this->connection->getQueryBuilder()->build($query); - if (strpos($sql, '@.') !== false) { + $prefix = $this->connection->quoteTableName('@', true) . '.'; + if (strpos($sql, $prefix) !== false) { if ($query->tableAlias !== null) { $alias = $this->connection->quoteTableName($query->tableAlias) . '.'; } else { $class = $query->modelClass; $alias = $this->connection->quoteTableName($class::tableName()) . '.'; } - $sql = str_replace('@.', $alias, $sql); + $sql = str_replace($prefix, $alias, $sql); } } $command = $this->connection->createCommand($sql, $query->params); @@ -125,18 +125,31 @@ class ActiveFinder extends \yii\base\Object return $records; } + /** + * @param ActiveRecord $record + * @param ActiveRelation $relation + * @return array + */ public function findRelatedRecords($record, $relation) { $this->_joinCount = 0; $this->_tableAliases = array(); $this->_hasMany = false; $query = new ActiveQuery(get_class($record)); + $modelClass = $query->modelClass; + $table = $modelClass::getMetaData()->table; + $query->select = $table->primaryKey; + $query->limit = $relation->limit; + $query->offset = $relation->offset; $joinTree = new JoinElement($this->_joinCount++, $query, null, null); $child = $this->buildJoinTree($joinTree, $relation->name); + $child->query = $relation; + $child->container = null; $this->buildJoinTree($child, $relation->with); $this->initJoinTree($joinTree); - // todo: set where by pk + $pk = $record->getPrimaryKey(true); + $this->addPkCondition($query, $table, array($pk), $query->tableAlias . '.'); $q = new Query; $this->buildJoinQuery($joinTree, $q); @@ -146,16 +159,13 @@ class ActiveFinder extends \yii\base\Object } $rows = $q->createCommand($this->connection)->queryAll(); - $joinTree->populateData($rows); + $child->populateData($rows); - if ($query->index !== null) { - $records = array(); - foreach ($joinTree->records as $record) { - $records[$record[$query->index]] = $record; - } + $records = $relation->index === null ? array_values($child->records) : $child->records; + if ($relation->hasMany) { return $records; } else { - return array_values($joinTree->records); + return $records === array() ? null : reset($records); } } @@ -563,7 +573,7 @@ class ActiveFinder extends \yii\base\Object protected function addPkCondition($query, $table, $rows, $prefix) { - if (count($table->primaryKey) === 1) { + if (count($table->primaryKey) === 1 && count($rows) > 1) { $name = $table->primaryKey[0]; $values = array(); foreach ($rows as $row) { diff --git a/framework/db/ar/ActiveRecord.php b/framework/db/ar/ActiveRecord.php index 0a0e375..751d816 100644 --- a/framework/db/ar/ActiveRecord.php +++ b/framework/db/ar/ActiveRecord.php @@ -425,7 +425,7 @@ abstract class ActiveRecord extends Model if (isset($md->table->columns[$name])) { return null; } elseif (isset($md->relations[$name])) { - if (array_key_exists($name, $this->_related)) { + if (isset($this->_related[$name]) || $this->_related !== null && array_key_exists($name, $this->_related)) { return $this->_related[$name]; } else { return $this->_related[$name] = $this->findByRelation($md->relations[$name]); @@ -555,6 +555,7 @@ abstract class ActiveRecord extends Model } $relation = $md->relations[$relation]; } + $relation = clone $relation; foreach ($params as $name => $value) { $relation->$name = $value; } diff --git a/framework/db/ar/JoinElement.php b/framework/db/ar/JoinElement.php index 9d4bf3d..92a8194 100644 --- a/framework/db/ar/JoinElement.php +++ b/framework/db/ar/JoinElement.php @@ -57,8 +57,8 @@ class JoinElement extends \yii\base\Object /** * @var array query results for this element (PK value => AR instance or data array) */ - public $records; - public $related; + public $records = array(); + public $related = array(); /** * @param integer $id diff --git a/tests/unit/framework/db/ar/ActiveRecordTest.php b/tests/unit/framework/db/ar/ActiveRecordTest.php index bd8e7f0..89e38dd 100644 --- a/tests/unit/framework/db/ar/ActiveRecordTest.php +++ b/tests/unit/framework/db/ar/ActiveRecordTest.php @@ -267,45 +267,49 @@ class ActiveRecordTest extends \yiiunit\MysqlTestCase $this->assertTrue(is_array($customers[1]['orders'][0]['customer'])); } - /* - public function testGetSql() - { - // sql for all - $sql = Customer::find()->sql; - $this->assertEquals('SELECT * FROM `tbl_customer`', $sql); - - // sql for one row - $sql = Customer::find()->oneSql; - $this->assertEquals('SELECT * FROM tbl_customer LIMIT 1', $sql); - - // sql for count - $sql = Customer::find()->countSql; - $this->assertEquals('SELECT COUNT(*) FROM tbl_customer', $sql); - } - - public function testArrayResult() - { - Customer::find()->asArray()->one(); - Customer::find()->asArray()->all(); - } - - public function testMisc() - { - // Customer::exists() - // Customer::updateAll() - // Customer::updateCounters() - // Customer::deleteAll() - } - - - public function testLazyLoading() - { - - } - - public function testEagerLoading() - { - - } - */ + public function testLazyLoading() + { + // has one + $order = Order::find(3)->one(); + $this->assertTrue($order->customer instanceof Customer); + $this->assertEquals(2, $order->customer->id); + + // has many + $customer = Customer::find(2)->one(); + $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()->order('@.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)->one(); + $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']); + } } \ No newline at end of file