Completed
Push — 2.1 ( 6b99e4...fa71f8 )
by Dmitry
15:50
created

BaseActiveRecord::unlinkAll()   C

Complexity

Conditions 16
Paths 41

Size

Total Lines 73
Code Lines 50

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 40
CRAP Score 16.1922

Importance

Changes 0
Metric Value
dl 0
loc 73
ccs 40
cts 44
cp 0.9091
rs 5.4156
c 0
b 0
f 0
cc 16
eloc 50
nc 41
nop 2
crap 16.1922

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
use Yii;
11
use yii\base\InvalidArgumentException;
12
use yii\base\InvalidCallException;
13
use yii\base\InvalidConfigException;
14
use yii\base\Model;
15
use yii\base\ModelEvent;
16
use yii\base\NotSupportedException;
17
use yii\base\UnknownMethodException;
18
use yii\helpers\ArrayHelper;
19
20
/**
21
 * ActiveRecord is the base class for classes representing relational data in terms of objects.
22
 *
23
 * See [[\yii\db\ActiveRecord]] for a concrete implementation.
24
 *
25
 * @property array $dirtyAttributes The changed attribute values (name-value pairs). This property is
26
 * read-only.
27
 * @property bool $isNewRecord Whether the record is new and should be inserted when calling [[save()]].
28
 * @property array $oldAttributes The old attribute values (name-value pairs). Note that the type of this
29
 * property differs in getter and setter. See [[getOldAttributes()]] and [[setOldAttributes()]] for details.
30
 * @property mixed $oldPrimaryKey The old primary key value. An array (column name => column value) is
31
 * returned if the primary key is composite. A string is returned otherwise (null will be returned if the key
32
 * value is null). This property is read-only.
33
 * @property mixed $primaryKey The primary key value. An array (column name => column value) is returned if
34
 * the primary key is composite. A string is returned otherwise (null will be returned if the key value is null).
35
 * This property is read-only.
36
 * @property array $relatedRecords An array of related records indexed by relation names. This property is
37
 * read-only.
38
 *
39
 * @author Qiang Xue <[email protected]>
40
 * @author Carsten Brandt <[email protected]>
41
 * @since 2.0
42
 */
