Completed
Push — master ( 2e8b6e...807945 )
by Dmitry
36:24
created

ActiveQueryTrait::asArray()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
ccs 3
cts 3
cp 1
cc 1
eloc 3
nc 1
nop 1
crap 1
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://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 125
    public function asArray($value = true)
40
    {
41 125
        $this->asArray = $value;
42 125
        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 102
    public function with()
83
    {
84 102
        $with = func_get_args();
85 102
        if (isset($with[0]) && is_array($with[0])) {
86
            // the parameter is given as an array
87 51
            $with = $with[0];
88
        }
89
90 102
        if (empty($this->with)) {
91 102
            $this->with = $with;
92 9
        } elseif (!empty($with)) {
93 9
            foreach ($with as $name => $value) {
94 9
                if (is_int($name)) {
95
                    // repeating relation is fine as normalizeRelations() handle it well
96 6
                    $this->with[] = $value;
97
                } else {
98 9
                    $this->with[$name] = $value;
99
                }
100
            }
101
        }
102
103 102
        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 316
    protected function createModels($rows)
113
    {
114 316
        if ($this->asArray) {
115 316
            return $rows;
116 65
        } else {
117 62
            $models = [];
118
            /* @var $class ActiveRecord */
119 3
            $class = $this->modelClass;
120 3
            foreach ($rows as $row) {
121 3
                $model = $class::instantiate($row);
122
                $modelClass = get_class($model);
123 3
                $modelClass::populateRecord($model, $row);
124
                $models[] = $model;
125 3
            }
126
            return $models;
127
        }
128
    }
129 306
130 306
    /**
131 303
     * Finds records corresponding to one or multiple relations and populates them into the primary models.
132 303
     * @param array $with a list of relations that this query should be performed with. Please
133 303
     * refer to [[with()]] for details about specifying this parameter.
134 303
     * @param array|ActiveRecord[] $models the primary models (can be either AR instances or arrays)
135 303
     */
136
    public function findWith($with, &$models)
137
    {
138 12
        $primaryModel = reset($models);
139 12
        if (!$primaryModel instanceof ActiveRecordInterface) {
140 12
            /* @var $modelClass ActiveRecordInterface */
141 12
            $modelClass = $this->modelClass;
142 12
            $primaryModel = $modelClass::instance();
143 12
        }
144
        $relations = $this->normalizeRelations($primaryModel, $with);
0 ignored issues
show
Compatibility introduced by
$primaryModel of type object<yii\db\ActiveRecordInterface> is not a sub-type of object<yii\db\ActiveRecord>. It seems like you assume a concrete implementation of the interface yii\db\ActiveRecordInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
145 3
        /* @var $relation ActiveQuery */
146
        foreach ($relations as $name => $relation) {
147 12
            if ($relation->asArray === null) {
0 ignored issues
show
Bug introduced by
Accessing asArray on the interface yii\db\ActiveQueryInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
148
                // inherit asArray from primary query
149
                $relation->asArray($this->asArray);
150
            }
151
            $relation->populateRelation($name, $models);
152 309
        }
153
    }
154
155
    /**
156
     * @param ActiveRecord $model
157
     * @param array $with
158
     * @return ActiveQueryInterface[]
159
     */
160
    private function normalizeRelations($model, $with)
161 81
    {
162
        $relations = [];
163 81
        foreach ($with as $name => $callback) {
164 81
            if (is_int($name)) {
165
                $name = $callback;
166 9
                $callback = null;
167 9
            }
168
            if (($pos = strpos($name, '.')) !== false) {
169 81
                // with sub-relations
170
                $childName = substr($name, $pos + 1);
171 81
                $name = substr($name, 0, $pos);
172 81
            } else {
173
                $childName = null;
174 81
            }
175
176 81
            if (!isset($relations[$name])) {
177
                $relation = $model->getRelation($name);
178 81
                $relation->primaryModel = null;
0 ignored issues
show
Bug introduced by
Accessing primaryModel on the interface yii\db\ActiveQueryInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
179
                $relations[$name] = $relation;
180
            } else {
181
                $relation = $relations[$name];
182
            }
183
184
            if (isset($childName)) {
185 81
                $relation->with[$childName] = $callback;
186
            } elseif ($callback !== null) {
187 81
                call_user_func($callback, $relation);
188 81
            }
189 81
        }
190 81
191 81
        return $relations;
192
    }
193
}
194