Completed
Push — master ( 0beb59...4b9e48 )
by Carsten
38:58 queued 27:39
created

BaseActiveRecord::updateAll()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 0
cts 2
cp 0
cc 1
eloc 2
nc 1
nop 2
crap 2
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\base\InvalidConfigException;
11
use yii\base\Event;
12
use yii\base\Model;
13
use yii\base\InvalidParamException;
14
use yii\base\ModelEvent;
15
use yii\base\NotSupportedException;
16
use yii\base\UnknownMethodException;
17
use yii\base\InvalidCallException;
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
101
    /**
102
     * @inheritdoc
103
     * @return static ActiveRecord instance matching the condition, or `null` if nothing matches.
104
     */
105 166
    public static function findOne($condition)
106
    {
107 166
        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 107 which is incompatible with the return type declared by the interface yii\db\ActiveRecordInterface::findOne of type yii\db\ActiveRecordInterface.
Loading history...
108
    }
109
110
    /**
111
     * @inheritdoc
112
     * @return static[] an array of ActiveRecord instances, or an empty array if nothing matches.
113
     */
114
    public static function findAll($condition)
115
    {
116
        return static::findByCondition($condition)->all();
117
    }
118
119
    /**
120
     * Finds ActiveRecord instance(s) by the given condition.
121
     * This method is internally called by [[findOne()]] and [[findAll()]].
122
     * @param mixed $condition please refer to [[findOne()]] for the explanation of this parameter
123
     * @return ActiveQueryInterface the newly created [[ActiveQueryInterface|ActiveQuery]] instance.
124
     * @throws InvalidConfigException if there is no primary key defined
125
     * @internal
126
     */
127
    protected static function findByCondition($condition)
128
    {
129
        $query = static::find();
130
131
        if (!ArrayHelper::isAssociative($condition)) {
132
            // query by primary key
133
            $primaryKey = static::primaryKey();
134
            if (isset($primaryKey[0])) {
135
                $condition = [$primaryKey[0] => $condition];
136
            } else {
137
                throw new InvalidConfigException('"' . get_called_class() . '" must have a primary key.');
138
            }
139
        }
140
141
        return $query->andWhere($condition);
142
    }
143
144
    /**
145
     * Updates the whole table using the provided attribute values and conditions.
146
     * For example, to change the status to be 1 for all customers whose status is 2:
147
     *
148
     * ```php
149
     * Customer::updateAll(['status' => 1], 'status = 2');
150
     * ```
151
     *
152
     * @param array $attributes attribute values (name-value pairs) to be saved into the table
153
     * @param string|array $condition the conditions that will be put in the WHERE part of the UPDATE SQL.
154
     * Please refer to [[Query::where()]] on how to specify this parameter.
155
     * @return int the number of rows updated
156
     * @throws NotSupportedException if not overridden
157
     */
158
    public static function updateAll($attributes, $condition = '')
159
    {
160
        throw new NotSupportedException(__METHOD__ . ' is not supported.');
161
    }
162
163
    /**
164
     * Updates the whole table using the provided counter changes and conditions.
165
     * For example, to increment all customers' age by 1,
166
     *
167
     * ```php
168
     * Customer::updateAllCounters(['age' => 1]);
169
     * ```
170
     *
171
     * @param array $counters the counters to be updated (attribute name => increment value).
172
     * Use negative values if you want to decrement the counters.
173
     * @param string|array $condition the conditions that will be put in the WHERE part of the UPDATE SQL.
174
     * Please refer to [[Query::where()]] on how to specify this parameter.
175
     * @return int the number of rows updated
176
     * @throws NotSupportedException if not overrided
177
     */
178
    public static function updateAllCounters($counters, $condition = '')
179
    {
180
        throw new NotSupportedException(__METHOD__ . ' is not supported.');
181
    }
182
183
    /**
184
     * Deletes rows in the table using the provided conditions.
185
     * WARNING: If you do not specify any condition, this method will delete ALL rows in the table.
186
     *
187
     * For example, to delete all customers whose status is 3:
188
     *
189
     * ```php
190
     * Customer::deleteAll('status = 3');
191
     * ```
192
     *
193
     * @param string|array $condition the conditions that will be put in the WHERE part of the DELETE SQL.
194
     * Please refer to [[Query::where()]] on how to specify this parameter.
195
     * @return int the number of rows deleted
196
     * @throws NotSupportedException if not overridden.
197
     */
198
    public static function deleteAll($condition = null)
199
    {
200
        throw new NotSupportedException(__METHOD__ . ' is not supported.');
201
    }
202
203
    /**
204
     * Returns the name of the column that stores the lock version for implementing optimistic locking.
205
     *
206
     * Optimistic locking allows multiple users to access the same record for edits and avoids
207
     * potential conflicts. In case when a user attempts to save the record upon some staled data
208
     * (because another user has modified the data), a [[StaleObjectException]] exception will be thrown,
209
     * and the update or deletion is skipped.
210
     *
211
     * Optimistic locking is only supported by [[update()]] and [[delete()]].
212
     *
213
     * To use Optimistic locking:
214
     *
215
     * 1. Create a column to store the version number of each row. The column type should be `BIGINT DEFAULT 0`.
216
     *    Override this method to return the name of this column.
217
     * 2. Add a `required` validation rule for the version column to ensure the version value is submitted.
218
     * 3. In the Web form that collects the user input, add a hidden field that stores
219
     *    the lock version of the recording being updated.
220
     * 4. In the controller action that does the data updating, try to catch the [[StaleObjectException]]
221
     *    and implement necessary business logic (e.g. merging the changes, prompting stated data)
222
     *    to resolve the conflict.
223
     *
224
     * @return string the column name that stores the lock version of a table row.
225
     * If `null` is returned (default implemented), optimistic locking will not be supported.
226
     */
227 24
    public function optimisticLock()
228
    {
229 24
        return null;
230
    }
231
232
    /**
233
     * @inheritdoc
234
     */
235 3
    public function canGetProperty($name, $checkVars = true, $checkBehaviors = true)
236
    {
237 3
        if (parent::canGetProperty($name, $checkVars, $checkBehaviors)) {
238 3
            return true;
239
        }
240
241
        try {
242 3
            return $this->hasAttribute($name);
243
        } catch (\Exception $e) {
244
            // `hasAttribute()` may fail on base/abstract classes in case automatic attribute list fetching used
245
            return false;
246
        }
247
    }
248
249
    /**
250
     * @inheritdoc
251
     */
252 9
    public function canSetProperty($name, $checkVars = true, $checkBehaviors = true)
253
    {
254 9
        if (parent::canSetProperty($name, $checkVars, $checkBehaviors)) {
255 6
            return true;
256
        }
257
258
        try {
259 3
            return $this->hasAttribute($name);
260
        } catch (\Exception $e) {
261
            // `hasAttribute()` may fail on base/abstract classes in case automatic attribute list fetching used
262
            return false;
263
        }
264
    }
265
266
    /**
267
     * PHP getter magic method.
268
     * This method is overridden so that attributes and related objects can be accessed like properties.
269
     *
270
     * @param string $name property name
271
     * @throws \yii\base\InvalidParamException if relation name is wrong
272
     * @return mixed property value
273
     * @see getAttribute()
274
     */
275 293
    public function __get($name)
