From 0e40fb4a6163573793b17da41e534c461432afb0 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Thu, 15 Mar 2012 10:57:45 -0400 Subject: [PATCH] finished AR findBySql and eager loading. --- framework/db/ar/ActiveFinder.php | 63 ++++++++++++++++++++----- framework/db/ar/JoinElement.php | 2 +- tests/unit/framework/db/ar/ActiveRecordTest.php | 3 ++ 3 files changed, 56 insertions(+), 12 deletions(-) diff --git a/framework/db/ar/ActiveFinder.php b/framework/db/ar/ActiveFinder.php index 13f92c9..1db8f2d 100644 --- a/framework/db/ar/ActiveFinder.php +++ b/framework/db/ar/ActiveFinder.php @@ -17,9 +17,10 @@ use yii\db\Exception; /** * ActiveFinder.php is ... - * todo: base limited with has_many, bySQL, lazy loading - * todo: quoting column names in 'on' clause - * + * todo: lazy loading + * todo: clean up joinOnly and select=false + * todo: records for index != null, asArray = true + * todo: refactor code * @property integer $count * * @author Qiang Xue @@ -72,6 +73,11 @@ class ActiveFinder extends \yii\base\Object $command = $this->connection->createCommand($sql, $query->params); $rows = $command->queryAll(); + return $this->createRecords($query, $rows); + } + + protected function createRecords($query, $rows) + { $records = array(); if ($query->asArray) { if ($query->index === null) { @@ -111,10 +117,38 @@ class ActiveFinder extends \yii\base\Object */ public function findRecordsWithRelations($query) { + if ($query->sql !== null) { + $command = $this->connection->createCommand($query->sql, $query->params); + $rows = $command->queryAll(); + $records = $this->createRecords($query, $rows); + $q = new ActiveQuery($query->modelClass); + $q->with = $query->with; + $q->tableAlias = 't'; + $q->asArray = $query->asArray; + $q->index = $query->index; + $modelClass = $query->modelClass; + $table = $modelClass::getMetaData()->table; + $q->select = $table->primaryKey; + $this->addPkCondition($q, $table, $rows, 't.'); + $query = $q; + } + $this->_joinCount = 0; $this->_tableAliases = array(); $this->_hasMany = false; $joinTree = new JoinElement($this->_joinCount++, $query, null, null); + + if (isset($records)) { + foreach ($records as $record) { + $pk = array(); + foreach ($table->primaryKey as $name) { + $pk[] = $record[$name]; + } + $pk = count($pk) === 1 ? $pk[0] : serialize($pk); + $joinTree->records[$pk] = $record; + } + } + $this->buildJoinTree($joinTree, $query->with); $this->initJoinTree($joinTree); @@ -143,6 +177,9 @@ class ActiveFinder extends \yii\base\Object protected function applyScopes($query) { + if ($query->modelClass === null || $query instanceof ActiveQuery && $query->sql !== null) { + return; + } $class = $query->modelClass; $class::defaultScope($query); if (is_array($query->scopes)) { @@ -262,9 +299,7 @@ class ActiveFinder extends \yii\base\Object $this->_tableAliases[$alias] = true; $element->query->tableAlias = $alias; - if ($element->query->modelClass !== null) { - $this->applyScopes($element->query); - } + $this->applyScopes($element->query); foreach ($element->children as $child) { $this->initJoinTree($child, $count); @@ -455,29 +490,35 @@ class ActiveFinder extends \yii\base\Object } $q->distinct = true; $rows = $q->createCommand($this->connection)->queryAll(); + $prefix = $activeQuery->tableAlias . '.'; + $this->addPkCondition($query, $table, $rows, $prefix); + $query->limit = $query->offset = null; + } + + protected function addPkCondition($query, $table, $rows, $prefix) + { if (count($table->primaryKey) === 1) { $name = $table->primaryKey[0]; $values = array(); foreach ($rows as $row) { $values[] = $table->columns[$name]->typecast($row[$name]); } - $query->andWhere(array('in', $activeQuery->tableAlias . '.' . $name, $values)); + $query->andWhere(array('in', $prefix . $name, $values)); } else { $ors = array('or'); $prefix = $this->connection->quoteTableName($activeQuery->tableAlias, true) . '.'; foreach ($rows as $row) { - $ands = array(); + $hash = array(); foreach ($table->primaryKey as $name) { $value = $table->columns[$name]->typecast($row[$name]); if (is_string($value)) { $value = $this->connection->quoteValue($value); } - $ands[] = $prefix . $this->connection->quoteColumnName($name, true) . '=' . $value; + $hash[$prefix . $name] = $value; } - $ors[] = implode(' AND ', $ands); + $ors[] = $hash; } $query->andWhere($ors); } - $query->limit = $query->offset = null; } } \ No newline at end of file diff --git a/framework/db/ar/JoinElement.php b/framework/db/ar/JoinElement.php index 371e6a6..51605d8 100644 --- a/framework/db/ar/JoinElement.php +++ b/framework/db/ar/JoinElement.php @@ -95,7 +95,7 @@ class JoinElement extends \yii\base\Object $modelClass = $this->query->modelClass; $this->records[$pk] = $record = $modelClass::create($attributes); foreach ($this->children as $child) { - if ($child->query->select !== false || $child->joinOnly) { + if ($child->query->select !== false && !$child->joinOnly) { $record->initRelation($child->query); } } diff --git a/tests/unit/framework/db/ar/ActiveRecordTest.php b/tests/unit/framework/db/ar/ActiveRecordTest.php index 7051db7..f483513 100644 --- a/tests/unit/framework/db/ar/ActiveRecordTest.php +++ b/tests/unit/framework/db/ar/ActiveRecordTest.php @@ -253,6 +253,9 @@ class ActiveRecordTest extends \yiiunit\MysqlTestCase $orders = Order::find()->with('items')->order('@.id')->limit(2)->all(); $this->assertEquals(2, count($orders)); + + $orders = Order::findBySql('SELECT * FROM tbl_order WHERE customer_id=2')->with('items')->all(); + $this->assertEquals(2, count($orders)); } /*