ActiveQuery.php 6.78 KB
Newer Older
Qiang Xue committed
1 2 3 4 5 6 7 8 9 10
<?php
/**
 * ActiveFinder class file.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @link http://www.yiiframework.com/
 * @copyright Copyright &copy; 2008-2012 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */

Qiang Xue committed
11
namespace yii\db;
Qiang Xue committed
12

Qiang Xue committed
13 14 15
use yii\db\Connection;
use yii\db\Command;
use yii\db\QueryBuilder;
Qiang Xue committed
16
use yii\base\VectorIterator;
Qiang Xue committed
17
use yii\db\Expression;
Qiang Xue committed
18 19
use yii\db\Exception;

Qiang Xue committed
20
class ActiveQuery extends Query
Qiang Xue committed
21 22 23 24 25 26 27 28
{
	/**
	 * @var string the name of the ActiveRecord class.
	 */
	public $modelClass;
	/**
	 * @var array list of relations that this query should be performed with
	 */
Qiang Xue committed
29
	public $with;
Qiang Xue committed
30
	/**
Qiang Xue committed
31 32
	 * @var string the name of the column by which the query result should be indexed.
	 * This is only used when the query result is returned as an array when calling [[all()]].
Qiang Xue committed
33
	 */
Qiang Xue committed
34
	public $indexBy;
Qiang Xue committed
35 36 37 38 39 40 41 42
	/**
	 * @var boolean whether to return each record as an array. If false (default), an object
	 * of [[modelClass]] will be created to represent each record.
	 */
	public $asArray;
	/**
	 * @var array list of scopes that should be applied to this query
	 */
Qiang Xue committed
43
	public $scopes;
Qiang Xue committed
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
	/**
	 * @var string the SQL statement to be executed for retrieving AR records.
	 * This is set by [[ActiveRecord::findBySql()]].
	 */
	public $sql;

	public function __call($name, $params)
	{
		if (method_exists($this->modelClass, $name)) {
			$this->scopes[$name] = $params;
			return $this;
		} else {
			return parent::__call($name, $params);
		}
	}

	/**
	 * Executes query and returns all results as an array.
	 * @return array the query results. If the query results in nothing, an empty array will be returned.
	 */
	public function all()
	{
Qiang Xue committed
66 67
		$command = $this->createCommand();
		$rows = $command->queryAll();
68 69 70 71 72 73 74
		if ($rows !== array()) {
			$models = $this->createModels($rows);
			if (!empty($this->with)) {
				$this->fetchRelatedModels($models, $this->with);
			}
			return $models;
		} else {
Qiang Xue committed
75 76
			return array();
		}
Qiang Xue committed
77 78 79 80
	}

	/**
	 * Executes query and returns a single row of result.
Qiang Xue committed
81 82
	 * @return ActiveRecord|array|null a single row of query result. Depending on the setting of [[asArray]],
	 * the query result may be either an array or an ActiveRecord object. Null will be returned
Qiang Xue committed
83 84 85 86
	 * if the query results in nothing.
	 */
	public function one()
	{
Qiang Xue committed
87 88
		$command = $this->createCommand();
		$row = $command->queryRow();
89
		if ($row !== false && !$this->asArray) {
Qiang Xue committed
90
			/** @var $class ActiveRecord */
Qiang Xue committed
91
			$class = $this->modelClass;
Qiang Xue committed
92 93
			$model = $class::create($row);
			if (!empty($this->with)) {
Qiang Xue committed
94 95 96
				$models = array($model);
				$this->fetchRelatedModels($models, $this->with);
				$model = $models[0];
Qiang Xue committed
97 98
			}
			return $model;
99 100
		} else {
			return $row === false ? null : $row;
Qiang Xue committed
101
		}
Qiang Xue committed
102 103 104 105 106 107 108 109 110 111
	}

	/**
	 * Returns a scalar value for this query.
	 * The value returned will be the first column in the first row of the query results.
	 * @return string|boolean the value of the first column in the first row of the query result.
	 * False is returned if there is no value.
	 */
	public function value()
	{
Qiang Xue committed
112
		return $this->createCommand()->queryScalar();
Qiang Xue committed
113 114 115 116 117 118 119 120
	}

	/**
	 * Executes query and returns if matching row exists in the table.
	 * @return bool if row exists in the table.
	 */
	public function exists()
	{
Qiang Xue committed
121 122
		$this->select = array(new Expression('1'));
		return $this->value() !== false;
Qiang Xue committed
123 124 125
	}

	/**
Qiang Xue committed
126
	 * Creates a DB command that can be used to execute this query.
Qiang Xue committed
127 128
	 * @param Connection $db the DB connection used to create the DB command.
	 * If null, the DB connection returned by [[modelClass]] will be used.
Qiang Xue committed
129
	 * @return Command the created DB command instance.
Qiang Xue committed
130
	 */
Qiang Xue committed
131
	public function createCommand($db = null)
Qiang Xue committed
132
	{
Qiang Xue committed
133
		/** @var $modelClass ActiveRecord */
Qiang Xue committed
134
		$modelClass = $this->modelClass;
Qiang Xue committed
135 136 137
		if ($db === null) {
			$db = $modelClass::getDbConnection();
		}
Qiang Xue committed
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
		if ($this->sql === null) {
			if ($this->from === null) {
				$tableName = $modelClass::tableName();
				$this->from = array($tableName);
			}
			if (!empty($this->scopes)) {
				foreach ($this->scopes as $name => $config) {
					if (is_integer($name)) {
						$modelClass::$config($this);
					} else {
						array_unshift($config, $this);
						call_user_func_array(array($modelClass, $name), $config);
					}
				}
			}
			/** @var $qb QueryBuilder */
			$qb = $db->getQueryBuilder();
Qiang Xue committed
155
			$this->sql = $qb->build($this);
Qiang Xue committed
156
		}
Qiang Xue committed
157
		return $db->createCommand($this->sql, $this->params);
Qiang Xue committed
158 159
	}

Qiang Xue committed
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
	public function asArray($value = true)
	{
		$this->asArray = $value;
		return $this;
	}

	public function with()
	{
		$this->with = func_get_args();
		if (isset($this->with[0]) && is_array($this->with[0])) {
			// the parameter is given as an array
			$this->with = $this->with[0];
		}
		return $this;
	}

Qiang Xue committed
176
	public function indexBy($column)
Qiang Xue committed
177
	{
Qiang Xue committed
178
		$this->indexBy = $column;
Qiang Xue committed
179 180 181 182 183 184 185 186 187
		return $this;
	}

	public function scopes($names)
	{
		$this->scopes = $names;
		return $this;
	}

Qiang Xue committed
188 189 190 191
	protected function createModels($rows)
	{
		$models = array();
		if ($this->asArray) {
Qiang Xue committed
192
			if ($this->indexBy === null) {
Qiang Xue committed
193 194 195
				return $rows;
			}
			foreach ($rows as $row) {
Qiang Xue committed
196
				$models[$row[$this->indexBy]] = $row;
Qiang Xue committed
197 198 199 200
			}
		} else {
			/** @var $class ActiveRecord */
			$class = $this->modelClass;
Qiang Xue committed
201
			if ($this->indexBy === null) {
Qiang Xue committed
202 203 204 205 206 207
				foreach ($rows as $row) {
					$models[] = $class::create($row);
				}
			} else {
				foreach ($rows as $row) {
					$model = $class::create($row);
Qiang Xue committed
208
					$models[$model->{$this->indexBy}] = $model;
Qiang Xue committed
209 210 211 212 213
				}
			}
		}
		return $models;
	}
Qiang Xue committed
214

Qiang Xue committed
215
	protected function fetchRelatedModels(&$models, $with)
Qiang Xue committed
216 217
	{
		$primaryModel = new $this->modelClass;
Qiang Xue committed
218 219
		$relations = $this->normalizeRelations($primaryModel, $with);
		foreach ($relations as $name => $relation) {
Qiang Xue committed
220 221 222 223
			if ($relation->asArray === null) {
				// inherit asArray from primary query
				$relation->asArray = $this->asArray;
			}
Qiang Xue committed
224
			$relation->findWith($name, $models);
Qiang Xue committed
225 226
		}
	}
Qiang Xue committed
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275

	/**
	 * @param ActiveRecord $model
	 * @param array $with
	 * @return ActiveRelation[]
	 * @throws \yii\db\Exception
	 */
	protected function normalizeRelations($model, $with)
	{
		$relations = array();
		foreach ($with as $name => $options) {
			if (is_integer($name)) {
				$name = $options;
				$options = array();
			}
			if (($pos = strpos($name, '.')) !== false) {
				// with sub-relations
				$childName = substr($name, $pos + 1);
				$name = substr($name, 0, $pos);
			} else {
				$childName = null;
			}

			if (!isset($relations[$name])) {
				if (!method_exists($model, $name)) {
					throw new Exception("Unknown relation: $name");
				}
				/** @var $relation ActiveRelation */
				$relation = $model->$name();
				$relation->primaryModel = null;
				$relations[$name] = $relation;
			} else {
				$relation = $relations[$name];
			}

			if (isset($childName)) {
				if (isset($relation->with[$childName])) {
					$relation->with[$childName] = array_merge($relation->with, $options);
				} else {
					$relation->with[$childName] = $options;
				}
			} else {
				foreach ($options as $p => $v) {
					$relation->$p = $v;
				}
			}
		}
		return $relations;
	}
Qiang Xue committed
276
}