276
    {
277 293
        if (isset($this->_attributes[$name]) || array_key_exists($name, $this->_attributes)) {
278 281
            return $this->_attributes[$name];
279 151
        } elseif ($this->hasAttribute($name)) {
280 34
            return null;
281
        } else {
282 129
            if (isset($this->_related[$name]) || array_key_exists($name, $this->_related)) {
283 78
                return $this->_related[$name];
284
            }
285 84
            $value = parent::__get($name);
286 84
            if ($value instanceof ActiveQueryInterface) {
287 52
                return $this->_related[$name] = $value->findFor($name, $this);
288
            } else {
289 38
                return $value;
290
            }
291
        }
292
    }
293
294
    /**
295
     * PHP setter magic method.
296
     * This method is overridden so that AR attributes can be accessed like properties.
297
     * @param string $name property name
298
     * @param mixed $value property value
299
     */
300 138
    public function __set($name, $value)
301
    {
302 138
        if ($this->hasAttribute($name)) {
303 138
            $this->_attributes[$name] = $value;
304
        } else {
305 4
            parent::__set($name, $value);
306
        }
307 138
    }
308
309
    /**
310
     * Checks if a property value is null.
311
     * This method overrides the parent implementation by checking if the named attribute is `null` or not.
312
     * @param string $name the property name or the event name
313
     * @return bool whether the property value is null
314
     */
315 45
    public function __isset($name)
316
    {
317
        try {
318 45
            return $this->__get($name) !== null;
319
        } catch (\Exception $e) {
320
            return false;
321
        }
322
    }
323
324
    /**
325
     * Sets a component property to be null.
326
     * This method overrides the parent implementation by clearing
327
     * the specified attribute value.
328
     * @param string $name the property name or the event name
329
     */
330 9
    public function __unset($name)
331
    {
332 9
        if ($this->hasAttribute($name)) {
333 3
            unset($this->_attributes[$name]);
334 6
        } elseif (array_key_exists($name, $this->_related)) {
335 6
            unset($this->_related[$name]);
336
        } elseif ($this->getRelation($name, false) === null) {
337
            parent::__unset($name);
338
        }
339 9
    }
340
341
    /**
342
     * Declares a `has-one` relation.
343
     * The declaration is returned in terms of a relational [[ActiveQuery]] instance
344
     * through which the related record can be queried and retrieved back.
345
     *
346
     * A `has-one` relation means that there is at most one related record matching
347
     * the criteria set by this relation, e.g., a customer has one country.
348
     *
349
     * For example, to declare the `country` relation for `Customer` class, we can write
350
     * the following code in the `Customer` class:
351
     *
352
     * ```php
353
     * public function getCountry()
354
     * {
355
     *     return $this->hasOne(Country::className(), ['id' => 'country_id']);
356
     * }
357
     * ```
358
     *
359
     * Note that in the above, the 'id' key in the `$link` parameter refers to an attribute name
360
     * in the related class `Country`, while the 'country_id' value refers to an attribute name
361
     * in the current AR class.
362
     *
363
     * Call methods declared in [[ActiveQuery]] to further customize the relation.
364
     *
365
     * @param string $class the class name of the related record
366
     * @param array $link the primary-foreign key constraint. The keys of the array refer to
367
     * the attributes of the record associated with the `$class` model, while the values of the
368
     * array refer to the corresponding attributes in **this** AR class.
369
     * @return ActiveQueryInterface the relational query object.
370
     */
371 43
    public function hasOne($class, $link)
372
    {
373 43
        return $this->createRelationQuery($class, $link, false);
374
    }
375
376
    /**
377
     * Declares a `has-many` relation.
378
     * The declaration is returned in terms of a relational [[ActiveQuery]] instance
379
     * through which the related record can be queried and retrieved back.
380
     *
381
     * A `has-many` relation means that there are multiple related records matching
382
     * the criteria set by this relation, e.g., a customer has many orders.
383
     *
384
     * For example, to declare the `orders` relation for `Customer` class, we can write
385
     * the following code in the `Customer` class:
386
     *
387
     * ```php
388
     * public function getOrders()
389
     * {
390
     *     return $this->hasMany(Order::className(), ['customer_id' => 'id']);
391
     * }
392
     * ```
393
     *
394
     * Note that in the above, the 'customer_id' key in the `$link` parameter refers to
395
     * an attribute name in the related class `Order`, while the 'id' value refers to
396
     * an attribute name in the current AR class.
397
     *
398
     * Call methods declared in [[ActiveQuery]] to further customize the relation.
399
     *
400
     * @param string $class the class name of the related record
401
     * @param array $link the primary-foreign key constraint. The keys of the array refer to
402
     * the attributes of the record associated with the `$class` model, while the values of the
403
     * array refer to the corresponding attributes in **this** AR class.
404
     * @return ActiveQueryInterface the relational query object.
405
     */
406 132
    public function hasMany($class, $link)
407
    {
408 132
        return $this->createRelationQuery($class, $link, true);
409
    }
410
411
    /**
412
     * Creates a query instance for `has-one` or `has-many` relation.
413
     * @param string $class the class name of the related record.
414
     * @param array $link the primary-foreign key constraint.
415
     * @param boolean $multiple whether this query represents a relation to more than one record.
416
     * @return ActiveQueryInterface the relational query object.
417
     * @since 2.0.12
418
     * @see hasOne()
419
     * @see hasMany()
420
     */
421 136
    protected function createRelationQuery($class, $link, $multiple)
422
    {
423
        /* @var $class ActiveRecordInterface */
424
        /* @var $query ActiveQuery */
425 136
        $query = $class::find();
426 136
        $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...
427 136
        $query->link = $link;
428 136
        $query->multiple = $multiple;
429 136
        return $query;
430
    }
431
432
    /**
433
     * Populates the named relation with the related records.
434
     * Note that this method does not check if the relation exists or not.
435
     * @param string $name the relation name, e.g. `orders` for a relation defined via `getOrders()` method (case-sensitive).
436
     * @param ActiveRecordInterface|array|null $records the related records to be populated into the relation.
437
     * @see getRelation()
438
     */
439 96
    public function populateRelation($name, $records)
440
    {
441 96
        $this->_related[$name] = $records;
442 96
    }
443
444
    /**
445
     * Check whether the named relation has been populated with records.
446
     * @param string $name the relation name, e.g. `orders` for a relation defined via `getOrders()` method (case-sensitive).
447
     * @return bool whether relation has been populated with records.
448
     * @see getRelation()
449
     */
450 45
    public function isRelationPopulated($name)
451
    {
452 45
        return array_key_exists($name, $this->_related);
453
    }
454
455
    /**
456
     * Returns all populated related records.
457
     * @return array an array of related records indexed by relation names.
458
     * @see getRelation()
459
     */
460 6
    public function getRelatedRecords()
461
    {
462 6
        return $this->_related;
463
    }
464
465
    /**
466
     * Returns a value indicating whether the model has an attribute with the specified name.
467
     * @param string $name the name of the attribute
468
     * @return bool whether the model has an attribute with the specified name.
469
     */
470 235
    public function hasAttribute($name)
471
    {
472 235
        return isset($this->_attributes[$name]) || in_array($name, $this->attributes(), true);
473
    }
474
475
    /**
476
     * Returns the named attribute value.
477
     * If this record is the result of a query and the attribute is not loaded,
478
     * `null` will be returned.
479
     * @param string $name the attribute name
480
     * @return mixed the attribute value. `null` if the attribute is not set or does not exist.
481
     * @see hasAttribute()
482
     */
483
    public function getAttribute($name)
484
    {
485
        return isset($this->_attributes[$name]) ? $this->_attributes[$name] : null;
486
    }
487
488
    /**
489
     * Sets the named attribute value.
490
     * @param string $name the attribute name
491
     * @param mixed $value the attribute value.
492
     * @throws InvalidParamException if the named attribute does not exist.
493
     * @see hasAttribute()
494
     */
