1 | <?php |
||
2 | /** |
||
3 | * @link https://www.yiiframework.com/ |
||
4 | * @copyright Copyright (c) 2008 Yii Software LLC |
||
5 | * @license https://www.yiiframework.com/license/ |
||
6 | */ |
||
7 | |||
8 | namespace yii\data; |
||
9 | |||
10 | use yii\base\InvalidConfigException; |
||
11 | use yii\base\Model; |
||
12 | use yii\db\ActiveQueryInterface; |
||
13 | use yii\db\Connection; |
||
14 | use yii\db\QueryInterface; |
||
15 | use yii\di\Instance; |
||
16 | |||
17 | /** |
||
18 | * ActiveDataProvider implements a data provider based on [[\yii\db\Query]] and [[\yii\db\ActiveQuery]]. |
||
19 | * |
||
20 | * ActiveDataProvider provides data by performing DB queries using [[query]]. |
||
21 | * |
||
22 | * The following is an example of using ActiveDataProvider to provide ActiveRecord instances: |
||
23 | * |
||
24 | * ```php |
||
25 | * $provider = new ActiveDataProvider([ |
||
26 | * 'query' => Post::find(), |
||
27 | * 'pagination' => [ |
||
28 | * 'pageSize' => 20, |
||
29 | * ], |
||
30 | * ]); |
||
31 | * |
||
32 | * // get the posts in the current page |
||
33 | * $posts = $provider->getModels(); |
||
34 | * ``` |
||
35 | * |
||
36 | * And the following example shows how to use ActiveDataProvider without ActiveRecord: |
||
37 | * |
||
38 | * ```php |
||
39 | * $query = new Query(); |
||
40 | * $provider = new ActiveDataProvider([ |
||
41 | * 'query' => $query->from('post'), |
||
42 | * 'pagination' => [ |
||
43 | * 'pageSize' => 20, |
||
44 | * ], |
||
45 | * ]); |
||
46 | * |
||
47 | * // get the posts in the current page |
||
48 | * $posts = $provider->getModels(); |
||
49 | * ``` |
||
50 | * |
||
51 | * For more details and usage information on ActiveDataProvider, see the [guide article on data providers](guide:output-data-providers). |
||
52 | * |
||
53 | * @author Qiang Xue <[email protected]> |
||
54 | * @since 2.0 |
||
55 | */ |
||
56 | class ActiveDataProvider extends BaseDataProvider |
||
57 | { |
||
58 | /** |
||
59 | * @var QueryInterface|null the query that is used to fetch data models and [[totalCount]] if it is not explicitly set. |
||
60 | */ |
||
61 | public $query; |
||
62 | /** |
||
63 | * @var string|callable|null the column that is used as the key of the data models. |
||
64 | * This can be either a column name, or a callable that returns the key value of a given data model. |
||
65 | * |
||
66 | * If this is not set, the following rules will be used to determine the keys of the data models: |
||
67 | * |
||
68 | * - If [[query]] is an [[\yii\db\ActiveQuery]] instance, the primary keys of [[\yii\db\ActiveQuery::modelClass]] will be used. |
||
69 | * - Otherwise, the keys of the [[models]] array will be used. |
||
70 | * |
||
71 | * @see getKeys() |
||
72 | */ |
||
73 | public $key; |
||
74 | /** |
||
75 | * @var Connection|array|string|null the DB connection object or the application component ID of the DB connection. |
||
76 | * If set it overrides [[query]] default DB connection. |
||
77 | * Starting from version 2.0.2, this can also be a configuration array for creating the object. |
||
78 | */ |
||
79 | public $db; |
||
80 | |||
81 | |||
82 | /** |
||
83 | * Initializes the DB connection component. |
||
84 | * This method will initialize the [[db]] property (when set) to make sure it refers to a valid DB connection. |
||
85 | * @throws InvalidConfigException if [[db]] is invalid. |
||
86 | */ |
||
87 | 35 | public function init() |
|
88 | { |
||
89 | 35 | parent::init(); |
|
90 | 35 | if ($this->db !== null) { |
|
91 | 12 | $this->db = Instance::ensure($this->db); |
|
92 | } |
||
93 | } |
||
94 | |||
95 | /** |
||
96 | * {@inheritdoc} |
||
97 | */ |
||
98 | 25 | protected function prepareModels() |
|
99 | { |
||
100 | 25 | if (!$this->query instanceof QueryInterface) { |
|
101 | 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 | } |
||
103 | 25 | $query = clone $this->query; |
|
104 | 25 | if (($pagination = $this->getPagination()) !== false) { |
|
105 | 25 | $pagination->totalCount = $this->getTotalCount(); |
|
106 | 25 | if ($pagination->totalCount === 0) { |
|
107 | 4 | return []; |
|
108 | } |
||
109 | 21 | $query->limit($pagination->getLimit())->offset($pagination->getOffset()); |
|
110 | } |
||
111 | 21 | if (($sort = $this->getSort()) !== false) { |
|
112 | 21 | $query->addOrderBy($sort->getOrders()); |
|
113 | } |
||
114 | |||
115 | 21 | return $query->all($this->db); |
|
116 | } |
||
117 | |||
118 | /** |
||
119 | * {@inheritdoc} |
||
120 | */ |
||
121 | 27 | protected function prepareKeys($models) |
|
122 | { |
||
123 | 27 | $keys = []; |
|
124 | 27 | if ($this->key !== null) { |
|
125 | foreach ($models as $model) { |
||
126 | if (is_string($this->key)) { |
||
127 | $keys[] = $model[$this->key]; |
||
128 | } else { |
||
129 | $keys[] = call_user_func($this->key, $model); |
||
130 | } |
||
131 | } |
||
132 | |||
133 | return $keys; |
||
134 | 27 | } elseif ($this->query instanceof ActiveQueryInterface) { |
|
135 | /* @var $class \yii\db\ActiveRecordInterface */ |
||
136 | 15 | $class = $this->query->modelClass; |
|
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
137 | 15 | $pks = $class::primaryKey(); |
|
138 | 15 | if (count($pks) === 1) { |
|
139 | 15 | $pk = $pks[0]; |
|
140 | 15 | foreach ($models as $model) { |
|
141 | 14 | $keys[] = $model[$pk]; |
|
142 | } |
||
143 | } else { |
||
144 | foreach ($models as $model) { |
||
145 | $kk = []; |
||
146 | foreach ($pks as $pk) { |
||
147 | $kk[$pk] = $model[$pk]; |
||
148 | } |
||
149 | $keys[] = $kk; |
||
150 | } |
||
151 | } |
||
152 | |||
153 | 15 | return $keys; |
|
154 | } |
||
155 | |||
156 | 12 | return array_keys($models); |
|
157 | } |
||
158 | |||
159 | /** |
||
160 | * {@inheritdoc} |
||
161 | */ |
||
162 | 25 | protected function prepareTotalCount() |
|
163 | { |
||
164 | 25 | if (!$this->query instanceof QueryInterface) { |
|
165 | 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.'); |
||
166 | } |
||
167 | 25 | $query = clone $this->query; |
|
168 | 25 | return (int) $query->limit(-1)->offset(-1)->orderBy([])->count('*', $this->db); |
|
169 | } |
||
170 | |||
171 | /** |
||
172 | * {@inheritdoc} |
||
173 | */ |
||
174 | 30 | public function setSort($value) |
|
175 | { |
||
176 | 30 | parent::setSort($value); |
|
177 | 30 | if ($this->query instanceof ActiveQueryInterface && ($sort = $this->getSort()) !== false) { |
|
178 | /* @var $modelClass Model */ |
||
179 | 20 | $modelClass = $this->query->modelClass; |
|
180 | 20 | $model = $modelClass::instance(); |
|
181 | 20 | if (empty($sort->attributes)) { |
|
182 | 16 | foreach ($model->attributes() as $attribute) { |
|
183 | 16 | $sort->attributes[$attribute] = [ |
|
184 | 16 | 'asc' => [$attribute => SORT_ASC], |
|
185 | 16 | 'desc' => [$attribute => SORT_DESC], |
|
186 | 16 | ]; |
|
187 | } |
||
188 | } |
||
189 | 20 | if ($sort->modelClass === null) { |
|
190 | 20 | $sort->modelClass = $modelClass; |
|
0 ignored issues
–
show
It seems like
$modelClass of type yii\base\Model is incompatible with the declared type null|string of property $modelClass .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. ![]() |
|||
191 | } |
||
192 | } |
||
193 | } |
||
194 | |||
195 | 1 | public function __clone() |
|
196 | { |
||
197 | 1 | if (is_object($this->query)) { |
|
198 | 1 | $this->query = clone $this->query; |
|
199 | } |
||
200 | |||
201 | 1 | parent::__clone(); |
|
202 | } |
||
203 | } |
||
204 |