From c4a57a9f6c0053d4b30a086cc812d62bd994cca9 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Tue, 9 Jul 2013 07:59:08 -0400 Subject: [PATCH] Refactored Query and ActiveQuery. Finished ActiveDataProvider. --- framework/yii/data/ActiveDataProvider.php | 165 ++++++++++++++++++++++++++++++ framework/yii/db/ActiveQuery.php | 93 ++--------------- framework/yii/db/Query.php | 140 ++++++++++++++++++++++++- 3 files changed, 309 insertions(+), 89 deletions(-) create mode 100644 framework/yii/data/ActiveDataProvider.php diff --git a/framework/yii/data/ActiveDataProvider.php b/framework/yii/data/ActiveDataProvider.php new file mode 100644 index 0000000..2255503 --- /dev/null +++ b/framework/yii/data/ActiveDataProvider.php @@ -0,0 +1,165 @@ + Post::find(), + * 'pagination' => array( + * 'pageSize' => 20, + * ), + * )); + * + * // get the posts in the current page + * $posts = $provider->getItems(); + * ~~~ + * + * @author Qiang Xue + * @since 2.0 + */ +class ActiveDataProvider extends DataProvider +{ + /** + * @var ActiveQuery the query that is used to fetch data items and [[totalItemCount]] + * if it is not explicitly set. + */ + public $query; + /** + * @var string|callable the attribute that is used as the key of the data items. + * This can be either the name of the attribute, or a callable that returns the key value + * of a given data item. If not set, the primary key of [[ActiveQuery::modelClass]] will be used. + */ + public $keyAttribute; + + private $_items; + private $_keys; + private $_count; + + /** + * Returns the number of data items in the current page. + * This is equivalent to `count($provider->getItems())`. + * When [[pagination]] is false, this is the same as [[totalItemCount]]. + * @param boolean $refresh whether to recalculate the item count. If true, + * this will cause re-fetching of [[items]]. + * @return integer the number of data items in the current page. + */ + public function getItemCount($refresh = false) + { + return count($this->getItems($refresh)); + } + + /** + * Returns the total number of data items. + * When [[pagination]] is false, this returns the same value as [[itemCount]]. + * If [[totalItemCount]] is not explicitly set, it will be calculated + * using [[query]] with a COUNT query. + * @param boolean $refresh whether to recalculate the item count + * @return integer total number of possible data items. + * @throws InvalidConfigException + */ + public function getTotalItemCount($refresh = false) + { + if ($this->getPagination() === false) { + return $this->getItemCount($refresh); + } elseif ($this->_count === null || $refresh) { + if (!$this->query instanceof ActiveQuery) { + throw new InvalidConfigException('The "query" property must be an instance of ActiveQuery or its subclass.'); + } + $query = clone $this->query; + $this->_count = $query->limit(-1)->offset(-1)->count(); + } + return $this->_count; + } + + /** + * Sets the total number of data items. + * @param integer $value the total number of data items. + */ + public function setTotalItemCount($value) + { + $this->_count = $value; + } + + /** + * Returns the data items in the current page. + * @param boolean $refresh whether to re-fetch the data items. + * @return array the list of data items in the current page. + * @throws InvalidConfigException + */ + public function getItems($refresh = false) + { + if ($this->_items === null || $refresh) { + if (!$this->query instanceof ActiveQuery) { + throw new InvalidConfigException('The "query" property must be an instance of ActiveQuery or its subclass.'); + } + if (($pagination = $this->getPagination()) !== false) { + $pagination->itemCount = $this->getTotalItemCount(); + $this->query->limit($pagination->getLimit())->offset($pagination->getOffset()); + } + if (($sort = $this->getSort()) !== false) { + $this->query->orderBy($sort->getOrders()); + } + $this->_items = $this->query->all(); + } + return $this->_items; + } + + /** + * Returns the key values associated with the data items. + * @param boolean $refresh whether to re-fetch the data items and re-calculate the keys + * @return array the list of key values corresponding to [[items]]. Each data item in [[items]] + * is uniquely identified by the corresponding key value in this array. + */ + public function getKeys($refresh = false) + { + if ($this->_keys === null || $refresh) { + $this->_keys = array(); + $items = $this->getItems($refresh); + $keyAttribute = $this->keyAttribute; + if ($keyAttribute === null) { + /** @var \yii\db\ActiveRecord $class */ + $class = $this->query->modelClass; + $pks = $class::primaryKey(); + if (count($pks) === 1) { + $pk = $pks[0]; + foreach ($items as $item) { + $this->_keys[] = $item[$pk]; + } + } else { + foreach ($items as $item) { + $keys = array(); + foreach ($pks as $pk) { + $keys[] = $item[$pk]; + } + $this->_keys[] = json_encode($keys); + } + } + } else { + foreach ($items as $item) { + if (is_string($this->keyAttribute)) { + $this->_keys[] = $item[$this->keyAttribute]; + } else { + $this->_keys[] = call_user_func($item, $this->keyAttribute); + } + } + } + } + return $this->_keys; + } +} diff --git a/framework/yii/db/ActiveQuery.php b/framework/yii/db/ActiveQuery.php index 9e1cb9d..9a2fc0d 100644 --- a/framework/yii/db/ActiveQuery.php +++ b/framework/yii/db/ActiveQuery.php @@ -93,11 +93,13 @@ class ActiveQuery extends Query /** * Executes query and returns all results as an array. + * @param Connection $db the DB connection used to create the DB command. + * If null, the DB connection returned by [[modelClass]] will be used. * @return array the query results. If the query results in nothing, an empty array will be returned. */ - public function all() + public function all($db = null) { - $command = $this->createCommand(); + $command = $this->createCommand($db); $rows = $command->queryAll(); if (!empty($rows)) { $models = $this->createModels($rows); @@ -112,13 +114,15 @@ class ActiveQuery extends Query /** * Executes query and returns a single row of result. + * @param Connection $db the DB connection used to create the DB command. + * If null, the DB connection returned by [[modelClass]] will be used. * @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 * if the query results in nothing. */ - public function one() + public function one($db = null) { - $command = $this->createCommand(); + $command = $this->createCommand($db); $row = $command->queryRow(); if ($row !== false && !$this->asArray) { /** @var $class ActiveRecord */ @@ -136,87 +140,6 @@ class ActiveQuery extends Query } /** - * Returns the number of records. - * @param string $q the COUNT expression. Defaults to '*'. - * Make sure you properly quote column names. - * @return integer number of records - */ - public function count($q = '*') - { - $this->select = array("COUNT($q)"); - return $this->createCommand()->queryScalar(); - } - - /** - * Returns the sum of the specified column values. - * @param string $q the column name or expression. - * Make sure you properly quote column names. - * @return integer the sum of the specified column values - */ - public function sum($q) - { - $this->select = array("SUM($q)"); - return $this->createCommand()->queryScalar(); - } - - /** - * Returns the average of the specified column values. - * @param string $q the column name or expression. - * Make sure you properly quote column names. - * @return integer the average of the specified column values. - */ - public function average($q) - { - $this->select = array("AVG($q)"); - return $this->createCommand()->queryScalar(); - } - - /** - * Returns the minimum of the specified column values. - * @param string $q the column name or expression. - * Make sure you properly quote column names. - * @return integer the minimum of the specified column values. - */ - public function min($q) - { - $this->select = array("MIN($q)"); - return $this->createCommand()->queryScalar(); - } - - /** - * Returns the maximum of the specified column values. - * @param string $q the column name or expression. - * Make sure you properly quote column names. - * @return integer the maximum of the specified column values. - */ - public function max($q) - { - $this->select = array("MAX($q)"); - return $this->createCommand()->queryScalar(); - } - - /** - * Returns the query result as a scalar value. - * 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 the query result is empty. - */ - public function scalar() - { - return $this->createCommand()->queryScalar(); - } - - /** - * Returns a value indicating whether the query result contains any row of data. - * @return boolean whether the query result contains any row of data. - */ - public function exists() - { - $this->select = array(new Expression('1')); - return $this->scalar() !== false; - } - - /** * Creates a DB command that can be used to execute this query. * @param Connection $db the DB connection used to create the DB command. * If null, the DB connection returned by [[modelClass]] will be used. diff --git a/framework/yii/db/Query.php b/framework/yii/db/Query.php index 2e03949..0af67a9 100644 --- a/framework/yii/db/Query.php +++ b/framework/yii/db/Query.php @@ -7,6 +7,9 @@ namespace yii\db; +use Yii; +use yii\base\Component; + /** * Query represents a SELECT SQL statement in a way that is independent of DBMS. * @@ -32,7 +35,7 @@ namespace yii\db; * @author Qiang Xue * @since 2.0 */ -class Query extends \yii\base\Component +class Query extends Component { /** * Sort ascending @@ -137,13 +140,142 @@ class Query extends \yii\base\Component public function createCommand($db = null) { if ($db === null) { - $db = \Yii::$app->db; + $db = Yii::$app->getDb(); } $sql = $db->getQueryBuilder()->build($this); return $db->createCommand($sql, $this->params); } /** + * Executes the query and returns all results as an array. + * @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 array the query results. If the query results in nothing, an empty array will be returned. + */ + public function all($db = null) + { + return $this->createCommand($db)->queryAll(); + } + + /** + * Executes the query and returns a single row of result. + * @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 array|boolean the first row (in terms of an array) of the query result. False is returned if the query + * results in nothing. + */ + public function one($db = null) + { + return $this->createCommand($db)->queryRow(); + } + + /** + * Returns the query result as a scalar value. + * The value returned will be the first column in the first row of the query results. + * @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 string|boolean the value of the first column in the first row of the query result. + * False is returned if the query result is empty. + */ + public function scalar($db = null) + { + return $this->createCommand($db)->queryScalar(); + } + + /** + * Executes the query and returns the first column of the result. + * @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 array the first column of the query result. An empty array is returned if the query results in nothing. + */ + public function column($db = null) + { + return $this->createCommand($db)->queryColumn(); + } + + /** + * Returns the number of records. + * @param string $q the COUNT expression. Defaults to '*'. + * Make sure you properly quote column names in the expression. + * @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 integer number of records + */ + public function count($q = '*', $db = null) + { + $this->select = array("COUNT($q)"); + return $this->createCommand($db)->queryScalar(); + } + + /** + * Returns the sum of the specified column values. + * @param string $q the column name or expression. + * Make sure you properly quote column names in the expression. + * @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 integer the sum of the specified column values + */ + public function sum($q, $db = null) + { + $this->select = array("SUM($q)"); + return $this->createCommand($db)->queryScalar(); + } + + /** + * Returns the average of the specified column values. + * @param string $q the column name or expression. + * Make sure you properly quote column names in the expression. + * @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 integer the average of the specified column values. + */ + public function average($q, $db = null) + { + $this->select = array("AVG($q)"); + return $this->createCommand($db)->queryScalar(); + } + + /** + * Returns the minimum of the specified column values. + * @param string $q the column name or expression. + * Make sure you properly quote column names in the expression. + * @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 integer the minimum of the specified column values. + */ + public function min($q, $db = null) + { + $this->select = array("MIN($q)"); + return $this->createCommand($db)->queryScalar(); + } + + /** + * Returns the maximum of the specified column values. + * @param string $q the column name or expression. + * Make sure you properly quote column names in the expression. + * @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 integer the maximum of the specified column values. + */ + public function max($q, $db = null) + { + $this->select = array("MAX($q)"); + return $this->createCommand($db)->queryScalar(); + } + + /** + * Returns a value indicating whether the query result contains any row of data. + * @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 boolean whether the query result contains any row of data. + */ + public function exists($db = null) + { + $this->select = array(new Expression('1')); + return $this->scalar($db) !== false; + } + + /** * Sets the SELECT part of the query. * @param string|array $columns the columns to be selected. * Columns can be specified in either a string (e.g. "id, name") or an array (e.g. array('id', 'name')). @@ -536,7 +668,7 @@ class Query extends \yii\base\Component /** * Sets the LIMIT part of the query. - * @param integer $limit the limit + * @param integer $limit the limit. Use null or negative value to disable limit. * @return Query the query object itself */ public function limit($limit) @@ -547,7 +679,7 @@ class Query extends \yii\base\Component /** * Sets the OFFSET part of the query. - * @param integer $offset the offset + * @param integer $offset the offset. Use null or negative value to disable offset. * @return Query the query object itself */ public function offset($offset)