495 64
    public function setAttribute($name, $value)
496
    {
497 64
        if ($this->hasAttribute($name)) {
498 64
            $this->_attributes[$name] = $value;
499
        } else {
500
            throw new InvalidParamException(get_class($this) . ' has no attribute named "' . $name . '".');
501
        }
502 64
    }
503
504
    /**
505
     * Returns the old attribute values.
506
     * @return array the old attribute values (name-value pairs)
507
     */
508
    public function getOldAttributes()
509
    {
510
        return $this->_oldAttributes === null ? [] : $this->_oldAttributes;
511
    }
512
513
    /**
514
     * Sets the old attribute values.
515
     * All existing old attribute values will be discarded.
516
     * @param array|null $values old attribute values to be set.
517
     * If set to `null` this record is considered to be [[isNewRecord|new]].
518
     */
519 81
    public function setOldAttributes($values)
520
    {
521 81
        $this->_oldAttributes = $values;
522 81
    }
523
524
    /**
525
     * Returns the old value of the named attribute.
526
     * If this record is the result of a query and the attribute is not loaded,
527
     * `null` will be returned.
528
     * @param string $name the attribute name
529
     * @return mixed the old attribute value. `null` if the attribute is not loaded before
530
     * or does not exist.
531
     * @see hasAttribute()
532
     */
533
    public function getOldAttribute($name)
534
    {
535
        return isset($this->_oldAttributes[$name]) ? $this->_oldAttributes[$name] : null;
536
    }
537
538
    /**
539
     * Sets the old value of the named attribute.
540
     * @param string $name the attribute name
541
     * @param mixed $value the old attribute value.
542
     * @throws InvalidParamException if the named attribute does not exist.
543
     * @see hasAttribute()
544
     */
545
    public function setOldAttribute($name, $value)
546
    {
547
        if (isset($this->_oldAttributes[$name]) || $this->hasAttribute($name)) {
548
            $this->_oldAttributes[$name] = $value;
549
        } else {
550
            throw new InvalidParamException(get_class($this) . ' has no attribute named "' . $name . '".');
551
        }
552
    }
553
554
    /**
555
     * Marks an attribute dirty.
556
     * This method may be called to force updating a record when calling [[update()]],
557
     * even if there is no change being made to the record.
558
     * @param string $name the attribute name
559
     */
560
    public function markAttributeDirty($name)
561
    {
562
        unset($this->_oldAttributes[$name]);
563
    }
564
565
    /**
566
     * Returns a value indicating whether the named attribute has been changed.
567
     * @param string $name the name of the attribute.
568
     * @param bool $identical whether the comparison of new and old value is made for
569
     * identical values using `===`, defaults to `true`. Otherwise `==` is used for comparison.
570
     * This parameter is available since version 2.0.4.
571
     * @return bool whether the attribute has been changed
572
     */
573 1
    public function isAttributeChanged($name, $identical = true)
574
    {
575 1
        if (isset($this->_attributes[$name], $this->_oldAttributes[$name])) {
576 1
            if ($identical) {
577 1
                return $this->_attributes[$name] !== $this->_oldAttributes[$name];
578
            } else {
579
                return $this->_attributes[$name] != $this->_oldAttributes[$name];
580
            }
581
        } else {
582
            return isset($this->_attributes[$name]) || isset($this->_oldAttributes[$name]);
583
        }
584
    }
585
586
    /**
587
     * Returns the attribute values that have been modified since they are loaded or saved most recently.
588
     *
589
     * The comparison of new and old values is made for identical values using `===`.
590
     *
591
     * @param string[]|null $names the names of the attributes whose values may be returned if they are
592
     * changed recently. If null, [[attributes()]] will be used.
593
     * @return array the changed attribute values (name-value pairs)
594
     */
595 91
    public function getDirtyAttributes($names = null)
596
    {
597 91
        if ($names === null) {
598 88
            $names = $this->attributes();
599
        }
600 91
        $names = array_flip($names);
601 91
        $attributes = [];
602 91
        if ($this->_oldAttributes === null) {
603 78
            foreach ($this->_attributes as $name => $value) {
604 75
                if (isset($names[$name])) {
605 78
                    $attributes[$name] = $value;
606
                }
607
            }
608
        } else {
609 30
            foreach ($this->_attributes as $name => $value) {
610 30
                if (isset($names[$name]) && (!array_key_exists($name, $this->_oldAttributes) || $value !== $this->_oldAttributes[$name])) {
611 27
                    $attributes[$name] = $value;
612
                }
613
            }
614
        }
615 91
        return $attributes;
616
    }
617
618
    /**
619
     * Saves the current record.
620
     *
621
     * This method will call [[insert()]] when [[isNewRecord]] is `true`, or [[update()]]
622
     * when [[isNewRecord]] is `false`.
623
     *
624
     * For example, to save a customer record:
625
     *
626
     * ```php
627
     * $customer = new Customer; // or $customer = Customer::findOne($id);
628
     * $customer->name = $name;
629
     * $customer->email = $email;
630
     * $customer->save();
631
     * ```
632
     *
633
     * @param bool $runValidation whether to perform validation (calling [[validate()]])
634
     * before saving the record. Defaults to `true`. If the validation fails, the record
635
     * will not be saved to the database and this method will return `false`.
636
     * @param array $attributeNames list of attribute names that need to be saved. Defaults to null,
637
     * meaning all attributes that are loaded from DB will be saved.
638
     * @return bool whether the saving succeeded (i.e. no validation errors occurred).
639
     */
640 88
    public function save($runValidation = true, $attributeNames = null)
641
    {
642 88
        if ($this->getIsNewRecord()) {
643 75
            return $this->insert($runValidation, $attributeNames);
644
        } else {
645 26
            return $this->update($runValidation, $attributeNames) !== false;
646
        }
647
    }
648
649
    /**
650
     * Saves the changes to this active record into the associated database table.
651
     *
652
     * This method performs the following steps in order:
653
     *
654
     * 1. call [[beforeValidate()]] when `$runValidation` is `true`. If [[beforeValidate()]]
655
     *    returns `false`, the rest of the steps will be skipped;
656
     * 2. call [[afterValidate()]] when `$runValidation` is `true`. If validation
657
     *    failed, the rest of the steps will be skipped;
658
     * 3. call [[beforeSave()]]. If [[beforeSave()]] returns `false`,
659
     *    the rest of the steps will be skipped;
660
     * 4. save the record into database. If this fails, it will skip the rest of the steps;
661
     * 5. call [[afterSave()]];
662
     *
663
     * In the above step 1, 2, 3 and 5, events [[EVENT_BEFORE_VALIDATE]],
664
     * [[EVENT_AFTER_VALIDATE]], [[EVENT_BEFORE_UPDATE]], and [[EVENT_AFTER_UPDATE]]
665
     * will be raised by the corresponding methods.
666
     *
667
     * Only the [[dirtyAttributes|changed attribute values]] will be saved into database.
668
     *
669
     * For example, to update a customer record:
670
     *
671
     * ```php
672
     * $customer = Customer::findOne($id);
673
     * $customer->name = $name;
674
     * $customer->email = $email;
675
     * $customer->update();
676
     * ```
677
     *
678
     * Note that it is possible the update does not affect any row in the table.
679
     * In this case, this method will return 0. For this reason, you should use the following
680
     * code to check if update() is successful or not:
681
     *
682
     * ```php
683
     * if ($customer->update() !== false) {
684
     *     // update successful
685
     * } else {
686
     *     // update failed
687
     * }
688
     * ```
689
     *
690
     * @param bool $runValidation whether to perform validation (calling [[validate()]])
691
     * before saving the record. Defaults to `true`. If the validation fails, the record
692
     * will not be saved to the database and this method will return `false`.
693
     * @param array $attributeNames list of attribute names that need to be saved. Defaults to null,
694
     * meaning all attributes that are loaded from DB will be saved.
695
     * @return int|false the number of rows affected, or `false` if validation fails
696
     * or [[beforeSave()]] stops the updating process.
697
     * @throws StaleObjectException if [[optimisticLock|optimistic locking]] is enabled and the data
698
     * being updated is outdated.
699
     * @throws Exception in case update failed.
700
     */
