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\db; |
||||||
9 | |||||||
10 | /** |
||||||
11 | * ActiveQueryTrait implements the common methods and properties for active record query classes. |
||||||
12 | * |
||||||
13 | * @author Qiang Xue <[email protected]> |
||||||
14 | * @author Carsten Brandt <[email protected]> |
||||||
15 | * @since 2.0 |
||||||
16 | */ |
||||||
17 | trait ActiveQueryTrait |
||||||
18 | { |
||||||
19 | /** |
||||||
20 | * @var string the name of the ActiveRecord class. |
||||||
21 | */ |
||||||
22 | public $modelClass; |
||||||
23 | /** |
||||||
24 | * @var array a list of relations that this query should be performed with |
||||||
25 | */ |
||||||
26 | public $with; |
||||||
27 | /** |
||||||
28 | * @var bool whether to return each record as an array. If false (default), an object |
||||||
29 | * of [[modelClass]] will be created to represent each record. |
||||||
30 | */ |
||||||
31 | public $asArray; |
||||||
32 | |||||||
33 | |||||||
34 | /** |
||||||
35 | * Sets the [[asArray]] property. |
||||||
36 | * @param bool $value whether to return the query results in terms of arrays instead of Active Records. |
||||||
37 | * @return $this the query object itself |
||||||
38 | */ |
||||||
39 | 173 | public function asArray($value = true) |
|||||
40 | { |
||||||
41 | 173 | $this->asArray = $value; |
|||||
42 | 173 | return $this; |
|||||
43 | } |
||||||
44 | |||||||
45 | /** |
||||||
46 | * Specifies the relations with which this query should be performed. |
||||||
47 | * |
||||||
48 | * The parameters to this method can be either one or multiple strings, or a single array |
||||||
49 | * of relation names and the optional callbacks to customize the relations. |
||||||
50 | * |
||||||
51 | * A relation name can refer to a relation defined in [[modelClass]] |
||||||
52 | * or a sub-relation that stands for a relation of a related record. |
||||||
53 | * For example, `orders.address` means the `address` relation defined |
||||||
54 | * in the model class corresponding to the `orders` relation. |
||||||
55 | * |
||||||
56 | * The following are some usage examples: |
||||||
57 | * |
||||||
58 | * ```php |
||||||
59 | * // find customers together with their orders and country |
||||||
60 | * Customer::find()->with('orders', 'country')->all(); |
||||||
61 | * // find customers together with their orders and the orders' shipping address |
||||||
62 | * Customer::find()->with('orders.address')->all(); |
||||||
63 | * // find customers together with their country and orders of status 1 |
||||||
64 | * Customer::find()->with([ |
||||||
65 | * 'orders' => function (\yii\db\ActiveQuery $query) { |
||||||
66 | * $query->andWhere('status = 1'); |
||||||
67 | * }, |
||||||
68 | * 'country', |
||||||
69 | * ])->all(); |
||||||
70 | * ``` |
||||||
71 | * |
||||||
72 | * You can call `with()` multiple times. Each call will add relations to the existing ones. |
||||||
73 | * For example, the following two statements are equivalent: |
||||||
74 | * |
||||||
75 | * ```php |
||||||
76 | * Customer::find()->with('orders', 'country')->all(); |
||||||
77 | * Customer::find()->with('orders')->with('country')->all(); |
||||||
78 | * ``` |
||||||
79 | * |
||||||
80 | * @return $this the query object itself |
||||||
81 | */ |
||||||
82 | 153 | public function with() |
|||||
83 | { |
||||||
84 | 153 | $with = func_get_args(); |
|||||
85 | 153 | if (isset($with[0]) && is_array($with[0])) { |
|||||
86 | // the parameter is given as an array |
||||||
87 | 93 | $with = $with[0]; |
|||||
88 | } |
||||||
89 | |||||||
90 | 153 | if (empty($this->with)) { |
|||||
91 | 153 | $this->with = $with; |
|||||
92 | 36 | } elseif (!empty($with)) { |
|||||
93 | 36 | foreach ($with as $name => $value) { |
|||||
94 | 36 | if (is_int($name)) { |
|||||
95 | // repeating relation is fine as normalizeRelations() handle it well |
||||||
96 | 12 | $this->with[] = $value; |
|||||
97 | } else { |
||||||
98 | 24 | $this->with[$name] = $value; |
|||||
99 | } |
||||||
100 | } |
||||||
101 | } |
||||||
102 | |||||||
103 | 153 | return $this; |
|||||
104 | } |
||||||
105 | |||||||
106 | /** |
||||||
107 | * Converts found rows into model instances. |
||||||
108 | * @param array $rows |
||||||
109 | * @return array|ActiveRecord[] |
||||||
110 | * @since 2.0.11 |
||||||
111 | */ |
||||||
112 | 406 | protected function createModels($rows) |
|||||
113 | { |
||||||
114 | 406 | if ($this->asArray) { |
|||||
115 | 71 | return $rows; |
|||||
116 | } else { |
||||||
117 | 396 | $models = []; |
|||||
118 | /* @var $class ActiveRecord */ |
||||||
119 | 396 | $class = $this->modelClass; |
|||||
120 | 396 | foreach ($rows as $row) { |
|||||
121 | 396 | $model = $class::instantiate($row); |
|||||
122 | 396 | $modelClass = get_class($model); |
|||||
123 | 396 | $modelClass::populateRecord($model, $row); |
|||||
124 | 396 | $models[] = $model; |
|||||
125 | } |
||||||
126 | 396 | return $models; |
|||||
127 | } |
||||||
128 | } |
||||||
129 | |||||||
130 | /** |
||||||
131 | * Finds records corresponding to one or multiple relations and populates them into the primary models. |
||||||
132 | * @param array $with a list of relations that this query should be performed with. Please |
||||||
133 | * refer to [[with()]] for details about specifying this parameter. |
||||||
134 | * @param array|ActiveRecord[] $models the primary models (can be either AR instances or arrays) |
||||||
135 | */ |
||||||
136 | 132 | public function findWith($with, &$models) |
|||||
137 | { |
||||||
138 | 132 | if (empty($models)) { |
|||||
139 | return; |
||||||
140 | } |
||||||
141 | |||||||
142 | 132 | $primaryModel = reset($models); |
|||||
143 | 132 | if (!$primaryModel instanceof ActiveRecordInterface) { |
|||||
144 | /* @var $modelClass ActiveRecordInterface */ |
||||||
145 | 15 | $modelClass = $this->modelClass; |
|||||
146 | 15 | $primaryModel = $modelClass::instance(); |
|||||
147 | } |
||||||
148 | 132 | $relations = $this->normalizeRelations($primaryModel, $with); |
|||||
149 | /* @var $relation ActiveQuery */ |
||||||
150 | 132 | foreach ($relations as $name => $relation) { |
|||||
151 | 132 | if ($relation->asArray === null) { |
|||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||||
152 | // inherit asArray from primary query |
||||||
153 | 132 | $relation->asArray($this->asArray); |
|||||
154 | } |
||||||
155 | 132 | $relation->populateRelation($name, $models); |
|||||
0 ignored issues
–
show
The method
populateRelation() does not exist on yii\db\ActiveQueryInterface . Since it exists in all sub-types, consider adding an abstract or default implementation to yii\db\ActiveQueryInterface .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
156 | } |
||||||
157 | } |
||||||
158 | |||||||
159 | /** |
||||||
160 | * @param ActiveRecord $model |
||||||
161 | * @param array $with |
||||||
162 | * @return ActiveQueryInterface[] |
||||||
163 | */ |
||||||
164 | 132 | private function normalizeRelations($model, $with) |
|||||
165 | { |
||||||
166 | 132 | $relations = []; |
|||||
167 | 132 | foreach ($with as $name => $callback) { |
|||||
168 | 132 | if (is_int($name)) { |
|||||
169 | 129 | $name = $callback; |
|||||
170 | 129 | $callback = null; |
|||||
171 | } |
||||||
172 | 132 | if (($pos = strpos($name, '.')) !== false) { |
|||||
173 | // with sub-relations |
||||||
174 | 21 | $childName = substr($name, $pos + 1); |
|||||
175 | 21 | $name = substr($name, 0, $pos); |
|||||
176 | } else { |
||||||
177 | 132 | $childName = null; |
|||||
178 | } |
||||||
179 | |||||||
180 | 132 | if (!isset($relations[$name])) { |
|||||
181 | 132 | $relation = $model->getRelation($name); |
|||||
182 | 132 | $relation->primaryModel = null; |
|||||
0 ignored issues
–
show
|
|||||||
183 | 132 | $relations[$name] = $relation; |
|||||
184 | } else { |
||||||
185 | 33 | $relation = $relations[$name]; |
|||||
186 | } |
||||||
187 | |||||||
188 | 132 | if (isset($childName)) { |
|||||
189 | 21 | $relation->with[$childName] = $callback; |
|||||
190 | 132 | } elseif ($callback !== null) { |
|||||
191 | 42 | call_user_func($callback, $relation); |
|||||
192 | } |
||||||
193 | } |
||||||
194 | |||||||
195 | 132 | return $relations; |
|||||
196 | } |
||||||
197 | } |
||||||
198 |