Issues (902)

framework/db/ActiveQueryTrait.php (3 issues)

Labels
Severity
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
Accessing asArray on the interface yii\db\ActiveQueryInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
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 ignore-call  annotation

155
            $relation->/** @scrutinizer ignore-call */ 
156
                       populateRelation($name, $models);
Loading history...
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
Accessing primaryModel on the interface yii\db\ActiveQueryInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
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