701
    public function update($runValidation = true, $attributeNames = null)
702
    {
703
        if ($runValidation && !$this->validate($attributeNames)) {
704
            return false;
705
        }
706
        return $this->updateInternal($attributeNames);
707
    }
708
709
    /**
710
     * Updates the specified attributes.
711
     *
712
     * This method is a shortcut to [[update()]] when data validation is not needed
713
     * and only a small set attributes need to be updated.
714
     *
715
     * You may specify the attributes to be updated as name list or name-value pairs.
716
     * If the latter, the corresponding attribute values will be modified accordingly.
717
     * The method will then save the specified attributes into database.
718
     *
719
     * Note that this method will **not** perform data validation and will **not** trigger events.
720
     *
721
     * @param array $attributes the attributes (names or name-value pairs) to be updated
722
     * @return int the number of rows affected.
723
     */
724 4
    public function updateAttributes($attributes)
725
    {
726 4
        $attrs = [];
727 4
        foreach ($attributes as $name => $value) {
728 4
            if (is_int($name)) {
729
                $attrs[] = $value;
730
            } else {
731 4
                $this->$name = $value;
732 4
                $attrs[] = $name;
733
            }
734
        }
735
736 4
        $values = $this->getDirtyAttributes($attrs);
737 4
        if (empty($values) || $this->getIsNewRecord()) {
738 4
            return 0;
739
        }
740
741 3
        $rows = static::updateAll($values, $this->getOldPrimaryKey(true));
742
743 3
        foreach ($values as $name => $value) {
744 3
            $this->_oldAttributes[$name] = $this->_attributes[$name];
745
        }
746
747 3
        return $rows;
748
    }
749
750
    /**
751
     * @see update()
752
     * @param array $attributes attributes to update
753
     * @return int|false the number of rows affected, or false if [[beforeSave()]] stops the updating process.
754
     * @throws StaleObjectException
755
     */
756 26
    protected function updateInternal($attributes = null)
757
    {
758 26
        if (!$this->beforeSave(false)) {
759
            return false;
760
        }
761 26
        $values = $this->getDirtyAttributes($attributes);
0 ignored issues
show
Bug introduced by
It seems like $attributes defined by parameter $attributes on line 756 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...
762 26
        if (empty($values)) {
763 3
            $this->afterSave(false, $values);
764 3
            return 0;
765
        }
766 24
        $condition = $this->getOldPrimaryKey(true);
767 24
        $lock = $this->optimisticLock();
768 24
        if ($lock !== null) {
769 3
            $values[$lock] = $this->$lock + 1;
770 3
            $condition[$lock] = $this->$lock;
771
        }
772
        // We do not check the return value of updateAll() because it's possible
773
        // that the UPDATE statement doesn't change anything and thus returns 0.
774 24
        $rows = static::updateAll($values, $condition);
775
776 24
        if ($lock !== null && !$rows) {
777 3
            throw new StaleObjectException('The object being updated is outdated.');
778
        }
779
780 24
        if (isset($values[$lock])) {
781 3
            $this->$lock = $values[$lock];
782
        }
783
784 24
        $changedAttributes = [];
785 24
        foreach ($values as $name => $value) {
786 24
            $changedAttributes[$name] = isset($this->_oldAttributes[$name]) ? $this->_oldAttributes[$name] : null;
787 24
            $this->_oldAttributes[$name] = $value;
788
        }
789 24
        $this->afterSave(false, $changedAttributes);
790
791 24
        return $rows;
792
    }
793
794
    /**
795
     * Updates one or several counter columns for the current AR object.
796
     * Note that this method differs from [[updateAllCounters()]] in that it only
797
     * saves counters for the current AR object.
798
     *
799
     * An example usage is as follows:
800
     *
801
     * ```php
802
     * $post = Post::findOne($id);
803
     * $post->updateCounters(['view_count' => 1]);
804
     * ```
805
     *
806
     * @param array $counters the counters to be updated (attribute name => increment value)
807
     * Use negative values if you want to decrement the counters.
808
     * @return bool whether the saving is successful
809
     * @see updateAllCounters()
810
     */
811 6
    public function updateCounters($counters)
812
    {
813 6
        if (static::updateAllCounters($counters, $this->getOldPrimaryKey(true)) > 0) {
814 6
            foreach ($counters as $name => $value) {
815 6
                if (!isset($this->_attributes[$name])) {
816 3
                    $this->_attributes[$name] = $value;
817
                } else {
818 3
                    $this->_attributes[$name] += $value;
819
                }
820 6
                $this->_oldAttributes[$name] = $this->_attributes[$name];
821
            }
822 6
            return true;
823
        } else {
824
            return false;
825
        }
826
    }
827
828
    /**
829
     * Deletes the table row corresponding to this active record.
830
     *
831
     * This method performs the following steps in order:
832
     *
833
     * 1. call [[beforeDelete()]]. If the method returns `false`, it will skip the
834
     *    rest of the steps;
835
     * 2. delete the record from the database;
836
     * 3. call [[afterDelete()]].
837
     *
838
     * In the above step 1 and 3, events named [[EVENT_BEFORE_DELETE]] and [[EVENT_AFTER_DELETE]]
839
     * will be raised by the corresponding methods.
840
     *
841
     * @return int|false the number of rows deleted, or `false` if the deletion is unsuccessful for some reason.
842
     * Note that it is possible the number of rows deleted is 0, even though the deletion execution is successful.
843
     * @throws StaleObjectException if [[optimisticLock|optimistic locking]] is enabled and the data
844
     * being deleted is outdated.
845
     * @throws Exception in case delete failed.
846
     */
847
    public function delete()
848
    {
849
        $result = false;
850
        if ($this->beforeDelete()) {
851
            // we do not check the return value of deleteAll() because it's possible
852
            // the record is already deleted in the database and thus the method will return 0
853
            $condition = $this->getOldPrimaryKey(true);
854
            $lock = $this->optimisticLock();
855
            if ($lock !== null) {
856
                $condition[$lock] = $this->$lock;
857
            }
858
            $result = static::deleteAll($condition);
859
            if ($lock !== null && !$result) {
860
                throw new StaleObjectException('The object being deleted is outdated.');
861
            }
862
            $this->_oldAttributes = null;
863
            $this->afterDelete();
864
        }
865
866
        return $result;
867
    }
868
869
    /**
870
     * Returns a value indicating whether the current record is new.
871
     * @return bool whether the record is new and should be inserted when calling [[save()]].
872
     */
873 122
    public function getIsNewRecord()
874
    {
875 122
        return $this->_oldAttributes === null;
876
    }
877
878
    /**
879
     * Sets the value indicating whether the record is new.
880
     * @param bool $value whether the record is new and should be inserted when calling [[save()]].
881
     * @see getIsNewRecord()
882
     */
883
    public function setIsNewRecord($value)
884
    {
885
        $this->_oldAttributes = $value ? null : $this->_attributes;
886
    }
