ActiveDataProvider.php 5.6 KB
Newer Older
1 2 3 4 5 6 7 8 9
<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */

namespace yii\data;

10
use Yii;
11
use yii\db\ActiveQueryInterface;
12
use yii\base\InvalidConfigException;
13
use yii\base\Model;
14
use yii\db\Connection;
15
use yii\db\QueryInterface;
16
use yii\di\Instance;
17 18

/**
Qiang Xue committed
19
 * ActiveDataProvider implements a data provider based on [[\yii\db\Query]] and [[\yii\db\ActiveQuery]].
20
 *
21
 * ActiveDataProvider provides data by performing DB queries using [[query]].
22
 *
23
 * The following is an example of using ActiveDataProvider to provide ActiveRecord instances:
24 25
 *
 * ~~~
Alexander Makarov committed
26
 * $provider = new ActiveDataProvider([
27
 *     'query' => Post::find(),
Alexander Makarov committed
28
 *     'pagination' => [
29
 *         'pageSize' => 20,
Alexander Makarov committed
30 31
 *     ],
 * ]);
32 33
 *
 * // get the posts in the current page
34
 * $posts = $provider->getModels();
35 36
 * ~~~
 *
37 38 39
 * And the following example shows how to use ActiveDataProvider without ActiveRecord:
 *
 * ~~~
40
 * $query = new Query;
Alexander Makarov committed
41
 * $provider = new ActiveDataProvider([
42
 *     'query' => $query->from('post'),
Alexander Makarov committed
43
 *     'pagination' => [
44
 *         'pageSize' => 20,
Alexander Makarov committed
45 46
 *     ],
 * ]);
47 48
 *
 * // get the posts in the current page
49
 * $posts = $provider->getModels();
50 51
 * ~~~
 *
52 53 54
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @since 2.0
 */
Qiang Xue committed
55
class ActiveDataProvider extends BaseDataProvider
56
{
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
    /**
     * @var QueryInterface the query that is used to fetch data models and [[totalCount]]
     * if it is not explicitly set.
     */
    public $query;
    /**
     * @var string|callable the column that is used as the key of the data models.
     * This can be either a column name, or a callable that returns the key value of a given data model.
     *
     * If this is not set, the following rules will be used to determine the keys of the data models:
     *
     * - If [[query]] is an [[\yii\db\ActiveQuery]] instance, the primary keys of [[\yii\db\ActiveQuery::modelClass]] will be used.
     * - Otherwise, the keys of the [[models]] array will be used.
     *
     * @see getKeys()
     */
    public $key;
    /**
     * @var Connection|string the DB connection object or the application component ID of the DB connection.
     * If not set, the default DB connection will be used.
     */
    public $db;
79

80

81 82 83 84 85 86 87 88 89
    /**
     * Initializes the DB connection component.
     * This method will initialize the [[db]] property to make sure it refers to a valid DB connection.
     * @throws InvalidConfigException if [[db]] is invalid.
     */
    public function init()
    {
        parent::init();
        if (is_string($this->db)) {
90
            $this->db = Instance::ensure($this->db, Connection::className());
91 92
        }
    }
93

94 95 96 97 98 99 100 101
    /**
     * @inheritdoc
     */
    protected function prepareModels()
    {
        if (!$this->query instanceof QueryInterface) {
            throw new InvalidConfigException('The "query" property must be an instance of a class that implements the QueryInterface e.g. yii\db\Query or its subclasses.');
        }
102
        $query = clone $this->query;
103 104
        if (($pagination = $this->getPagination()) !== false) {
            $pagination->totalCount = $this->getTotalCount();
105
            $query->limit($pagination->getLimit())->offset($pagination->getOffset());
106 107
        }
        if (($sort = $this->getSort()) !== false) {
108
            $query->addOrderBy($sort->getOrders());
109
        }
110

111
        return $query->all($this->db);
112
    }
113

114 115 116 117 118 119 120 121 122 123 124 125 126 127
    /**
     * @inheritdoc
     */
    protected function prepareKeys($models)
    {
        $keys = [];
        if ($this->key !== null) {
            foreach ($models as $model) {
                if (is_string($this->key)) {
                    $keys[] = $model[$this->key];
                } else {
                    $keys[] = call_user_func($this->key, $model);
                }
            }
128

129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
            return $keys;
        } elseif ($this->query instanceof ActiveQueryInterface) {
            /** @var \yii\db\ActiveRecord $class */
            $class = $this->query->modelClass;
            $pks = $class::primaryKey();
            if (count($pks) === 1) {
                $pk = $pks[0];
                foreach ($models as $model) {
                    $keys[] = $model[$pk];
                }
            } else {
                foreach ($models as $model) {
                    $kk = [];
                    foreach ($pks as $pk) {
                        $kk[$pk] = $model[$pk];
                    }
                    $keys[] = $kk;
                }
            }

            return $keys;
        } else {
            return array_keys($models);
        }
    }

    /**
     * @inheritdoc
     */
    protected function prepareTotalCount()
    {
        if (!$this->query instanceof QueryInterface) {
            throw new InvalidConfigException('The "query" property must be an instance of a class that implements the QueryInterface e.g. yii\db\Query or its subclasses.');
        }
        $query = clone $this->query;
        return (int) $query->limit(-1)->offset(-1)->orderBy([])->count('*', $this->db);
    }

    /**
     * @inheritdoc
     */
    public function setSort($value)
    {
        parent::setSort($value);
        if (($sort = $this->getSort()) !== false && empty($sort->attributes) && $this->query instanceof ActiveQueryInterface) {
            /** @var Model $model */
            $model = new $this->query->modelClass;
            foreach ($model->attributes() as $attribute) {
                $sort->attributes[$attribute] = [
                    'asc' => [$attribute => SORT_ASC],
                    'desc' => [$attribute => SORT_DESC],
                    'label' => $model->getAttributeLabel($attribute),
                ];
            }
        }
    }
185
}