43
abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
44
{
45
    /**
46
     * @event Event an event that is triggered when the record is initialized via [[init()]].
47
     */
48
    const EVENT_INIT = 'init';
49
    /**
50
     * @event Event an event that is triggered after the record is created and populated with query result.
51
     */
52
    const EVENT_AFTER_FIND = 'afterFind';
53
    /**
54
     * @event ModelEvent an event that is triggered before inserting a record.
55
     * You may set [[ModelEvent::isValid]] to be `false` to stop the insertion.
56
     */
57
    const EVENT_BEFORE_INSERT = 'beforeInsert';
58
    /**
59
     * @event AfterSaveEvent an event that is triggered after a record is inserted.
60
     */
61
    const EVENT_AFTER_INSERT = 'afterInsert';
62
    /**
63
     * @event ModelEvent an event that is triggered before updating a record.
64
     * You may set [[ModelEvent::isValid]] to be `false` to stop the update.
65
     */
66
    const EVENT_BEFORE_UPDATE = 'beforeUpdate';
67
    /**
68
     * @event AfterSaveEvent an event that is triggered after a record is updated.
69
     */
70
    const EVENT_AFTER_UPDATE = 'afterUpdate';
71
    /**
72
     * @event ModelEvent an event that is triggered before deleting a record.
73
     * You may set [[ModelEvent::isValid]] to be `false` to stop the deletion.
74
     */
75
    const EVENT_BEFORE_DELETE = 'beforeDelete';
76
    /**
77
     * @event Event an event that is triggered after a record is deleted.
78
     */
79
    const EVENT_AFTER_DELETE = 'afterDelete';
80
    /**
81
     * @event Event an event that is triggered after a record is refreshed.
82
     * @since 2.0.8
83
     */
84
    const EVENT_AFTER_REFRESH = 'afterRefresh';
85
86
    /**
87
     * @var array attribute values indexed by attribute names
88
     */
89
    private $_attributes = [];
90
    /**
91
     * @var array|null old attribute values indexed by attribute names.
92
     * This is `null` if the record [[isNewRecord|is new]].
93
     */
94
    private $_oldAttributes;
95
    /**
96
     * @var array related models indexed by the relation names
97
     */
98
    private $_related = [];
99
    /**
100
     * @var array relation names indexed by their link attributes
101
     */
102
    private $_relationsDependencies = [];
103
104
105
    /**
106
     * {@inheritdoc}
107
     * @return static|null ActiveRecord instance matching the condition, or `null` if nothing matches.
108
     */
109 190
    public static function findOne($condition)
110
    {
111 190
        return static::findByCondition($condition)->one();
0 ignored issues
show
Bug Compatibility introduced by
The expression static::findByCondition($condition)->one(); of type yii\db\ActiveRecordInterface|array|null adds the type array to the return on line 111 which is incompatible with the return type declared by the interface yii\db\ActiveRecordInterface::findOne of type yii\db\ActiveRecordInterface.
Loading history...
112
    }
113
114
    /**
115
     * {@inheritdoc}
116
     * @return static[] an array of ActiveRecord instances, or an empty array if nothing matches.
117
     */
118
    public static function findAll($condition)
119
    {
120
        return static::findByCondition($condition)->all();
121
    }
122
123
    /**
124
     * Finds ActiveRecord instance(s) by the given condition.
125
     * This method is internally called by [[findOne()]] and [[findAll()]].
126
     * @param mixed $condition please refer to [[findOne()]] for the explanation of this parameter
127
     * @return ActiveQueryInterface the newly created [[ActiveQueryInterface|ActiveQuery]] instance.
128
     * @throws InvalidConfigException if there is no primary key defined
129
     * @internal
130
     */
131
    protected static function findByCondition($condition)
132
    {
133
        $query = static::find();
134
135
        if (!ArrayHelper::isAssociative($condition)) {
136
            // query by primary key
137
            $primaryKey = static::primaryKey();
138
            if (isset($primaryKey[0])) {
139
                // if condition is scalar, search for a single primary key, if it is array, search for multiple primary key values
140
                $condition = [$primaryKey[0] => is_array($condition) ? array_values($condition) : $condition];
141
            } else {
142
                throw new InvalidConfigException('"' . get_called_class() . '" must have a primary key.');
143
            }
144
        }
145
146
        return $query->andWhere($condition);
147
    }
148
149
    /**
150
     * Updates the whole table using the provided attribute values and conditions.
151
     *
152
     * For example, to change the status to be 1 for all customers whose status is 2:
153
     *
154
     * ```php
155
     * Customer::updateAll(['status' => 1], 'status = 2');
156
     * ```
157
     *
158
     * @param array $attributes attribute values (name-value pairs) to be saved into the table
159
     * @param string|array $condition the conditions that will be put in the WHERE part of the UPDATE SQL.
160
     * Please refer to [[Query::where()]] on how to specify this parameter.
161
     * @return int the number of rows updated
162
     * @throws NotSupportedException if not overridden
163
     */
164
    public static function updateAll($attributes, $condition = '')
165
    {
166
        throw new NotSupportedException(__METHOD__ . ' is not supported.');
167
    }
168
169
    /**
170
     * Updates the whole table using the provided counter changes and conditions.
171
     *
172
     * For example, to increment all customers' age by 1,
173
     *
174
     * ```php
175
     * Customer::updateAllCounters(['age' => 1]);
176
     * ```
177
     *
178
     * @param array $counters the counters to be updated (attribute name => increment value).
179
     * Use negative values if you want to decrement the counters.
180
     * @param string|array $condition the conditions that will be put in the WHERE part of the UPDATE SQL.
181
     * Please refer to [[Query::where()]] on how to specify this parameter.
182
     * @return int the number of rows updated
183
     * @throws NotSupportedException if not overrided
184
     */
185
    public static function updateAllCounters($counters, $condition = '')
186
    {
187
        throw new NotSupportedException(__METHOD__ . ' is not supported.');
188
    }
189
190
    /**
191
     * Deletes rows in the table using the provided conditions.
192
     * WARNING: If you do not specify any condition, this method will delete ALL rows in the table.
193
     *
194
     * For example, to delete all customers whose status is 3:
195
     *
196
     * ```php
197
     * Customer::deleteAll('status = 3');
198
     * ```
199
     *
200
     * @param string|array $condition the conditions that will be put in the WHERE part of the DELETE SQL.
201
     * Please refer to [[Query::where()]] on how to specify this parameter.
202
     * @return int the number of rows deleted
203
     * @throws NotSupportedException if not overridden.
204
     */
205
    public static function deleteAll($condition = null)
206
    {
207
        throw new NotSupportedException(__METHOD__ . ' is not supported.');
208
    }
209
210
    /**
211
     * Returns the name of the column that stores the lock version for implementing optimistic locking.
212
     *
213
     * Optimistic locking allows multiple users to access the same record for edits and avoids
214
     * potential conflicts. In case when a user attempts to save the record upon some staled data
215
     * (because another user has modified the data), a [[StaleObjectException]] exception will be thrown,
216
     * and the update or deletion is skipped.
217
     *
218
     * Optimistic locking is only supported by [[update()]] and [[delete()]].
219
     *
220
     * To use Optimistic locking:
221
     *
222
     * 1. Create a column to store the version number of each row. The column type should be `BIGINT DEFAULT 0`.
223
     *    Override this method to return the name of this column.
224
     * 2. Ensure the version value is submitted and loaded to your model before any update or delete.
225
     *    Or add [[\yii\behaviors\OptimisticLockBehavior|OptimisticLockBehavior]] to your model 
226
     *    class in order to automate the process.
227
     * 3. In the Web form that collects the user input, add a hidden field that stores
228
     *    the lock version of the recording being updated.
229
     * 4. In the controller action that does the data updating, try to catch the [[StaleObjectException]]
230
     *    and implement necessary business logic (e.g. merging the changes, prompting stated data)
231
     *    to resolve the conflict.
232
     *
233
     * @return string the column name that stores the lock version of a table row.
234
     * If `null` is returned (default implemented), optimistic locking will not be supported.
235
     */
236 36
    public function optimisticLock()
237
    {
238 36
        return null;
239
    }
240
241
    /**
242
     * {@inheritdoc}
243
     */
244 3
    public function canGetProperty($name, $checkVars = true, $checkBehaviors = true)
245
    {
246 3
        if (parent::canGetProperty($name, $checkVars, $checkBehaviors)) {
247 3
            return true;
248
        }
249
250
        try {
251 3
            return $this->hasAttribute($name);
252
        } catch (\Exception $e) {
253
            // `hasAttribute()` may fail on base/abstract classes in case automatic attribute list fetching used
254
            return false;
255
        }
256
    }
257
258
    /**
259
     * {@inheritdoc}
260
     */
261 9
    public function canSetProperty($name, $checkVars = true, $checkBehaviors = true)
262
    {
263 9
        if (parent::canSetProperty($name, $checkVars, $checkBehaviors)) {
264 6
            return true;
265
        }
266
267
        try {
268 3
            return $this->hasAttribute($name);
269
        } catch (\Exception $e) {
270
            // `hasAttribute()` may fail on base/abstract classes in case automatic attribute list fetching used
271
            return false;
272
        }
273
    }
274
275
    /**
276
     * PHP getter magic method.
277
     * This method is overridden so that attributes and related objects can be accessed like properties.
278
     *
279
     * @param string $name property name
280
     * @throws \yii\base\InvalidArgumentException if relation name is wrong
281
     * @return mixed property value
282
     * @see getAttribute()
283
     */
284 396
    public function __get($name)
285
    {
286 396
        if (isset($this->_attributes[$name]) || array_key_exists($name, $this->_attributes)) {
287 364
            return $this->_attributes[$name];
288
        }
289
290 206
        if ($this->hasAttribute($name)) {
291 55
            return null;
292
        }
293
294 172
        if (isset($this->_related[$name]) || array_key_exists($name, $this->_related)) {
295 93
            return $this->_related[$name];
296
        }
297 124
        $value = parent::__get($name);
298 118
        if ($value instanceof ActiveQueryInterface) {
299 73
            $this->setRelationDependencies($name, $value);
300 73
            return $this->_related[$name] = $value->findFor($name, $this);
301
        }
302
303 54
        return $value;
304
    }
305
306
    /**
307
     * PHP setter magic method.
308
     * This method is overridden so that AR attributes can be accessed like properties.
309
     * @param string $name property name
310
     * @param mixed $value property value
311
     */
312 194
    public function __set($name, $value)
313
    {
314 194
        if ($this->hasAttribute($name)) {
315
            if (
316 194
                !empty($this->_relationsDependencies[$name])
317 194
                && (!array_key_exists($name, $this->_attributes) || $this->_attributes[$name] !== $value)
318
            ) {
319 15
                $this->resetDependentRelations($name);
320
            }
321 194
            $this->_attributes[$name] = $value;
322
        } else {
323 5
            parent::__set($name, $value);
324
        }
325 194
    }
326
327
    /**
328
     * Checks if a property value is null.
329
     * This method overrides the parent implementation by checking if the named attribute is `null` or not.
330
     * @param string $name the property name or the event name
331
     * @return bool whether the property value is null
332
     */
333 65
    public function __isset($name)
334
    {
335
        try {
336 65
            return $this->__get($name) !== null;
337 6
        } catch (\Throwable $t) {
338 6
            return false;
339
        } catch (\Exception $e) {
340
            return false;
341
        }
342
    }
343
344
    /**
345
     * Sets a component property to be null.
346
     * This method overrides the parent implementation by clearing
347
     * the specified attribute value.
348
     * @param string $name the property name or the event name
349
     */
350 15
    public function __unset($name)
351
    {
352 15
        if ($this->hasAttribute($name)) {
353 9
            unset($this->_attributes[$name]);
354 9
            if (!empty($this->_relationsDependencies[$name])) {
355 9
                $this->resetDependentRelations($name);
356
            }
357 6
        } elseif (array_key_exists($name, $this->_related)) {
358 6
            unset($this->_related[$name]);
359
        } elseif ($this->getRelation($name, false) === null) {
360
            parent::__unset($name);
361
        }
362 15
    }
363
364
    /**
365
     * Declares a `has-one` relation.
366
     * The declaration is returned in terms of a relational [[ActiveQuery]] instance
367
     * through which the related record can be queried and retrieved back.
368
     *
369
     * A `has-one` relation means that there is at most one related record matching
370
     * the criteria set by this relation, e.g., a customer has one country.
371
     *
372
     * For example, to declare the `country` relation for `Customer` class, we can write
373
     * the following code in the `Customer` class:
374
     *
375
     * ```php
376
     * public function getCountry()
377
     * {
378
     *     return $this->hasOne(Country::class, ['id' => 'country_id']);
379
     * }
380
     * ```
381
     *
382
     * Note that in the above, the 'id' key in the `$link` parameter refers to an attribute name
383
     * in the related class `Country`, while the 'country_id' value refers to an attribute name
384
     * in the current AR class.
385
     *
386
     * Call methods declared in [[ActiveQuery]] to further customize the relation.
387
     *
388
     * @param string $class the class name of the related record
389
     * @param array $link the primary-foreign key constraint. The keys of the array refer to
390
     * the attributes of the record associated with the `$class` model, while the values of the
391
     * array refer to the corresponding attributes in **this** AR class.
392
     * @return ActiveQueryInterface the relational query object.
393
     */
394 73
    public function hasOne($class, $link)
395
    {
396 73
        return $this->createRelationQuery($class, $link, false);
397
    }
398
399
    /**
400
     * Declares a `has-many` relation.
401
     * The declaration is returned in terms of a relational [[ActiveQuery]] instance
402
     * through which the related record can be queried and retrieved back.
403
     *
404
     * A `has-many` relation means that there are multiple related records matching
405
     * the criteria set by this relation, e.g., a customer has many orders.
406
     *
407
     * For example, to declare the `orders` relation for `Customer` class, we can write
408
     * the following code in the `Customer` class:
409
     *
410
     * ```php
411
     * public function getOrders()
412
     * {
413
     *     return $this->hasMany(Order::class, ['customer_id' => 'id']);
414
     * }
415
     * ```
416
     *
417
     * Note that in the above, the 'customer_id' key in the `$link` parameter refers to
418
     * an attribute name in the related class `Order`, while the 'id' value refers to
419
     * an attribute name in the current AR class.
420
     *
421
     * Call methods declared in [[ActiveQuery]] to further customize the relation.
422
     *
423
     * @param string $class the class name of the related record
424
     * @param array $link the primary-foreign key constraint. The keys of the array refer to
425
     * the attributes of the record associated with the `$class` model, while the values of the
426
     * array refer to the corresponding attributes in **this** AR class.
427
     * @return ActiveQueryInterface the relational query object.
428
     */
429 150
    public function hasMany($class, $link)
430
    {
431 150
        return $this->createRelationQuery($class, $link, true);
432
    }
433
434
    /**
435
     * Creates a query instance for `has-one` or `has-many` relation.
436
     * @param string $class the class name of the related record.
437
     * @param array $link the primary-foreign key constraint.
438
     * @param bool $multiple whether this query represents a relation to more than one record.
439
     * @return ActiveQueryInterface the relational query object.
440
     * @since 2.0.12
441
     * @see hasOne()
442
     * @see hasMany()
443
     */
444 181
    protected function createRelationQuery($class, $link, $multiple)
445
    {
446
        /* @var $class ActiveRecordInterface */
447
        /* @var $query ActiveQuery */
448 181
        $query = $class::find();
449 181
        $query->primaryModel = $this;
0 ignored issues
show
Documentation Bug introduced by
$this is of type object<yii\db\BaseActiveRecord>, but the property $primaryModel was declared to be of type object<yii\db\ActiveRecord>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
450 181
        $query->link = $link;
451 181
        $query->multiple = $multiple;
452 181
        return $query;
453
    }
454
455
    /**
456
     * Populates the named relation with the related records.
457
     * Note that this method does not check if the relation exists or not.
458
     * @param string $name the relation name, e.g. `orders` for a relation defined via `getOrders()` method (case-sensitive).
459
     * @param ActiveRecordInterface|array|null $records the related records to be populated into the relation.
460
     * @see getRelation()
461
     */
462 123
    public function populateRelation($name, $records)
463
    {
464 123
        foreach ($this->_relationsDependencies as &$relationNames) {
465 27
            unset($relationNames[$name]);
466
        }
467
468 123
        $this->_related[$name] = $records;
469 123
    }
470
471
    /**
472
     * Check whether the named relation has been populated with records.
473
     * @param string $name the relation name, e.g. `orders` for a relation defined via `getOrders()` method (case-sensitive).
474
     * @return bool whether relation has been populated with records.
475
     * @see getRelation()
476
     */
477 48
    public function isRelationPopulated($name)
478
    {
479 48
        return array_key_exists($name, $this->_related);
480
    }
481
482
    /**
483
     * Returns all populated related records.
484
     * @return array an array of related records indexed by relation names.
485
     * @see getRelation()
486
     */
487 6
    public function getRelatedRecords()
488
    {
489 6
        return $this->_related;
490
    }
491
492
    /**
493
     * Returns a value indicating whether the model has an attribute with the specified name.
494
     * @param string $name the name of the attribute
495
     * @return bool whether the model has an attribute with the specified name.
496
     */
497 308
    public function hasAttribute($name)
498
    {
499 308
        return isset($this->_attributes[$name]) || in_array($name, $this->attributes(), true);
500
    }
501
502
    /**
503
     * Returns the named attribute value.
504
     * If this record is the result of a query and the attribute is not loaded,
505
     * `null` will be returned.
506
     * @param string $name the attribute name
507
     * @return mixed the attribute value. `null` if the attribute is not set or does not exist.
508
     * @see hasAttribute()
509
     */
510
    public function getAttribute($name)
511
    {
512
        return isset($this->_attributes[$name]) ? $this->_attributes[$name] : null;
513
    }
514
515
    /**
516
     * Sets the named attribute value.
517
     * @param string $name the attribute name
518
     * @param mixed $value the attribute value.
519
     * @throws InvalidArgumentException if the named attribute does not exist.
520
     * @see hasAttribute()
521
     */
522 89
    public function setAttribute($name, $value)
523
    {
524 89
        if ($this->hasAttribute($name)) {
525
            if (
526 89
                !empty($this->_relationsDependencies[$name])
527 89
                && (!array_key_exists($name, $this->_attributes) || $this->_attributes[$name] !== $value)
528
            ) {
529 6
                $this->resetDependentRelations($name);
530
            }
531 89
            $this->_attributes[$name] = $value;
532
        } else {
533
            throw new InvalidArgumentException(get_class($this) . ' has no attribute named "' . $name . '".');
534
        }
535 89
    }
536
537
    /**
538
     * Returns the old attribute values.
539
     * @return array the old attribute values (name-value pairs)
540
     */
541
    public function getOldAttributes()
542
    {
543
        return $this->_oldAttributes === null ? [] : $this->_oldAttributes;
544
    }
545
546
    /**
547
     * Sets the old attribute values.
548
     * All existing old attribute values will be discarded.
549
     * @param array|null $values old attribute values to be set.
550
     * If set to `null` this record is considered to be [[isNewRecord|new]].
551
     */
552 100
    public function setOldAttributes($values)
553
    {
554 100
        $this->_oldAttributes = $values;
555 100
    }
556
557
    /**
558
     * Returns the old value of the named attribute.
559
     * If this record is the result of a query and the attribute is not loaded,
560
     * `null` will be returned.
561
     * @param string $name the attribute name
562
     * @return mixed the old attribute value. `null` if the attribute is not loaded before
563
     * or does not exist.
564
     * @see hasAttribute()
565
     */
566
    public function getOldAttribute($name)
567
    {
568
        return isset($this->_oldAttributes[$name]) ? $this->_oldAttributes[$name] : null;
569
    }
570
571
    /**
572
     * Sets the old value of the named attribute.
573
     * @param string $name the attribute name
574
     * @param mixed $value the old attribute value.
575
     * @throws InvalidArgumentException if the named attribute does not exist.
576
     * @see hasAttribute()
577
     */
578
    public function setOldAttribute($name, $value)
579
    {
580
        if (isset($this->_oldAttributes[$name]) || $this->hasAttribute($name)) {
581
            $this->_oldAttributes[$name] = $value;
582
        } else {
583
            throw new InvalidArgumentException(get_class($this) . ' has no attribute named "' . $name . '".');
584
        }
585
    }
586
587
    /**
588
     * Marks an attribute dirty.
589
     * This method may be called to force updating a record when calling [[update()]],
590
     * even if there is no change being made to the record.
591
     * @param string $name the attribute name
592
     */
593 6
    public function markAttributeDirty($name)
594
    {
595 6
        unset($this->_oldAttributes[$name]);
596 6
    }
597
598
    /**
599
     * Returns a value indicating whether the named attribute has been changed.
600
     * @param string $name the name of the attribute.
601
     * @param bool $identical whether the comparison of new and old value is made for
602
     * identical values using `===`, defaults to `true`. Otherwise `==` is used for comparison.
603
     * This parameter is available since version 2.0.4.
604
     * @return bool whether the attribute has been changed
605
     */
606 2
    public function isAttributeChanged($name, $identical = true)
607
    {
608 2
        if (isset($this->_attributes[$name], $this->_oldAttributes[$name])) {
609 1
            if ($identical) {
610 1
                return $this->_attributes[$name] !== $this->_oldAttributes[$name];
611
            }
612
613
            return $this->_attributes[$name] != $this->_oldAttributes[$name];
614
        }
615
616 1
        return isset($this->_attributes[$name]) || isset($this->_oldAttributes[$name]);
617
    }
618
619
    /**
620
     * Returns the attribute values that have been modified since they are loaded or saved most recently.
621
     *
622
     * The comparison of new and old values is made for identical values using `===`.
623
     *
624
     * @param string[]|null $names the names of the attributes whose values may be returned if they are
625
     * changed recently. If null, [[attributes()]] will be used.
626
     * @return array the changed attribute values (name-value pairs)
627
     */
628 110
    public function getDirtyAttributes($names = null)
629
    {
630 110
        if ($names === null) {
631 107
            $names = $this->attributes();
632
        }
633 110
        $names = array_flip($names);
634 110
        $attributes = [];
635 110
        if ($this->_oldAttributes === null) {
636 97
            foreach ($this->_attributes as $name => $value) {
637 93
                if (isset($names[$name])) {
638 97
                    $attributes[$name] = $value;
639
                }
640
            }
641
        } else {
642 42
            foreach ($this->_attributes as $name => $value) {
643 42
                if (isset($names[$name]) && (!array_key_exists($name, $this->_oldAttributes) || $value !== $this->_oldAttributes[$name])) {
644 42
                    $attributes[$name] = $value;
645
                }
646
            }
647
        }
648
649 110
        return $attributes;
650
    }
651
652
    /**
653
     * Saves the current record.
654
     *
655
     * This method will call [[insert()]] when [[isNewRecord]] is `true`, or [[update()]]
656
     * when [[isNewRecord]] is `false`.
657
     *
658
     * For example, to save a customer record:
659
     *
660
     * ```php
661
     * $customer = new Customer; // or $customer = Customer::findOne($id);
662
     * $customer->name = $name;
663
     * $customer->email = $email;
664
     * $customer->save();
665
     * ```
666
     *
667
     * @param bool $runValidation whether to perform validation (calling [[validate()]])
668
     * before saving the record. Defaults to `true`. If the validation fails, the record
669
     * will not be saved to the database and this method will return `false`.
670
     * @param array $attributeNames list of attribute names that need to be saved. Defaults to null,
671
     * meaning all attributes that are loaded from DB will be saved.
672
     * @return bool whether the saving succeeded (i.e. no validation errors occurred).
673
     */
674 104
    public function save($runValidation = true, $attributeNames = null)
675
    {
676 104
        if ($this->getIsNewRecord()) {
677 91
            return $this->insert($runValidation, $attributeNames);
678
        }
679
680 28
        return $this->update($runValidation, $attributeNames) !== false;
681
    }
682
683
    /**
684
     * Saves the changes to this active record into the associated database table.
685
     *
686
     * This method performs the following steps in order:
687
     *
688
     * 1. call [[beforeValidate()]] when `$runValidation` is `true`. If [[beforeValidate()]]
689
     *    returns `false`, the rest of the steps will be skipped;
690
     * 2. call [[afterValidate()]] when `$runValidation` is `true`. If validation
691
     *    failed, the rest of the steps will be skipped;
692
     * 3. call [[beforeSave()]]. If [[beforeSave()]] returns `false`,
693
     *    the rest of the steps will be skipped;
694
     * 4. save the record into database. If this fails, it will skip the rest of the steps;
695
     * 5. call [[afterSave()]];
696
     *
697
     * In the above step 1, 2, 3 and 5, events [[EVENT_BEFORE_VALIDATE]],
698
     * [[EVENT_AFTER_VALIDATE]], [[EVENT_BEFORE_UPDATE]], and [[EVENT_AFTER_UPDATE]]
699
     * will be raised by the corresponding methods.
700
     *
701
     * Only the [[dirtyAttributes|changed attribute values]] will be saved into database.
702
     *
703
     * For example, to update a customer record:
704
     *
705
     * ```php
706
     * $customer = Customer::findOne($id);
707
     * $customer->name = $name;
708
     * $customer->email = $email;
709
     * $customer->update();
710
     * ```
711
     *
712
     * Note that it is possible the update does not affect any row in the table.
713
     * In this case, this method will return 0. For this reason, you should use the following
714
     * code to check if update() is successful or not:
715
     *
716
     * ```php
717
     * if ($customer->update() !== false) {
718
     *     // update successful
719
     * } else {
720
     *     // update failed
721
     * }
722
     * ```
723
     *
724
     * @param bool $runValidation whether to perform validation (calling [[validate()]])
725
     * before saving the record. Defaults to `true`. If the validation fails, the record
726
     * will not be saved to the database and this method will return `false`.
727
     * @param array $attributeNames list of attribute names that need to be saved. Defaults to null,
728
     * meaning all attributes that are loaded from DB will be saved.
729
     * @return int|false the number of rows affected, or `false` if validation fails
730
     * or [[beforeSave()]] stops the updating process.
731
     * @throws StaleObjectException if [[optimisticLock|optimistic locking]] is enabled and the data
732
     * being updated is outdated.
733
     * @throws Exception in case update failed.
734
     */
735
    public function update($runValidation = true, $attributeNames = null)
736
    {
737
        if ($runValidation && !$this->validate($attributeNames)) {
0 ignored issues
show
Bug introduced by
It seems like $attributeNames defined by parameter $attributeNames on line 735 can also be of type array; however, yii\base\Model::validate() does only seem to accept array<integer,string>|string|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
738
            return false;
739
        }
740
741
        return $this->updateInternal($attributeNames);
742
    }
743
744
    /**
745
     * Updates the specified attributes.
746
     *
747
     * This method is a shortcut to [[update()]] when data validation is not needed
748
     * and only a small set attributes need to be updated.
749
     *
750
     * You may specify the attributes to be updated as name list or name-value pairs.
751
     * If the latter, the corresponding attribute values will be modified accordingly.
752
     * The method will then save the specified attributes into database.
753
     *
754
     * Note that this method will **not** perform data validation and will **not** trigger events.
755
     *
756
     * @param array $attributes the attributes (names or name-value pairs) to be updated
757
     * @return int the number of rows affected.
758
     */
759 4
    public function updateAttributes($attributes)
760
    {
761 4
        $attrs = [];
762 4
        foreach ($attributes as $name => $value) {
763 4
            if (is_int($name)) {
764
                $attrs[] = $value;
765
            } else {
766 4
                $this->$name = $value;
767 4
                $attrs[] = $name;
768
            }
769
        }
770
771 4
        $values = $this->getDirtyAttributes($attrs);
772 4
        if (empty($values) || $this->getIsNewRecord()) {
773 4
            return 0;
774
        }
775
776 3
        $rows = static::updateAll($values, $this->getOldPrimaryKey(true));
777
778 3
        foreach ($values as $name => $value) {
779 3
            $this->_oldAttributes[$name] = $this->_attributes[$name];
780
        }
781
782 3
        return $rows;
783
    }
784
785
    /**
786
     * @see update()
787
     * @param array $attributes attributes to update
788
     * @return int|false the number of rows affected, or false if [[beforeSave()]] stops the updating process.
789
     * @throws StaleObjectException
790
     */
791 38
    protected function updateInternal($attributes = null)
792
    {
793 38
        if (!$this->beforeSave(false)) {
794
            return false;
795
        }
796 38
        $values = $this->getDirtyAttributes($attributes);
0 ignored issues
show
Bug introduced by
It seems like $attributes defined by parameter $attributes on line 791 can also be of type array; however, yii\db\BaseActiveRecord::getDirtyAttributes() does only seem to accept array<integer,string>|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
797 38
        if (empty($values)) {
798 3
            $this->afterSave(false, $values);
799 3
            return 0;
800
        }
801 36
        $condition = $this->getOldPrimaryKey(true);
802 36
        $lock = $this->optimisticLock();
803 36
        if ($lock !== null) {
804 3
            $values[$lock] = $this->$lock + 1;
805 3
            $condition[$lock] = $this->$lock;
806
        }
807
        // We do not check the return value of updateAll() because it's possible
808
        // that the UPDATE statement doesn't change anything and thus returns 0.
809 36
        $rows = static::updateAll($values, $condition);
810
811 36
        if ($lock !== null && !$rows) {
812 3
            throw new StaleObjectException('The object being updated is outdated.');
813
        }
814
815 36
        if (isset($values[$lock])) {
816 3
            $this->$lock = $values[$lock];
817
        }
818
819 36
        $changedAttributes = [];
820 36
        foreach ($values as $name => $value) {
821 36
            $changedAttributes[$name] = isset($this->_oldAttributes[$name]) ? $this->_oldAttributes[$name] : null;
822 36
            $this->_oldAttributes[$name] = $value;
823
        }
824 36
        $this->afterSave(false, $changedAttributes);
825
826 36
        return $rows;
827
    }
828
829
    /**
830
     * Updates one or several counter columns for the current AR object.
831
     * Note that this method differs from [[updateAllCounters()]] in that it only
832
     * saves counters for the current AR object.
833
     *
834
     * An example usage is as follows:
835
     *
836
     * ```php
837
     * $post = Post::findOne($id);
838
     * $post->updateCounters(['view_count' => 1]);
839
     * ```
840
     *
841
     * @param array $counters the counters to be updated (attribute name => increment value)
842
     * Use negative values if you want to decrement the counters.
843
     * @return bool whether the saving is successful
844
     * @see updateAllCounters()
845
     */
846 6
    public function updateCounters($counters)
847
    {
848 6
        if (static::updateAllCounters($counters, $this->getOldPrimaryKey(true)) > 0) {
849 6
            foreach ($counters as $name => $value) {
850 6
                if (!isset($this->_attributes[$name])) {
851 3
                    $this->_attributes[$name] = $value;
852
                } else {
853 3
                    $this->_attributes[$name] += $value;
854
                }
855 6
                $this->_oldAttributes[$name] = $this->_attributes[$name];
856
            }
857
858 6
            return true;
859
        }
860
861
        return false;
862
    }
863
864
    /**
865
     * Deletes the table row corresponding to this active record.
866
     *
867
     * This method performs the following steps in order:
868
     *
869
     * 1. call [[beforeDelete()]]. If the method returns `false`, it will skip the
870
     *    rest of the steps;
871
     * 2. delete the record from the database;
872
     * 3. call [[afterDelete()]].
873
     *
874
     * In the above step 1 and 3, events named [[EVENT_BEFORE_DELETE]] and [[EVENT_AFTER_DELETE]]
875
     * will be raised by the corresponding methods.
876
     *
877
     * @return int|false the number of rows deleted, or `false` if the deletion is unsuccessful for some reason.
878
     * Note that it is possible the number of rows deleted is 0, even though the deletion execution is successful.
879
     * @throws StaleObjectException if [[optimisticLock|optimistic locking]] is enabled and the data
880
     * being deleted is outdated.
881
     * @throws Exception in case delete failed.
882
     */
883
    public function delete()
884
    {
885
        $result = false;
886
        if ($this->beforeDelete()) {
887
            // we do not check the return value of deleteAll() because it's possible
888
            // the record is already deleted in the database and thus the method will return 0
889
            $condition = $this->getOldPrimaryKey(true);
890
            $lock = $this->optimisticLock();
891
            if ($lock !== null) {
892
                $condition[$lock] = $this->$lock;
893
            }
894
            $result = static::deleteAll($condition);
895
            if ($lock !== null && !$result) {
896
                throw new StaleObjectException('The object being deleted is outdated.');
897
            }
898
            $this->_oldAttributes = null;
899
            $this->afterDelete();
900
        }
901
902
        return $result;
903
    }
904
905
    /**
906
     * Returns a value indicating whether the current record is new.
907
     * @return bool whether the record is new and should be inserted when calling [[save()]].
908
     */
909 147
    public function getIsNewRecord()
910
    {
911 147
        return $this->_oldAttributes === null;
912
    }
913
914
    /**
915
     * Sets the value indicating whether the record is new.
916
     * @param bool $value whether the record is new and should be inserted when calling [[save()]].
917
     * @see getIsNewRecord()
918
     */
919
    public function setIsNewRecord($value)
920
    {
921
        $this->_oldAttributes = $value ? null : $this->_attributes;
922
    }
923
924
    /**
925
     * Initializes the object.
926
     * This method is called at the end of the constructor.
927
     * The default implementation will trigger an [[EVENT_INIT]] event.
928
     */
929 437
    public function init()
930
    {
931 437
        parent::init();
932 437
        $this->trigger(self::EVENT_INIT);
933 437
    }
934
935
    /**
936
     * This method is called when the AR object is created and populated with the query result.
937
     * The default implementation will trigger an [[EVENT_AFTER_FIND]] event.
938
     * When overriding this method, make sure you call the parent implementation to ensure the
939
     * event is triggered.
940
     */
941 322
    public function afterFind()
942
    {
943 322
        $this->trigger(self::EVENT_AFTER_FIND);
944 322
    }
945
946
    /**
947
     * This method is called at the beginning of inserting or updating a record.
948
     *
949
     * The default implementation will trigger an [[EVENT_BEFORE_INSERT]] event when `$insert` is `true`,
950
     * or an [[EVENT_BEFORE_UPDATE]] event if `$insert` is `false`.
951
     * When overriding this method, make sure you call the parent implementation like the following:
952
     *
953
     * ```php
954
     * public function beforeSave($insert)
955
     * {
956
     *     if (!parent::beforeSave($insert)) {
957
     *         return false;
958
     *     }
959
     *
960
     *     // ...custom code here...
961
     *     return true;
962
     * }
963
     * ```
964
     *
965
     * @param bool $insert whether this method called while inserting a record.
966
     * If `false`, it means the method is called while updating a record.
967
     * @return bool whether the insertion or updating should continue.
968
     * If `false`, the insertion or updating will be cancelled.
969
     */
970 114
    public function beforeSave($insert)
971
    {
972 114
        $event = new ModelEvent();
973 114
        $this->trigger($insert ? self::EVENT_BEFORE_INSERT : self::EVENT_BEFORE_UPDATE, $event);
974
975 114
        return $event->isValid;
976
    }
977
978
    /**
979
     * This method is called at the end of inserting or updating a record.
980
     * The default implementation will trigger an [[EVENT_AFTER_INSERT]] event when `$insert` is `true`,
981
     * or an [[EVENT_AFTER_UPDATE]] event if `$insert` is `false`. The event class used is [[AfterSaveEvent]].
982
     * When overriding this method, make sure you call the parent implementation so that
983
     * the event is triggered.
984
     * @param bool $insert whether this method called while inserting a record.
985
     * If `false`, it means the method is called while updating a record.
986
     * @param array $changedAttributes The old values of attributes that had changed and were saved.
987
     * You can use this parameter to take action based on the changes made for example send an email
988
     * when the password had changed or implement audit trail that tracks all the changes.
989
     * `$changedAttributes` gives you the old attribute values while the active record (`$this`) has
990
     * already the new, updated values.
991
     *
992
     * Note that no automatic type conversion performed by default. You may use
993
     * [[\yii\behaviors\AttributeTypecastBehavior]] to facilitate attribute typecasting.
994
     * See http://www.yiiframework.com/doc-2.0/guide-db-active-record.html#attributes-typecasting.
995
     */
996 107
    public function afterSave($insert, $changedAttributes)
997
    {
998 107
        $this->trigger($insert ? self::EVENT_AFTER_INSERT : self::EVENT_AFTER_UPDATE, new AfterSaveEvent([
999 107
            'changedAttributes' => $changedAttributes,
1000
        ]));
1001 107
    }
1002
1003
    /**
1004
     * This method is invoked before deleting a record.
1005
     *
1006
     * The default implementation raises the [[EVENT_BEFORE_DELETE]] event.
1007
     * When overriding this method, make sure you call the parent implementation like the following:
1008
     *
1009
     * ```php
1010
     * public function beforeDelete()
1011
     * {
1012
     *     if (!parent::beforeDelete()) {
1013
     *         return false;
1014
     *     }
1015
     *
1016
     *     // ...custom code here...
1017
     *     return true;
1018
     * }
1019
     * ```
1020
     *
1021
     * @return bool whether the record should be deleted. Defaults to `true`.
1022
     */
1023 6
    public function beforeDelete()
1024
    {
1025 6
        $event = new ModelEvent();
1026 6
        $this->trigger(self::EVENT_BEFORE_DELETE, $event);
1027
1028 6
        return $event->isValid;
1029
    }
1030
1031
    /**
1032
     * This method is invoked after deleting a record.
1033
     * The default implementation raises the [[EVENT_AFTER_DELETE]] event.
1034
     * You may override this method to do postprocessing after the record is deleted.
1035
     * Make sure you call the parent implementation so that the event is raised properly.
1036
     */
1037 6
    public function afterDelete()
1038
    {
1039 6
        $this->trigger(self::EVENT_AFTER_DELETE);
1040 6
    }
1041
1042
    /**
1043
     * Repopulates this active record with the latest data.
1044
     *
1045
     * If the refresh is successful, an [[EVENT_AFTER_REFRESH]] event will be triggered.
1046
     * This event is available since version 2.0.8.
1047
     *
1048
     * @return bool whether the row still exists in the database. If `true`, the latest data
1049
     * will be populated to this active record. Otherwise, this record will remain unchanged.
1050
     */
1051
    public function refresh()
1052
    {
1053
        /* @var $record BaseActiveRecord */
1054
        $record = static::findOne($this->getPrimaryKey(true));
1055
        return $this->refreshInternal($record);
1056
    }
1057
1058
    /**
1059
     * Repopulates this active record with the latest data from a newly fetched instance.
1060
     * @param BaseActiveRecord $record the record to take attributes from.
1061
     * @return bool whether refresh was successful.
1062
     * @see refresh()
1063
     * @since 2.0.13
1064
     */
1065 29
    protected function refreshInternal($record)
1066
    {
1067 29
        if ($record === null) {
1068 3
            return false;
1069
        }
1070 29
        foreach ($this->attributes() as $name) {
1071 29
            $this->_attributes[$name] = isset($record->_attributes[$name]) ? $record->_attributes[$name] : null;
1072
        }
1073 29
        $this->_oldAttributes = $record->_oldAttributes;
1074 29
        $this->_related = [];
1075 29
        $this->_relationsDependencies = [];
1076 29
        $this->afterRefresh();
1077
1078 29
        return true;
1079
    }
1080
1081
    /**
1082
     * This method is called when the AR object is refreshed.
1083
     * The default implementation will trigger an [[EVENT_AFTER_REFRESH]] event.
1084
     * When overriding this method, make sure you call the parent implementation to ensure the
1085
     * event is triggered.
1086
     * @since 2.0.8
1087
     */
1088 29
    public function afterRefresh()
1089
    {
1090 29
        $this->trigger(self::EVENT_AFTER_REFRESH);
1091 29
    }
1092
1093
    /**
1094
     * Returns a value indicating whether the given active record is the same as the current one.
1095
     * The comparison is made by comparing the table names and the primary key values of the two active records.
1096
     * If one of the records [[isNewRecord|is new]] they are also considered not equal.
1097
     * @param ActiveRecordInterface $record record to compare to
1098
     * @return bool whether the two active records refer to the same row in the same database table.
1099
     */
1100
    public function equals($record)
1101
    {
1102
        if ($this->getIsNewRecord() || $record->getIsNewRecord()) {
1103
            return false;
1104
        }
1105
1106
        return get_class($this) === get_class($record) && $this->getPrimaryKey() === $record->getPrimaryKey();
1107
    }
1108
1109
    /**
1110
     * Returns the primary key value(s).
1111
     * @param bool $asArray whether to return the primary key value as an array. If `true`,
1112
     * the return value will be an array with column names as keys and column values as values.
1113
     * Note that for composite primary keys, an array will always be returned regardless of this parameter value.
1114
     * @property mixed The primary key value. An array (column name => column value) is returned if
1115
     * the primary key is composite. A string is returned otherwise (null will be returned if
1116
     * the key value is null).
1117
     * @return mixed the primary key value. An array (column name => column value) is returned if the primary key
1118
     * is composite or `$asArray` is `true`. A string is returned otherwise (null will be returned if
1119
     * the key value is null).
1120
     */
1121 45
    public function getPrimaryKey($asArray = false)
1122
    {
1123 45
        $keys = $this->primaryKey();
1124 45
        if (!$asArray && count($keys) === 1) {
1125 19
            return isset($this->_attributes[$keys[0]]) ? $this->_attributes[$keys[0]] : null;
1126
        }
1127
1128 29
        $values = [];
1129 29
        foreach ($keys as $name) {
1130 29
            $values[$name] = isset($this->_attributes[$name]) ? $this->_attributes[$name] : null;
1131
        }
1132
1133 29
        return $values;
1134
    }
1135
1136
    /**
1137
     * Returns the old primary key value(s).
1138
     * This refers to the primary key value that is populated into the record
1139
     * after executing a find method (e.g. find(), findOne()).
1140
     * The value remains unchanged even if the primary key attribute is manually assigned with a different value.
1141
     * @param bool $asArray whether to return the primary key value as an array. If `true`,
1142
     * the return value will be an array with column name as key and column value as value.
1143
     * If this is `false` (default), a scalar value will be returned for non-composite primary key.
1144
     * @property mixed The old primary key value. An array (column name => column value) is
1145
     * returned if the primary key is composite. A string is returned otherwise (null will be
1146
     * returned if the key value is null).
1147
     * @return mixed the old primary key value. An array (column name => column value) is returned if the primary key
1148
     * is composite or `$asArray` is `true`. A string is returned otherwise (null will be returned if
1149
     * the key value is null).
1150
     * @throws Exception if the AR model does not have a primary key
1151
     */
1152 70
    public function getOldPrimaryKey($asArray = false)
1153
    {
1154 70
        $keys = $this->primaryKey();
1155 70
        if (empty($keys)) {
1156
            throw new Exception(get_class($this) . ' does not have a primary key. You should either define a primary key for the corresponding table or override the primaryKey() method.');
1157
        }
1158 70
        if (!$asArray && count($keys) === 1) {
1159
            return isset($this->_oldAttributes[$keys[0]]) ? $this->_oldAttributes[$keys[0]] : null;
1160
        }
1161
1162 70
        $values = [];
1163 70
        foreach ($keys as $name) {
1164 70
            $values[$name] = isset($this->_oldAttributes[$name]) ? $this->_oldAttributes[$name] : null;
1165
        }
1166
1167 70
        return $values;
1168
    }
1169
1170
    /**
1171
     * Populates an active record object using a row of data from the database/storage.
1172
     *
1173
     * This is an internal method meant to be called to create active record objects after
1174
     * fetching data from the database. It is mainly used by [[ActiveQuery]] to populate
1175
     * the query results into active records.
1176
     *
1177
     * When calling this method manually you should call [[afterFind()]] on the created
1178
     * record to trigger the [[EVENT_AFTER_FIND|afterFind Event]].
1179
     *
1180
     * @param BaseActiveRecord $record the record to be populated. In most cases this will be an instance
1181
     * created by [[instantiate()]] beforehand.
1182
     * @param array $row attribute values (name => value)
1183
     */
1184 322
    public static function populateRecord($record, $row)
1185
    {
1186 322
        $columns = array_flip($record->attributes());
1187 322
        foreach ($row as $name => $value) {
1188 322
            if (isset($columns[$name])) {
1189 322
                $record->_attributes[$name] = $value;
1190 6
            } elseif ($record->canSetProperty($name)) {
1191 322
                $record->$name = $value;
1192
            }
1193
        }
1194 322
        $record->_oldAttributes = $record->_attributes;
1195 322
        $record->_related = [];
1196 322
        $record->_relationsDependencies = [];
1197 322
    }
1198
1199
    /**
1200
     * Creates an active record instance.
1201
     *
1202
     * This method is called together with [[populateRecord()]] by [[ActiveQuery]].
1203
     * It is not meant to be used for creating new records directly.
1204
     *
1205
     * You may override this method if the instance being created
1206
     * depends on the row data to be populated into the record.
1207
     * For example, by creating a record based on the value of a column,
1208
     * you may implement the so-called single-table inheritance mapping.
1209
     * @param array $row row data to be populated into the record.
1210
     * @return static the newly created active record
1211
     */
1212 316
    public static function instantiate($row)
0 ignored issues
show
Unused Code introduced by
The parameter $row is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1213
    {
1214 316
        return new static();
1215
    }
1216
1217
    /**
1218
     * Returns whether there is an element at the specified offset.
1219
     * This method is required by the interface [[\ArrayAccess]].
1220
     * @param mixed $offset the offset to check on
1221
     * @return bool whether there is an element at the specified offset.
1222
     */
1223 33
    public function offsetExists($offset)
1224
    {
1225 33
        return $this->__isset($offset);
1226
    }
1227
1228
    /**
1229
     * Returns the relation object with the specified name.
1230
     * A relation is defined by a getter method which returns an [[ActiveQueryInterface]] object.
1231
     * It can be declared in either the Active Record class itself or one of its behaviors.
1232
     * @param string $name the relation name, e.g. `orders` for a relation defined via `getOrders()` method (case-sensitive).
1233
     * @param bool $throwException whether to throw exception if the relation does not exist.
1234
     * @return ActiveQueryInterface|ActiveQuery the relational query object. If the relation does not exist
1235
     * and `$throwException` is `false`, `null` will be returned.
1236
     * @throws InvalidArgumentException if the named relation does not exist.
1237
     */
1238 150
    public function getRelation($name, $throwException = true)
1239
    {
1240 150
        $getter = 'get' . $name;
1241
        try {
1242
            // the relation could be defined in a behavior
1243 150
            $relation = $this->$getter();
1244
        } catch (UnknownMethodException $e) {
1245
            if ($throwException) {
1246
                throw new InvalidArgumentException(get_class($this) . ' has no relation named "' . $name . '".', 0, $e);
1247
            }
1248
1249
            return null;
1250
        }
1251 150
        if (!$relation instanceof ActiveQueryInterface) {
1252
            if ($throwException) {
1253
                throw new InvalidArgumentException(get_class($this) . ' has no relation named "' . $name . '".');
1254
            }
1255
1256
            return null;
1257
        }
1258
1259 150
        if (method_exists($this, $getter)) {
1260
            // relation name is case sensitive, trying to validate it when the relation is defined within this class
1261 150
            $method = new \ReflectionMethod($this, $getter);
1262 150
            $realName = lcfirst(substr($method->getName(), 3));
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
1263 150
            if ($realName !== $name) {
1264
                if ($throwException) {
1265
                    throw new InvalidArgumentException('Relation names are case sensitive. ' . get_class($this) . " has a relation named \"$realName\" instead of \"$name\".");
1266
                }
1267
1268
                return null;
1269
            }
1270
        }
1271
1272 150
        return $relation;
1273
    }
1274
1275
    /**
1276
     * Establishes the relationship between two models.
1277
     *
1278
     * The relationship is established by setting the foreign key value(s) in one model
1279
     * to be the corresponding primary key value(s) in the other model.
1280
     * The model with the foreign key will be saved into database without performing validation.
1281
     *
1282
     * If the relationship involves a junction table, a new row will be inserted into the
1283
     * junction table which contains the primary key values from both models.
1284
     *
1285
     * Note that this method requires that the primary key value is not null.
1286
     *
1287
     * @param string $name the case sensitive name of the relationship, e.g. `orders` for a relation defined via `getOrders()` method.
1288
     * @param ActiveRecordInterface $model the model to be linked with the current one.
1289
     * @param array $extraColumns additional column values to be saved into the junction table.
1290
     * This parameter is only meaningful for a relationship involving a junction table
1291
     * (i.e., a relation set with [[ActiveRelationTrait::via()]] or [[ActiveQuery::viaTable()]].)
1292
     * @throws InvalidCallException if the method is unable to link two models.
1293
     */
1294 9
    public function link($name, $model, $extraColumns = [])
1295
    {
1296 9
        $relation = $this->getRelation($name);
1297
1298 9
        if ($relation->via !== null) {
0 ignored issues
show
Bug introduced by
Accessing via 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...
1299 3
            if ($this->getIsNewRecord() || $model->getIsNewRecord()) {
1300
                throw new InvalidCallException('Unable to link models: the models being linked cannot be newly created.');
1301
            }
1302 3
            if (is_array($relation->via)) {
0 ignored issues
show
Bug introduced by
Accessing via 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...
1303
                /* @var $viaRelation ActiveQuery */
1304 3
                [$viaName, $viaRelation] = $relation->via;
0 ignored issues
show
Bug introduced by
Accessing via 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...
Bug introduced by
The variable $viaName does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $viaRelation seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
1305 3
                $viaClass = $viaRelation->modelClass;
0 ignored issues
show
Bug introduced by
The variable $viaRelation seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
1306
                // unset $viaName so that it can be reloaded to reflect the change
1307 3
                unset($this->_related[$viaName]);
1308
            } else {
1309
                $viaRelation = $relation->via;
0 ignored issues
show
Bug introduced by
Accessing via 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...
1310
                $viaTable = reset($relation->via->from);
0 ignored issues
show
Bug introduced by
Accessing via 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...
1311
            }
1312 3
            $columns = [];
1313 3
            foreach ($viaRelation->link as $a => $b) {
0 ignored issues
show
Bug introduced by
The variable $viaRelation does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1314 3
                $columns[$a] = $this->$b;
1315
            }
1316 3
            foreach ($relation->link as $a => $b) {
0 ignored issues
show
Bug introduced by
Accessing link 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...
1317 3
                $columns[$b] = $model->$a;
1318
            }
1319 3
            foreach ($extraColumns as $k => $v) {
1320 3
                $columns[$k] = $v;
1321
            }
1322 3
            if (is_array($relation->via)) {
0 ignored issues
show
Bug introduced by
Accessing via 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...
1323
                /* @var $viaClass ActiveRecordInterface */
1324
                /* @var $record ActiveRecordInterface */
1325 3
                $record = Yii::createObject($viaClass);
0 ignored issues
show
Bug introduced by
The variable $viaClass does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1326 3
                foreach ($columns as $column => $value) {
1327 3
                    $record->$column = $value;
1328
                }
1329 3
                $record->insert(false);
1330
            } else {
1331
                /* @var $viaTable string */
1332
                static::getDb()->createCommand()
1333 3
                    ->insert($viaTable, $columns)->execute();
0 ignored issues
show
Bug introduced by
The variable $viaTable does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1334
            }
1335
        } else {
1336 9
            $p1 = $model->isPrimaryKey(array_keys($relation->link));
0 ignored issues
show
Bug introduced by
Accessing link 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...
1337 9
            $p2 = static::isPrimaryKey(array_values($relation->link));
0 ignored issues
show
Bug introduced by
Accessing link 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...
1338 9
            if ($p1 && $p2) {
1339
                if ($this->getIsNewRecord() && $model->getIsNewRecord()) {
1340
                    throw new InvalidCallException('Unable to link models: at most one model can be newly created.');
1341
                } elseif ($this->getIsNewRecord()) {
1342
                    $this->bindModels(array_flip($relation->link), $this, $model);
0 ignored issues
show
Bug introduced by
Accessing link 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...
1343
                } else {
1344
                    $this->bindModels($relation->link, $model, $this);
0 ignored issues
show
Bug introduced by
Accessing link 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...
1345
                }
1346 9
            } elseif ($p1) {
1347 3
                $this->bindModels(array_flip($relation->link), $this, $model);
0 ignored issues
show
Bug introduced by
Accessing link 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...
1348 9
            } elseif ($p2) {
1349 9
                $this->bindModels($relation->link, $model, $this);
0 ignored issues
show
Bug introduced by
Accessing link 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...
1350
            } else {
1351
                throw new InvalidCallException('Unable to link models: the link defining the relation does not involve any primary key.');
1352
            }
1353
        }
1354
1355
        // update lazily loaded related objects
1356 9
        if (!$relation->multiple) {
0 ignored issues
show
Bug introduced by
Accessing multiple 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...
1357 3
            $this->_related[$name] = $model;
1358 9
        } elseif (isset($this->_related[$name])) {
1359 9
            if ($relation->indexBy !== null) {
0 ignored issues
show
Bug introduced by
Accessing indexBy 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...
1360 6
                if ($relation->indexBy instanceof \Closure) {
0 ignored issues
show
Bug introduced by
Accessing indexBy 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...
1361 3
                    $index = call_user_func($relation->indexBy, $model);
0 ignored issues
show
Bug introduced by
Accessing indexBy 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...
1362
                } else {
1363 3
                    $index = $model->{$relation->indexBy};
0 ignored issues
show
Bug introduced by
Accessing indexBy 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...
1364
                }
1365 6
                $this->_related[$name][$index] = $model;
1366
            } else {
1367 3
                $this->_related[$name][] = $model;
1368
            }
1369
        }
1370 9
    }
1371
1372
    /**
1373
     * Destroys the relationship between two models.
1374
     *
1375
     * The model with the foreign key of the relationship will be deleted if `$delete` is `true`.
1376
     * Otherwise, the foreign key will be set `null` and the model will be saved without validation.
1377
     *
1378
     * @param string $name the case sensitive name of the relationship, e.g. `orders` for a relation defined via `getOrders()` method.
1379
     * @param ActiveRecordInterface $model the model to be unlinked from the current one.
1380
     * You have to make sure that the model is really related with the current model as this method
1381
     * does not check this.
1382
     * @param bool $delete whether to delete the model that contains the foreign key.
1383
     * If `false`, the model's foreign key will be set `null` and saved.
1384
     * If `true`, the model containing the foreign key will be deleted.
1385
     * @throws InvalidCallException if the models cannot be unlinked
1386
     */
1387 3
    public function unlink($name, $model, $delete = false)
1388
    {
1389 3
        $relation = $this->getRelation($name);
1390
1391 3
        if ($relation->via !== null) {
0 ignored issues
show
Bug introduced by
Accessing via 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...
1392 3
            if (is_array($relation->via)) {
0 ignored issues
show
Bug introduced by
Accessing via 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...
1393
                /* @var $viaRelation ActiveQuery */
1394 3
                [$viaName, $viaRelation] = $relation->via;
0 ignored issues
show
Bug introduced by
Accessing via 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...
Bug introduced by
The variable $viaName does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $viaRelation seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
1395 3
                $viaClass = $viaRelation->modelClass;
0 ignored issues
show
Bug introduced by
The variable $viaRelation seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
1396 3
                unset($this->_related[$viaName]);
1397
            } else {
1398 3
                $viaRelation = $relation->via;
0 ignored issues
show
Bug introduced by
Accessing via 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...
1399 3
                $viaTable = reset($relation->via->from);
0 ignored issues
show
Bug introduced by
Accessing via 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...
1400
            }
1401 3
            $columns = [];
1402 3
            foreach ($viaRelation->link as $a => $b) {
0 ignored issues
show
Bug introduced by
The variable $viaRelation does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1403 3
                $columns[$a] = $this->$b;
1404
            }
1405 3
            foreach ($relation->link as $a => $b) {
0 ignored issues
show
Bug introduced by
Accessing link 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...
1406 3
                $columns[$b] = $model->$a;
1407
            }
1408 3
            $nulls = [];
1409 3
            foreach (array_keys($columns) as $a) {
1410 3
                $nulls[$a] = null;
1411
            }
1412 3
            if (is_array($relation->via)) {
0 ignored issues
show
Bug introduced by
Accessing via 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...
1413
                /* @var $viaClass ActiveRecordInterface */
1414 3
                if ($delete) {
1415 3
                    $viaClass::deleteAll($columns);
0 ignored issues
show
Bug introduced by
The variable $viaClass does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1416
                } else {
1417 3
                    $viaClass::updateAll($nulls, $columns);
1418
                }
1419
            } else {
1420
                /* @var $viaTable string */
1421
                /* @var $command Command */
1422 3
                $command = static::getDb()->createCommand();
1423 3
                if ($delete) {
1424
                    $command->delete($viaTable, $columns)->execute();
0 ignored issues
show
Bug introduced by
The variable $viaTable does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1425
                } else {
1426 3
                    $command->update($viaTable, $nulls, $columns)->execute();
1427
                }
1428
            }
1429
        } else {
1430 3
            $p1 = $model->isPrimaryKey(array_keys($relation->link));
0 ignored issues
show
Bug introduced by
Accessing link 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...
1431 3
            $p2 = static::isPrimaryKey(array_values($relation->link));
0 ignored issues
show
Bug introduced by
Accessing link 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...
1432 3
            if ($p2) {
1433 3
                if ($delete) {
1434 3
                    $model->delete();
1435
                } else {
1436 3
                    foreach ($relation->link as $a => $b) {
0 ignored issues
show
Bug introduced by
Accessing link 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...
1437 3
                        $model->$a = null;
1438
                    }
1439 3
                    $model->save(false);
1440
                }
1441
            } elseif ($p1) {
1442
                foreach ($relation->link as $a => $b) {
0 ignored issues
show
Bug introduced by
Accessing link 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...
1443
                    if (is_array($this->$b)) { // relation via array valued attribute
1444
                        if (($key = array_search($model->$a, $this->$b, false)) !== false) {
1445
                            $values = $this->$b;
1446
                            unset($values[$key]);
1447
                            $this->$b = array_values($values);
1448
                        }
1449
                    } else {
1450
                        $this->$b = null;
1451
                    }
1452
                }
1453
                $delete ? $this->delete() : $this->save(false);
1454
            } else {
1455
                throw new InvalidCallException('Unable to unlink models: the link does not involve any primary key.');
1456
            }
1457
        }
1458
1459 3
        if (!$relation->multiple) {
0 ignored issues
show
Bug introduced by
Accessing multiple 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...
1460
            unset($this->_related[$name]);
1461 3
        } elseif (isset($this->_related[$name])) {
1462
            /* @var $b ActiveRecordInterface */
1463 3
            foreach ($this->_related[$name] as $a => $b) {
1464 3
                if ($model->getPrimaryKey() === $b->getPrimaryKey()) {
1465 3
                    unset($this->_related[$name][$a]);
1466
                }
1467
            }
1468
        }
1469 3
    }
1470
1471
    /**
1472
     * Destroys the relationship in current model.
1473
     *
1474
     * The model with the foreign key of the relationship will be deleted if `$delete` is `true`.
1475
     * Otherwise, the foreign key will be set `null` and the model will be saved without validation.
1476
     *
1477
     * Note that to destroy the relationship without removing records make sure your keys can be set to null
1478
     *
1479
     * @param string $name the case sensitive name of the relationship, e.g. `orders` for a relation defined via `getOrders()` method.
1480
     * @param bool $delete whether to delete the model that contains the foreign key.
1481
     *
1482
     * Note that the deletion will be performed using [[deleteAll()]], which will not trigger any events on the related models.
1483
     * If you need [[EVENT_BEFORE_DELETE]] or [[EVENT_AFTER_DELETE]] to be triggered, you need to [[find()|find]] the models first
1484
     * and then call [[delete()]] on each of them.
1485
     */
1486 18
    public function unlinkAll($name, $delete = false)
1487
    {
1488 18
        $relation = $this->getRelation($name);
1489
1490 18
        if ($relation->via !== null) {
0 ignored issues
show
Bug introduced by
Accessing via 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...
1491 9
            if (is_array($relation->via)) {
0 ignored issues
show
Bug introduced by
Accessing via 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...
1492
                /* @var $viaRelation ActiveQuery */
1493 6
                [$viaName, $viaRelation] = $relation->via;
0 ignored issues
show
Bug introduced by
Accessing via 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...
Bug introduced by
The variable $viaName does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $viaRelation seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
1494 6
                $viaClass = $viaRelation->modelClass;
0 ignored issues
show
Bug introduced by
The variable $viaRelation seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
1495 6
                unset($this->_related[$viaName]);
1496
            } else {
1497 3
                $viaRelation = $relation->via;
0 ignored issues
show
Bug introduced by
Accessing via 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...
1498 3
                $viaTable = reset($relation->via->from);
0 ignored issues
show
Bug introduced by
Accessing via 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...
1499
            }
1500 9
            $condition = [];
1501 9
            $nulls = [];
1502 9
            foreach ($viaRelation->link as $a => $b) {
0 ignored issues
show
Bug introduced by
The variable $viaRelation does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1503 9
                $nulls[$a] = null;
1504 9
                $condition[$a] = $this->$b;
1505
            }
1506 9
            if (!empty($viaRelation->where)) {
1507
                $condition = ['and', $condition, $viaRelation->where];
1508
            }
1509 9
            if (!empty($viaRelation->on)) {
1510
                $condition = ['and', $condition, $viaRelation->on];
1511
            }
1512 9
            if (is_array($relation->via)) {
0 ignored issues
show
Bug introduced by
Accessing via 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...
1513
                /* @var $viaClass ActiveRecordInterface */
1514 6
                if ($delete) {
1515 6
                    $viaClass::deleteAll($condition);
0 ignored issues
show
Bug introduced by
The variable $viaClass does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1516
                } else {
1517 6
                    $viaClass::updateAll($nulls, $condition);
1518
                }
1519
            } else {
1520
                /* @var $viaTable string */
1521
                /* @var $command Command */
1522 3
                $command = static::getDb()->createCommand();
1523 3
                if ($delete) {
1524 3
                    $command->delete($viaTable, $condition)->execute();
0 ignored issues
show
Bug introduced by
The variable $viaTable does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1525
                } else {
1526 9
                    $command->update($viaTable, $nulls, $condition)->execute();
1527
                }
1528
            }
1529
        } else {
1530
            /* @var $relatedModel ActiveRecordInterface */
1531 12
            $relatedModel = $relation->modelClass;
0 ignored issues
show
Bug introduced by
Accessing modelClass 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...
1532 12
            if (!$delete && count($relation->link) === 1 && is_array($this->{$b = reset($relation->link)})) {
0 ignored issues
show
Bug introduced by
Accessing link 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...
1533
                // relation via array valued attribute
1534
                $this->$b = [];
1535
                $this->save(false);
1536
            } else {
1537 12
                $nulls = [];
1538 12
                $condition = [];
1539 12
                foreach ($relation->link as $a => $b) {
0 ignored issues
show
Bug introduced by
Accessing link 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...
1540 12
                    $nulls[$a] = null;
1541 12
                    $condition[$a] = $this->$b;
1542
                }
1543 12
                if (!empty($relation->where)) {
0 ignored issues
show
Bug introduced by
Accessing where 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...
1544 6
                    $condition = ['and', $condition, $relation->where];
0 ignored issues
show
Bug introduced by
Accessing where 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...
1545
                }
1546 12
                if (!empty($relation->on)) {
0 ignored issues
show
Bug introduced by
Accessing on 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...
1547 3
                    $condition = ['and', $condition, $relation->on];
0 ignored issues
show
Bug introduced by
Accessing on 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...
1548
                }
1549 12
                if ($delete) {
1550 9
                    $relatedModel::deleteAll($condition);
1551
                } else {
1552 6
                    $relatedModel::updateAll($nulls, $condition);
1553
                }
1554
            }
1555
        }
1556
1557 18
        unset($this->_related[$name]);
1558 18
    }
1559
1560
    /**
1561
     * @param array $link
1562
     * @param ActiveRecordInterface $foreignModel
1563
     * @param ActiveRecordInterface $primaryModel
1564
     * @throws InvalidCallException
1565
     */
1566 9
    private function bindModels($link, $foreignModel, $primaryModel)
1567
    {
1568 9
        foreach ($link as $fk => $pk) {
1569 9
            $value = $primaryModel->$pk;
1570 9
            if ($value === null) {
1571
                throw new InvalidCallException('Unable to link models: the primary key of ' . get_class($primaryModel) . ' is null.');
1572
            }
1573 9
            if (is_array($foreignModel->$fk)) { // relation via array valued attribute
1574
                $foreignModel->$fk = array_merge($foreignModel->$fk, [$value]);
1575
            } else {
1576 9
                $foreignModel->$fk = $value;
1577
            }
1578
        }
1579 9
        $foreignModel->save(false);
1580 9
    }
1581
1582
    /**
1583
     * Returns a value indicating whether the given set of attributes represents the primary key for this model.
1584
     * @param array $keys the set of attributes to check
1585
     * @return bool whether the given set of attributes represents the primary key for this model
1586
     */
1587 15
    public static function isPrimaryKey($keys)
1588
    {
1589 15
        $pks = static::primaryKey();
1590 15
        if (count($keys) === count($pks)) {
1591 15
            return count(array_intersect($keys, $pks)) === count($pks);
1592
        }
1593
1594 9
        return false;
1595
    }
1596
1597
    /**
1598
     * Returns the text label for the specified attribute.
1599
     * If the attribute looks like `relatedModel.attribute`, then the attribute will be received from the related model.
1600
     * @param string $attribute the attribute name
1601
     * @return string the attribute label
1602
     * @see generateAttributeLabel()
1603
     * @see attributeLabels()
1604
     */
1605 57
    public function getAttributeLabel($attribute)
1606
    {
1607 57
        $labels = $this->attributeLabels();
1608 57
        if (isset($labels[$attribute])) {
1609 10
            return $labels[$attribute];
1610 54
        } elseif (strpos($attribute, '.')) {
1611
            $attributeParts = explode('.', $attribute);
1612
            $neededAttribute = array_pop($attributeParts);
1613
1614
            $relatedModel = $this;
1615
            foreach ($attributeParts as $relationName) {
1616
                if ($relatedModel->isRelationPopulated($relationName) && $relatedModel->$relationName instanceof self) {
1617
                    $relatedModel = $relatedModel->$relationName;
1618
                } else {
1619
                    try {
1620
                        $relation = $relatedModel->getRelation($relationName);
1621
                    } catch (InvalidArgumentException $e) {
1622
                        return $this->generateAttributeLabel($attribute);
1623
                    }
1624
                    /* @var $modelClass ActiveRecordInterface */
1625
                    $modelClass = $relation->modelClass;
1626
                    $relatedModel = $modelClass::instance();
1627
                }
1628
            }
1629
1630
            $labels = $relatedModel->attributeLabels();
1631
            if (isset($labels[$neededAttribute])) {
1632
                return $labels[$neededAttribute];
1633
            }
1634
        }
1635
1636 54
        return $this->generateAttributeLabel($attribute);
1637
    }
1638
1639
    /**
1640
     * Returns the text hint for the specified attribute.
1641
     * If the attribute looks like `relatedModel.attribute`, then the attribute will be received from the related model.
1642
     * @param string $attribute the attribute name
1643
     * @return string the attribute hint
1644
     * @see attributeHints()
1645
     * @since 2.0.4
1646
     */
1647
    public function getAttributeHint($attribute)
1648
    {
1649
        $hints = $this->attributeHints();
1650
        if (isset($hints[$attribute])) {
1651
            return $hints[$attribute];
1652
        } elseif (strpos($attribute, '.')) {
1653
            $attributeParts = explode('.', $attribute);
1654
            $neededAttribute = array_pop($attributeParts);
1655
1656
            $relatedModel = $this;
1657
            foreach ($attributeParts as $relationName) {
1658
                if ($relatedModel->isRelationPopulated($relationName) && $relatedModel->$relationName instanceof self) {
1659
                    $relatedModel = $relatedModel->$relationName;
1660
                } else {
1661
                    try {
1662
                        $relation = $relatedModel->getRelation($relationName);
1663
                    } catch (InvalidArgumentException $e) {
1664
                        return '';
1665
                    }
1666
                    /* @var $modelClass ActiveRecordInterface */
1667
                    $modelClass = $relation->modelClass;
1668
                    $relatedModel = $modelClass::instance();
1669
                }
1670
            }
1671
1672
            $hints = $relatedModel->attributeHints();
1673
            if (isset($hints[$neededAttribute])) {
1674
                return $hints[$neededAttribute];
1675
            }
1676
        }
1677
1678
        return '';
1679
    }
1680
1681
    /**
1682
     * {@inheritdoc}
1683
     *
1684
     * The default implementation returns the names of the columns whose values have been populated into this record.
1685
     */
1686
    public function fields()
1687
    {
1688
        $fields = array_keys($this->_attributes);
1689
1690
        return array_combine($fields, $fields);
1691
    }
1692
1693
    /**
1694
     * {@inheritdoc}
1695
     *
1696
     * The default implementation returns the names of the relations that have been populated into this record.
1697
     */
1698
    public function extraFields()
1699
    {
1700
        $fields = array_keys($this->getRelatedRecords());
1701
1702
        return array_combine($fields, $fields);
1703
    }
1704
1705
    /**
1706
     * Sets the element value at the specified offset to null.
1707
     * This method is required by the SPL interface [[\ArrayAccess]].
1708
     * It is implicitly called when you use something like `unset($model[$offset])`.
1709
     * @param mixed $offset the offset to unset element
1710
     */
1711 3
    public function offsetUnset($offset)
1712
    {
1713 3
        if (property_exists($this, $offset)) {
1714
            $this->$offset = null;
1715
        } else {
1716 3
            unset($this->$offset);
1717
        }
1718 3
    }
1719
1720
    /**
1721
     * Resets dependent related models checking if their links contain specific attribute.
1722
     * @param string $attribute The changed attribute name.
1723
     */
1724 15
    private function resetDependentRelations($attribute)
1725
    {
1726 15
        foreach ($this->_relationsDependencies[$attribute] as $relation) {
1727 15
            unset($this->_related[$relation]);
1728
        }
1729 15
        unset($this->_relationsDependencies[$attribute]);
1730 15
    }
1731
1732
    /**
1733
     * Sets relation dependencies for a property
1734
     * @param string $name property name
1735
     * @param ActiveQueryInterface $relation relation instance
1736
     */
1737 73
    private function setRelationDependencies($name, $relation)
1738
    {
1739 73
        if (empty($relation->via) && $relation->link) {
0 ignored issues
show
Bug introduced by
Accessing via 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...
Bug introduced by
Accessing link 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...
1740 70
            foreach ($relation->link as $attribute) {
0 ignored issues
show
Bug introduced by
Accessing link 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...
1741 70
                $this->_relationsDependencies[$attribute][$name] = $name;
1742
            }
1743 39
        } elseif ($relation->via instanceof ActiveQueryInterface) {
0 ignored issues
show
Bug introduced by
Accessing via 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...
1744 15
            $this->setRelationDependencies($name, $relation->via);
0 ignored issues
show
Bug introduced by
Accessing via 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...
1745 27
        } elseif (is_array($relation->via)) {
0 ignored issues
show
Bug introduced by
Accessing via 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...
1746 24
            [, $viaQuery] = $relation->via;
0 ignored issues
show
Bug introduced by
Accessing via 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...
Bug introduced by
The variable $viaQuery does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
1747 24
            $this->setRelationDependencies($name, $viaQuery);
1748
        }
1749 73
    }
1750
}
1751