887
888
    /**
889
     * Initializes the object.
890
     * This method is called at the end of the constructor.
891
     * The default implementation will trigger an [[EVENT_INIT]] event.
892
     * If you override this method, make sure you call the parent implementation at the end
893
     * to ensure triggering of the event.
894
     */
895 326
    public function init()
896
    {
897 326
        parent::init();
898 326
        $this->trigger(self::EVENT_INIT);
899 326
    }
900
901
    /**
902
     * This method is called when the AR object is created and populated with the query result.
903
     * The default implementation will trigger an [[EVENT_AFTER_FIND]] event.
904
     * When overriding this method, make sure you call the parent implementation to ensure the
905
     * event is triggered.
906
     */
907 258
    public function afterFind()
908
    {
909 258
        $this->trigger(self::EVENT_AFTER_FIND);
910 258
    }
911
912
    /**
913
     * This method is called at the beginning of inserting or updating a record.
914
     * The default implementation will trigger an [[EVENT_BEFORE_INSERT]] event when `$insert` is `true`,
915
     * or an [[EVENT_BEFORE_UPDATE]] event if `$insert` is `false`.
916
     * When overriding this method, make sure you call the parent implementation like the following:
917
     *
918
     * ```php
919
     * public function beforeSave($insert)
920
     * {
921
     *     if (!parent::beforeSave($insert)) {
922
     *         return false;
923
     *     }
924
     *
925
     *     // ...custom code here...
926
     *     return true;
927
     * }
928
     * ```
929
     *
930
     * @param bool $insert whether this method called while inserting a record.
931
     * If `false`, it means the method is called while updating a record.
932
     * @return bool whether the insertion or updating should continue.
933
     * If `false`, the insertion or updating will be cancelled.
934
     */
935 93
    public function beforeSave($insert)
936
    {
937 93
        $event = new ModelEvent;
938 93
        $this->trigger($insert ? self::EVENT_BEFORE_INSERT : self::EVENT_BEFORE_UPDATE, $event);
939
940 93
        return $event->isValid;
941
    }
942
943
    /**
944
     * This method is called at the end of inserting or updating a record.
945
     * The default implementation will trigger an [[EVENT_AFTER_INSERT]] event when `$insert` is `true`,
946
     * or an [[EVENT_AFTER_UPDATE]] event if `$insert` is `false`. The event class used is [[AfterSaveEvent]].
947
     * When overriding this method, make sure you call the parent implementation so that
948
     * the event is triggered.
949
     * @param bool $insert whether this method called while inserting a record.
950
     * If `false`, it means the method is called while updating a record.
951
     * @param array $changedAttributes The old values of attributes that had changed and were saved.
952
     * You can use this parameter to take action based on the changes made for example send an email
953
     * when the password had changed or implement audit trail that tracks all the changes.
954
     * `$changedAttributes` gives you the old attribute values while the active record (`$this`) has
955
     * already the new, updated values.
956
     *
957
     * Note that no automatic type conversion performed by default. You may use
958
     * [[\yii\behaviors\AttributeTypecastBehavior]] to facilitate attribute typecasting.
959
     * See http://www.yiiframework.com/doc-2.0/guide-db-active-record.html#attributes-typecasting.
960
     */
961 88
    public function afterSave($insert, $changedAttributes)
962
    {
963 88
        $this->trigger($insert ? self::EVENT_AFTER_INSERT : self::EVENT_AFTER_UPDATE, new AfterSaveEvent([
964 88
            'changedAttributes' => $changedAttributes,
965
        ]));
966 88
    }
967
968
    /**
969
     * This method is invoked before deleting a record.
970
     * The default implementation raises the [[EVENT_BEFORE_DELETE]] event.
971
     * When overriding this method, make sure you call the parent implementation like the following:
972
     *
973
     * ```php
974
     * public function beforeDelete()
975
     * {
976
     *     if (!parent::beforeDelete()) {
977
     *         return false;
978
     *     }
979
     *
980
     *     // ...custom code here...
981
     *     return true;
982
     * }
983
     * ```
984
     *
985
     * @return bool whether the record should be deleted. Defaults to `true`.
986
     */
987 6
    public function beforeDelete()
988
    {
989 6
        $event = new ModelEvent;
990 6
        $this->trigger(self::EVENT_BEFORE_DELETE, $event);
991
992 6
        return $event->isValid;
993
    }
994
995
    /**
996
     * This method is invoked after deleting a record.
997
     * The default implementation raises the [[EVENT_AFTER_DELETE]] event.
998
     * You may override this method to do postprocessing after the record is deleted.
999
     * Make sure you call the parent implementation so that the event is raised properly.
1000
     */
1001 6
    public function afterDelete()
1002
    {
1003 6
        $this->trigger(self::EVENT_AFTER_DELETE);
1004 6
    }
1005
1006
    /**
1007
     * Repopulates this active record with the latest data.
1008
     *
1009
     * If the refresh is successful, an [[EVENT_AFTER_REFRESH]] event will be triggered.
1010
     * This event is available since version 2.0.8.
1011
     *
1012
     * @return bool whether the row still exists in the database. If `true`, the latest data
1013
     * will be populated to this active record. Otherwise, this record will remain unchanged.
1014
     */
1015 19
    public function refresh()
1016
    {
1017
        /* @var $record BaseActiveRecord */
1018 19
        $record = static::findOne($this->getPrimaryKey(true));
1019 19
        if ($record === null) {
1020 3
            return false;
1021
        }
1022 19
        foreach ($this->attributes() as $name) {
1023 19
            $this->_attributes[$name] = isset($record->_attributes[$name]) ? $record->_attributes[$name] : null;
1024
        }
1025 19
        $this->_oldAttributes = $record->_oldAttributes;
1026 19
        $this->_related = [];
1027 19
        $this->afterRefresh();
1028
1029 19
        return true;
1030
    }
1031
1032
    /**
1033
     * This method is called when the AR object is refreshed.
1034
     * The default implementation will trigger an [[EVENT_AFTER_REFRESH]] event.
1035
     * When overriding this method, make sure you call the parent implementation to ensure the
1036
     * event is triggered.
1037
     * @since 2.0.8
1038
     */
1039 19
    public function afterRefresh()
1040
    {
1041 19
        $this->trigger(self::EVENT_AFTER_REFRESH);
1042 19
    }
1043
1044
    /**
1045
     * Returns a value indicating whether the given active record is the same as the current one.
1046
     * The comparison is made by comparing the table names and the primary key values of the two active records.
1047
     * If one of the records [[isNewRecord|is new]] they are also considered not equal.
1048
     * @param ActiveRecordInterface $record record to compare to
1049
     * @return bool whether the two active records refer to the same row in the same database table.
1050
     */
1051
    public function equals($record)
1052
    {
1053
        if ($this->getIsNewRecord() || $record->getIsNewRecord()) {
1054
            return false;
1055
        }
1056
1057
        return get_class($this) === get_class($record) && $this->getPrimaryKey() === $record->getPrimaryKey();
1058
    }
1059
1060
    /**
1061
     * Returns the primary key value(s).
1062
     * @param bool $asArray whether to return the primary key value as an array. If `true`,
1063
     * the return value will be an array with column names as keys and column values as values.
1064
     * Note that for composite primary keys, an array will always be returned regardless of this parameter value.
1065
     * @property mixed The primary key value. An array (column name => column value) is returned if
1066
     * the primary key is composite. A string is returned otherwise (null will be returned if
1067
     * the key value is null).
1068
     * @return mixed the primary key value. An array (column name => column value) is returned if the primary key
1069
     * is composite or `$asArray` is `true`. A string is returned otherwise (null will be returned if
1070
     * the key value is null).
1071
     */
1072 35
    public function getPrimaryKey($asArray = false)
1073
    {
1074 35
        $keys = $this->primaryKey();
1075 35
        if (!$asArray && count($keys) === 1) {
1076 19
            return isset($this->_attributes[$keys[0]]) ? $this->_attributes[$keys[0]] : null;
1077
        } else {
1078 19
            $values = [];
1079 19
            foreach ($keys as $name) {
1080 19
                $values[$name] = isset($this->_attributes[$name]) ? $this->_attributes[$name] : null;
1081
            }
1082
1083 19
            return $values;
1084
        }
1085
    }
1086
1087
    /**
1088
     * Returns the old primary key value(s).
1089
     * This refers to the primary key value that is populated into the record
1090
     * after executing a find method (e.g. find(), findOne()).
1091
     * The value remains unchanged even if the primary key attribute is manually assigned with a different value.
1092
     * @param bool $asArray whether to return the primary key value as an array. If `true`,
1093
     * the return value will be an array with column name as key and column value as value.
1094
     * If this is `false` (default), a scalar value will be returned for non-composite primary key.
1095
     * @property mixed The old primary key value. An array (column name => column value) is
1096
     * returned if the primary key is composite. A string is returned otherwise (null will be
1097
     * returned if the key value is null).
1098
     * @return mixed the old primary key value. An array (column name => column value) is returned if the primary key
1099
     * is composite or `$asArray` is `true`. A string is returned otherwise (null will be returned if
1100
     * the key value is null).
1101
     * @throws Exception if the AR model does not have a primary key
1102
     */
1103 51
    public function getOldPrimaryKey($asArray = false)
1104
    {
1105 51
        $keys = $this->primaryKey();
1106 51
        if (empty($keys)) {
1107
            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.');
1108
        }
1109 51
        if (!$asArray && count($keys) === 1) {
1110
            return isset($this->_oldAttributes[$keys[0]]) ? $this->_oldAttributes[$keys[0]] : null;
1111
        } else {
1112 51
            $values = [];
1113 51
            foreach ($keys as $name) {
1114 51
                $values[$name] = isset($this->_oldAttributes[$name]) ? $this->_oldAttributes[$name] : null;
1115
            }
1116
1117 51
            return $values;
1118
        }
1119
    }
1120
1121
    /**
1122
     * Populates an active record object using a row of data from the database/storage.
1123
     *
1124
     * This is an internal method meant to be called to create active record objects after
1125
     * fetching data from the database. It is mainly used by [[ActiveQuery]] to populate
1126
     * the query results into active records.
1127
     *
1128
     * When calling this method manually you should call [[afterFind()]] on the created
1129
     * record to trigger the [[EVENT_AFTER_FIND|afterFind Event]].
1130
     *
1131
     * @param BaseActiveRecord $record the record to be populated. In most cases this will be an instance
1132
     * created by [[instantiate()]] beforehand.
1133
     * @param array $row attribute values (name => value)
1134
     */
1135 258
    public static function populateRecord($record, $row)
1136
    {
1137 258
        $columns = array_flip($record->attributes());
1138 258
        foreach ($row as $name => $value) {
1139 258
            if (isset($columns[$name])) {
1140 258
                $record->_attributes[$name] = $value;
1141 6
            } elseif ($record->canSetProperty($name)) {
1142 6
                $record->$name = $value;
1143
            }
1144
        }
1145 258
        $record->_oldAttributes = $record->_attributes;
1146 258
    }
1147
1148
    /**
1149
     * Creates an active record instance.
1150
     *
1151
     * This method is called together with [[populateRecord()]] by [[ActiveQuery]].
1152
     * It is not meant to be used for creating new records directly.
1153
     *
1154
     * You may override this method if the instance being created
1155
     * depends on the row data to be populated into the record.
1156
     * For example, by creating a record based on the value of a column,
1157
     * you may implement the so-called single-table inheritance mapping.
1158
     * @param array $row row data to be populated into the record.
1159
     * @return static the newly created active record
1160
     */
1161 255
    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...
1162
    {
1163 255
        return new static;
1164
    }
1165
1166
    /**
1167
     * Returns whether there is an element at the specified offset.
1168
     * This method is required by the interface [[\ArrayAccess]].
1169
     * @param mixed $offset the offset to check on
1170
     * @return bool whether there is an element at the specified offset.
1171
     */
1172 24
    public function offsetExists($offset)
1173
    {
1174 24
        return $this->__isset($offset);
1175
    }
1176
1177
    /**
1178
     * Returns the relation object with the specified name.
1179
     * A relation is defined by a getter method which returns an [[ActiveQueryInterface]] object.
1180
     * It can be declared in either the Active Record class itself or one of its behaviors.
1181
     * @param string $name the relation name, e.g. `orders` for a relation defined via `getOrders()` method (case-sensitive).
1182
     * @param bool $throwException whether to throw exception if the relation does not exist.
1183
     * @return ActiveQueryInterface|ActiveQuery the relational query object. If the relation does not exist
1184
     * and `$throwException` is `false`, `null` will be returned.
1185
     * @throws InvalidParamException if the named relation does not exist.
1186
     */
1187 123
    public function getRelation($name, $throwException = true)
1188
    {
1189 123
        $getter = 'get' . $name;
1190
        try {
1191
            // the relation could be defined in a behavior
1192 123
            $relation = $this->$getter();
1193
        } catch (UnknownMethodException $e) {
1194
            if ($throwException) {
1195
                throw new InvalidParamException(get_class($this) . ' has no relation named "' . $name . '".', 0, $e);
1196
            } else {
1197
                return null;
1198
            }
1199
        }
1200 123
        if (!$relation instanceof ActiveQueryInterface) {
1201
            if ($throwException) {
1202
                throw new InvalidParamException(get_class($this) . ' has no relation named "' . $name . '".');
1203
            } else {
1204
                return null;
1205
            }
1206
        }
1207
1208 123
        if (method_exists($this, $getter)) {
1209
            // relation name is case sensitive, trying to validate it when the relation is defined within this class
1210 123
            $method = new \ReflectionMethod($this, $getter);
1211 123
            $realName = lcfirst(substr($method->getName(), 3));
1212 123
            if ($realName !== $name) {
1213
                if ($throwException) {
1214
                    throw new InvalidParamException('Relation names are case sensitive. ' . get_class($this) . " has a relation named \"$realName\" instead of \"$name\".");
1215
                } else {
1216
                    return null;
1217
                }
1218
            }
1219
        }
1220
1221 123
        return $relation;
1222
    }
1223
1224
    /**
1225
     * Establishes the relationship between two models.
1226
     *
1227
     * The relationship is established by setting the foreign key value(s) in one model
1228
     * to be the corresponding primary key value(s) in the other model.
1229
     * The model with the foreign key will be saved into database without performing validation.
1230
     *
1231
     * If the relationship involves a junction table, a new row will be inserted into the
1232
     * junction table which contains the primary key values from both models.
1233
     *
1234
     * Note that this method requires that the primary key value is not null.
1235
     *
1236
     * @param string $name the case sensitive name of the relationship, e.g. `orders` for a relation defined via `getOrders()` method.
1237
     * @param ActiveRecordInterface $model the model to be linked with the current one.
1238
     * @param array $extraColumns additional column values to be saved into the junction table.
1239
     * This parameter is only meaningful for a relationship involving a junction table
1240
     * (i.e., a relation set with [[ActiveRelationTrait::via()]] or [[ActiveQuery::viaTable()]].)
1241
     * @throws InvalidCallException if the method is unable to link two models.
1242
     */
1243 9
    public function link($name, $model, $extraColumns = [])
1244
    {
1245 9
        $relation = $this->getRelation($name);
1246
1247 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...
1248 3
            if ($this->getIsNewRecord() || $model->getIsNewRecord()) {
1249
                throw new InvalidCallException('Unable to link models: the models being linked cannot be newly created.');
1250
            }
1251 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...
1252
                /* @var $viaRelation ActiveQuery */
1253 3
                list($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...
1254 3
                $viaClass = $viaRelation->modelClass;
1255
                // unset $viaName so that it can be reloaded to reflect the change
1256 3
                unset($this->_related[$viaName]);
1257
            } else {
1258
                $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...
1259
                $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...
1260
            }
1261 3
            $columns = [];
1262 3
            foreach ($viaRelation->link as $a => $b) {
1263 3
                $columns[$a] = $this->$b;
1264
            }
1265 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...
1266 3
                $columns[$b] = $model->$a;
1267
            }
1268 3
            foreach ($extraColumns as $k => $v) {
1269 3
                $columns[$k] = $v;
1270
            }
1271 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...
1272
                /* @var $viaClass ActiveRecordInterface */
1273
                /* @var $record ActiveRecordInterface */
1274 3
                $record = new $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...
1275 3
                foreach ($columns as $column => $value) {
1276 3
                    $record->$column = $value;
1277
                }
1278 3
                $record->insert(false);
1279
            } else {
1280
                /* @var $viaTable string */
1281
                static::getDb()->createCommand()
1282
                    ->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...
1283
            }
1284
        } else {
1285 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...
1286 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...
1287 9
            if ($p1 && $p2) {
1288
                if ($this->getIsNewRecord() && $model->getIsNewRecord()) {
1289
                    throw new InvalidCallException('Unable to link models: at most one model can be newly created.');
1290
                } elseif ($this->getIsNewRecord()) {
1291
                    $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...
1292
                } else {
1293
                    $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...
1294
                }
1295 3
            } elseif ($p1) {
1296 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...
1297 9
            } elseif ($p2) {
1298 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...
1299
            } else {
1300
                throw new InvalidCallException('Unable to link models: the link defining the relation does not involve any primary key.');
1301
            }
1302
        }
1303
1304
        // update lazily loaded related objects
1305 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...
1306 3
            $this->_related[$name] = $model;
1307 9
        } elseif (isset($this->_related[$name])) {
1308 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...
1309 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...
1310 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...
1311
                } else {
1312 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...
1313
                }
1314 6
                $this->_related[$name][$index] = $model;
1315
            } else {
1316 3
                $this->_related[$name][] = $model;
1317
            }
1318
        }
1319 9
    }
1320
1321
    /**
1322
     * Destroys the relationship between two models.
1323
     *
1324
     * The model with the foreign key of the relationship will be deleted if `$delete` is `true`.
1325
     * Otherwise, the foreign key will be set `null` and the model will be saved without validation.
1326
     *
1327
     * @param string $name the case sensitive name of the relationship, e.g. `orders` for a relation defined via `getOrders()` method.
1328
     * @param ActiveRecordInterface $model the model to be unlinked from the current one.
1329
     * You have to make sure that the model is really related with the current model as this method
1330
     * does not check this.
1331
     * @param bool $delete whether to delete the model that contains the foreign key.
1332
     * If `false`, the model's foreign key will be set `null` and saved.
1333
     * If `true`, the model containing the foreign key will be deleted.
1334
     * @throws InvalidCallException if the models cannot be unlinked
1335
     */
1336 3
    public function unlink($name, $model, $delete = false)
1337
    {
1338 3
        $relation = $this->getRelation($name);
1339
1340 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...
1341 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...
1342
                /* @var $viaRelation ActiveQuery */
1343 3
                list($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...
1344 3
                $viaClass = $viaRelation->modelClass;
1345 3
                unset($this->_related[$viaName]);
1346
            } else {
1347 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...
1348 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...
1349
            }
1350 3
            $columns = [];
1351 3
            foreach ($viaRelation->link as $a => $b) {
1352 3
                $columns[$a] = $this->$b;
1353
            }
1354 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...
1355 3
                $columns[$b] = $model->$a;
1356
            }
1357 3
            $nulls = [];
1358 3
            foreach (array_keys($columns) as $a) {
1359 3
                $nulls[$a] = null;
1360
            }
1361 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...
1362
                /* @var $viaClass ActiveRecordInterface */
1363 3
                if ($delete) {
1364 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...
1365
                } else {
1366
                    $viaClass::updateAll($nulls, $columns);
1367
                }
1368
            } else {
1369
                /* @var $viaTable string */
1370
                /* @var $command Command */
1371 3
                $command = static::getDb()->createCommand();
1372 3
                if ($delete) {
1373
                    $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...
1374
                } else {
1375 3
                    $command->update($viaTable, $nulls, $columns)->execute();
1376
                }
1377
            }
1378
        } else {
1379 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...
1380 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...
1381 3
            if ($p2) {
1382 3
                if ($delete) {
1383 3
                    $model->delete();
1384
                } else {
1385 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...
1386 3
                        $model->$a = null;
1387
                    }
1388 3
                    $model->save(false);
1389
                }
1390
            } elseif ($p1) {
1391
                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...
1392
                    if (is_array($this->$b)) { // relation via array valued attribute
1393
                        if (($key = array_search($model->$a, $this->$b, false)) !== false) {
1394
                            $values = $this->$b;
1395
                            unset($values[$key]);
1396
                            $this->$b = array_values($values);
1397
                        }
1398
                    } else {
1399
                        $this->$b = null;
1400
                    }
1401
                }
1402
                $delete ? $this->delete() : $this->save(false);
1403
            } else {
1404
                throw new InvalidCallException('Unable to unlink models: the link does not involve any primary key.');
1405
            }
1406
        }
1407
1408 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...
1409
            unset($this->_related[$name]);
1410 3
        } elseif (isset($this->_related[$name])) {
1411
            /* @var $b ActiveRecordInterface */
1412 3
            foreach ($this->_related[$name] as $a => $b) {
1413 3
                if ($model->getPrimaryKey() === $b->getPrimaryKey()) {
1414 3
                    unset($this->_related[$name][$a]);
1415
                }
1416
            }
1417
        }
1418 3
    }
1419
1420
    /**
1421
     * Destroys the relationship in current model.
1422
     *
1423
     * The model with the foreign key of the relationship will be deleted if `$delete` is `true`.
1424
     * Otherwise, the foreign key will be set `null` and the model will be saved without validation.
1425
     *
1426
     * Note that to destroy the relationship without removing records make sure your keys can be set to null
1427
     *
1428
     * @param string $name the case sensitive name of the relationship, e.g. `orders` for a relation defined via `getOrders()` method.
1429
     * @param bool $delete whether to delete the model that contains the foreign key.
1430
     */
1431 18
    public function unlinkAll($name, $delete = false)
1432
    {
1433 18
        $relation = $this->getRelation($name);
1434
1435 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...
1436 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...
1437
                /* @var $viaRelation ActiveQuery */
1438 6
                list($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...
1439 6
                $viaClass = $viaRelation->modelClass;
1440 6
                unset($this->_related[$viaName]);
1441
            } else {
1442 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...
1443 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...
1444
            }
1445 9
            $condition = [];
1446 9
            $nulls = [];
1447 9
            foreach ($viaRelation->link as $a => $b) {
1448 9
                $nulls[$a] = null;
1449 9
                $condition[$a] = $this->$b;
1450
            }
1451 9
            if (!empty($viaRelation->where)) {
1452
                $condition = ['and', $condition, $viaRelation->where];
1453
            }
1454 9
            if (!empty($viaRelation->on)) {
1455
                $condition = ['and', $condition, $viaRelation->on];
1456
            }
1457 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...
1458
                /* @var $viaClass ActiveRecordInterface */
1459 6
                if ($delete) {
1460 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...
1461
                } else {
1462 3
                    $viaClass::updateAll($nulls, $condition);
1463
                }
1464
            } else {
1465
                /* @var $viaTable string */
1466
                /* @var $command Command */
1467 3
                $command = static::getDb()->createCommand();
1468 3
                if ($delete) {
1469 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...
1470
                } else {
1471 3
                    $command->update($viaTable, $nulls, $condition)->execute();
1472
                }
1473
            }
1474
        } else {
1475
            /* @var $relatedModel ActiveRecordInterface */
1476 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...
1477 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...
1478
                // relation via array valued attribute
1479
                $this->$b = [];
1480
                $this->save(false);
1481
            } else {
1482 12
                $nulls = [];
1483 12
                $condition = [];
1484 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...
1485 12
                    $nulls[$a] = null;
1486 12
                    $condition[$a] = $this->$b;
1487
                }
1488 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...
1489 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...
1490
                }
1491 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...
1492 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...
1493
                }
1494 12
                if ($delete) {
1495 9
                    $relatedModel::deleteAll($condition);
1496
                } else {
1497 6
                    $relatedModel::updateAll($nulls, $condition);
1498
                }
1499
            }
1500
        }
1501
1502 18
        unset($this->_related[$name]);
1503 18
    }
1504
1505
    /**
1506
     * @param array $link
1507
     * @param ActiveRecordInterface $foreignModel
1508
     * @param ActiveRecordInterface $primaryModel
1509
     * @throws InvalidCallException
1510
     */
1511 9
    private function bindModels($link, $foreignModel, $primaryModel)
1512
    {
1513 9
        foreach ($link as $fk => $pk) {
1514 9
            $value = $primaryModel->$pk;
1515 9
            if ($value === null) {
1516
                throw new InvalidCallException('Unable to link models: the primary key of ' . get_class($primaryModel) . ' is null.');
1517
            }
1518 9
            if (is_array($foreignModel->$fk)) { // relation via array valued attribute
1519
                $foreignModel->$fk = array_merge($foreignModel->$fk, [$value]);
1520
            } else {
1521 9
                $foreignModel->$fk = $value;
1522
            }
1523
        }
1524 9
        $foreignModel->save(false);
1525 9
    }
1526
1527
    /**
1528
     * Returns a value indicating whether the given set of attributes represents the primary key for this model
1529
     * @param array $keys the set of attributes to check
1530
     * @return bool whether the given set of attributes represents the primary key for this model
1531
     */
1532 15
    public static function isPrimaryKey($keys)
1533
    {
1534 15
        $pks = static::primaryKey();
1535 15
        if (count($keys) === count($pks)) {
1536 15
            return count(array_intersect($keys, $pks)) === count($pks);
1537
        } else {
1538 9
            return false;
1539
        }
1540
    }
1541
1542
    /**
1543
     * Returns the text label for the specified attribute.
1544
     * If the attribute looks like `relatedModel.attribute`, then the attribute will be received from the related model.
1545
     * @param string $attribute the attribute name
1546
     * @return string the attribute label
1547
     * @see generateAttributeLabel()
1548
     * @see attributeLabels()
1549
     */
1550 51
    public function getAttributeLabel($attribute)
1551
    {
1552 51
        $labels = $this->attributeLabels();
1553 51
        if (isset($labels[$attribute])) {
1554 10
            return $labels[$attribute];
1555 48
        } elseif (strpos($attribute, '.')) {
1556
            $attributeParts = explode('.', $attribute);
1557
            $neededAttribute = array_pop($attributeParts);
1558
1559
            $relatedModel = $this;
1560
            foreach ($attributeParts as $relationName) {
1561
                if ($relatedModel->isRelationPopulated($relationName) && $relatedModel->$relationName instanceof self) {
1562
                    $relatedModel = $relatedModel->$relationName;
1563
                } else {
1564
                    try {
1565
                        $relation = $relatedModel->getRelation($relationName);
1566
                    } catch (InvalidParamException $e) {
1567
                        return $this->generateAttributeLabel($attribute);
1568
                    }
1569
                    $relatedModel = new $relation->modelClass;
1570
                }
1571
            }
1572
1573
            $labels = $relatedModel->attributeLabels();
1574
            if (isset($labels[$neededAttribute])) {
1575
                return $labels[$neededAttribute];
1576
            }
1577
        }
1578
1579 48
        return $this->generateAttributeLabel($attribute);
1580
    }
1581
1582
    /**
1583
     * Returns the text hint for the specified attribute.
1584
     * If the attribute looks like `relatedModel.attribute`, then the attribute will be received from the related model.
1585
     * @param string $attribute the attribute name
1586
     * @return string the attribute hint
1587
     * @see attributeHints()
1588
     * @since 2.0.4
1589
     */
1590
    public function getAttributeHint($attribute)
1591
    {
1592
        $hints = $this->attributeHints();
1593
        if (isset($hints[$attribute])) {
1594
            return $hints[$attribute];
1595
        } elseif (strpos($attribute, '.')) {
1596
            $attributeParts = explode('.', $attribute);
1597
            $neededAttribute = array_pop($attributeParts);
1598
1599
            $relatedModel = $this;
1600
            foreach ($attributeParts as $relationName) {
1601
                if ($relatedModel->isRelationPopulated($relationName) && $relatedModel->$relationName instanceof self) {
1602
                    $relatedModel = $relatedModel->$relationName;
1603
                } else {
1604
                    try {
1605
                        $relation = $relatedModel->getRelation($relationName);
1606
                    } catch (InvalidParamException $e) {
1607
                        return '';
1608
                    }
1609
                    $relatedModel = new $relation->modelClass;
1610
                }
1611
            }
1612
1613
            $hints = $relatedModel->attributeHints();
1614
            if (isset($hints[$neededAttribute])) {
1615
                return $hints[$neededAttribute];
1616
            }
1617
        }
1618
        return '';
1619
    }
1620
1621
    /**
1622
     * @inheritdoc
1623
     *
1624
     * The default implementation returns the names of the columns whose values have been populated into this record.
1625
     */
1626
    public function fields()
1627
    {
1628
        $fields = array_keys($this->_attributes);
1629
1630
        return array_combine($fields, $fields);
1631
    }
1632
1633
    /**
1634
     * @inheritdoc
1635
     *
1636
     * The default implementation returns the names of the relations that have been populated into this record.
1637
     */
1638
    public function extraFields()
1639
    {
1640
        $fields = array_keys($this->getRelatedRecords());
1641
1642
        return array_combine($fields, $fields);
1643
    }
1644
1645
    /**
1646
     * Sets the element value at the specified offset to null.
1647
     * This method is required by the SPL interface [[\ArrayAccess]].
1648
     * It is implicitly called when you use something like `unset($model[$offset])`.
1649
     * @param mixed $offset the offset to unset element
1650
     */
1651 3
    public function offsetUnset($offset)
1652
    {
1653 3
        if (property_exists($this, $offset)) {
1654
            $this->$offset = null;
1655
        } else {
1656 3
            unset($this->$offset);
1657
        }
1658 3
    }
1659
}
1660