Completed
Push — master ( 752018...7c5631 )
by vistart
05:02
created

SelfBlameableTrait::onDeleteChildren()   C

Complexity

Conditions 8
Paths 7

Size

Total Lines 25
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 0
Metric Value
dl 0
loc 25
c 0
b 0
f 0
ccs 0
cts 22
cp 0
rs 5.3846
cc 8
eloc 20
nc 7
nop 1
crap 72
1
<?php
2
3
/**
4
 *  _   __ __ _____ _____ ___  ____  _____
5
 * | | / // // ___//_  _//   ||  __||_   _|
6
 * | |/ // /(__  )  / / / /| || |     | |
7
 * |___//_//____/  /_/ /_/ |_||_|     |_|
8
 * @link https://vistart.me/
9
 * @copyright Copyright (c) 2016 - 2017 vistart
10
 * @license https://vistart.me/license/
11
 */
12
13
namespace rhosocial\base\models\traits;
14
15
use yii\base\ModelEvent;
16
use yii\base\InvalidConfigException;
17
use yii\base\InvalidParamException;
18
use yii\db\ActiveQuery;
19
use yii\db\IntegrityException;
20
21
/**
22
 * This trait is designed for the model who contains parent.
23
 * The BlameableTrait use this trait by default. If you want to use this trait
24
 * into seperate model, please call the `initSelfBlameableEvents()` method in
25
 * `init()` method, like following:
26
 * ```php
27
 * public function init()
28
 * {
29
 *     $this->initSelfBlameableEvents();  // put it before parent call.
30
 *     parent::init();
31
 * }
32
 * ```
33
 *
34
 * @property static $parent
35
 * @property-read static[] $ancestors
36
 * @property-read string[] $ancestorChain
37
 * @property-read array $ancestorModels
38
 * @property-read static $commonAncestor
39
 * @property-read static[] $children
40
 * @property-read static[] $oldChildren
41
 * @property array $selfBlameableRules
42
 * @version 1.0
43
 * @author vistart <[email protected]>
44
 */
45
trait SelfBlameableTrait
46
{
47
48
    /**
49
     * @var false|string attribute name of which store the parent's guid.
50
     * If you do not want to use self-blameable features, please set it false.
51
     * Or if you access any features of this trait when this parameter is false,
52
     * exception may be thrown.
53
     */
54
    public $parentAttribute = false;
55
56
    /**
57
     * @var string|array rule name and parameters of parent attribute, as well
58
     * as self referenced ID attribute.
59
     */
60
    public $parentAttributeRule = ['string', 'max' => 16];
61
62
    /**
63
     * @var string self referenced ID attribute.
64
     * If you enable self-blameable features, this parameter should be specified,
65
     * otherwise, exception will be thrown.
66
     */
67
    public $refIdAttribute = 'guid';
68
    public static $parentNone = 0;
69
    public static $parentParent = 1;
70
    public static $parentTypes = [
71
        0 => 'none',
72
        1 => 'parent',
73
    ];
74
    public static $onNoAction = 0;
75
    public static $onRestrict = 1;
76
    public static $onCascade = 2;
77
    public static $onSetNull = 3;
78
    public static $onUpdateTypes = [
79
        0 => 'on action',
80
        1 => 'restrict',
81
        2 => 'cascade',
82
        3 => 'set null',
83
    ];
84
85
    /**
86
     * @var integer indicates the on delete type. default to cascade.
87
     */
88
    public $onDeleteType = 2;
89
90
    /**
91
     * @var integer indicates the on update type. default to cascade.
92
     */
93
    public $onUpdateType = 2;
94
95
    /**
96
     * @var boolean indicates whether throw exception or not when restriction occured on updating or deleting operation.
97
     */
98
    public $throwRestrictException = false;
99
    
100
    /**
101
     * @var array store the attribute validation rules.
102
     * If this field is a non-empty array, then it will be given.
103
     */
104
    private $localSelfBlameableRules = [];
105
    public static $eventParentChanged = 'parentChanged';
106
    public static $eventChildAdded = 'childAdded';
107
108
    /**
109
     * @var false|integer Set the limit of ancestor level. False is no limit.
110
     * We strongly recommend you set an unsigned integer which is less than 256.
111
     */
112
    public $ancestorLimit = false;
113
114
    /**
115
     * @var false|integer Set the limit of children (not descendants). False is no limit.
116
     * We strongly recommend you set an unsigned integer which is less than 1024.
117
     */
118
    public $childrenLimit = false;
119
120
    /**
121
     * Get rules associated with self blameable attribute.
122
     * If self-blameable rules has been stored locally, then it will be given,
123
     * or return the parent attribute rule.
124
     * @return array rules.
125
     */
126 21
    public function getSelfBlameableRules()
127
    {
128 21
        if (!is_string($this->parentAttribute)) {
129 21
            return [];
130
        }
131 12
        if (!empty($this->localSelfBlameableRules) && is_array($this->localSelfBlameableRules)) {
132
            return $this->localSelfBlameableRules;
133
        }
134 12
        if (is_string($this->parentAttributeRule)) {
135
            $this->parentAttributeRule = [$this->parentAttributeRule];
136
        }
137 12
        $this->localSelfBlameableRules = [
138 12
            array_merge([$this->parentAttribute], $this->parentAttributeRule),
139
        ];
140 12
        return $this->localSelfBlameableRules;
141
    }
142
143
    /**
144
     * Set rules associated with self blameable attribute.
145
     * @param array $rules rules.
146
     */
147
    public function setSelfBlameableRules($rules = [])
148
    {
149
        $this->localSelfBlameableRules = $rules;
150
    }
151
152
    /**
153
     * Check whether this model has reached the ancestor limit.
154
     * If $ancestorLimit is false, it will be regared as no limit(return false).
155
     * If $ancestorLimit is not false and not an unsigned integer, 256 will be taken.
156
     * @return boolean
157
     */
158
    public function hasReachedAncestorLimit()
159
    {
160
        if ($this->ancestorLimit === false) {
161
            return false;
162
        }
163
        if (!is_numeric($this->ancestorLimit)) {
164
            $this->ancestorLimit = 256;
165
        }
166
        return count($this->getAncestorChain()) >= $this->ancestorLimit;
167
    }
168
169
    /**
170
     * Check whether this model has reached the children limit.
171
     * If $childrenLimit is false, it will be regarded as no limit(return false).
172
     * If $childrenLimit is not false and not an unsigned integer, 1024 will be taken.
173
     * @return boolean
174
     */
175
    public function hasReachedChildrenLimit()
176
    {
177
        if ($this->childrenLimit === false) {
178
            return false;
179
        }
180
        if (!is_numeric($this->childrenLimit)) {
181
            $this->childrenLimit = 1024;
182
        }
183
        return ((int) $this->getChildren()->count()) >= $this->childrenLimit;
184
    }
185
186
    /**
187
     * Bear a child.
188
     * The creator of this child is not necessarily the creator of current one.
189
     * For example: Someone commit a comment on another user's comment, these
190
     * two comments are father and son, but do not belong to the same owner.
191
     * Therefore, you need to specify the creator of current model.
192
     * @param array $config
193
     * @return static|null Null if reached the ancestor limit or children limit.
194
     * @throws InvalidConfigException Self reference ID attribute or
195
     * parent attribute not determined.
196
     * @throws InvalidParamException ancestor or children limit reached.
197
     */
198
    public function bear($config = [])
199
    {
200
        if (!$this->parentAttribute) {
201
            throw new InvalidConfigException("Parent Attribute Not Determined.");
202
        }
203
        if (!$this->refIdAttribute) {
204
            throw new InvalidConfigException("Self Reference ID Attribute Not Determined.");
205
        }
206
        if ($this->hasReachedAncestorLimit()) {
207
            throw new InvalidParamException("Reached Ancestor Limit: " . $this->ancestorLimit);
208
        }
209
        if ($this->hasReachedChildrenLimit()) {
210
            throw new InvalidParamException("Reached Children Limit: ". $this->childrenLimit);
211
        }
212
        if (isset($config['class'])) {
213
            unset($config['class']);
214
        }
215
        $refIdAttribute = $this->refIdAttribute;
216
        $config[$this->parentAttribute] = $this->$refIdAttribute;
217
        return new static($config);
0 ignored issues
show
Unused Code introduced by
The call to SelfBlameableTrait::__construct() has too many arguments starting with $config.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
218
    }
219
220
    /**
221
     * Add a child.
222
     * @param static $child
0 ignored issues
show
introduced by
The type SelfBlameableTrait for parameter $child is a trait, and thus cannot be used for type-hinting in PHP. Maybe consider adding an interface and use that for type-hinting?
Loading history...
223
     * @return boolean
224
     */
225
    public function addChild($child)
226
    {
227
        return $this->hasReachedChildrenLimit() ? $child->setParent($this) : false;
228
    }
229
230
    /**
231
     * Event triggered before deleting itself.
232
     * @param ModelEvent $event
233
     * @return boolean true if parentAttribute not specified.
234
     * @throws IntegrityException throw if $throwRestrictException is true when $onDeleteType is on restrict.
235
     */
236
    public function onDeleteChildren($event)
237
    {
238
        $sender = $event->sender;
239
        if (empty($sender->parentAttribute) || !is_string($sender->parentAttribute)) {
240
            return true;
241
        }
242
        switch ($sender->onDeleteType) {
243
            case static::$onRestrict:
244
                $event->isValid = $sender->children === null;
245
                if ($this->throwRestrictException) {
246
                    throw new IntegrityException('Delete restricted.');
247
                }
248
                break;
249
            case static::$onCascade:
250
                $event->isValid = $sender->deleteChildren();
251
                break;
252
            case static::$onSetNull:
253
                $event->isValid = $sender->updateChildren(null);
254
                break;
255
            case static::$onNoAction:
256
            default:
257
                $event->isValid = true;
258
                break;
259
        }
260
    }
261
262
    /**
263
     * Event triggered before updating itself.
264
     * @param ModelEvent $event
265
     * @return boolean true if parentAttribute not specified.
266
     * @throws IntegrityException throw if $throwRestrictException is true when $onUpdateType is on restrict.
267
     */
268
    public function onUpdateChildren($event)
269
    {
270
        $sender = $event->sender;
271
        if (empty($sender->parentAttribute) || !is_string($sender->parentAttribute)) {
272
            return true;
273
        }
274
        switch ($sender->onUpdateType) {
275
            case static::$onRestrict:
276
                $event->isValid = $sender->getOldChildren() === null;
277
                if ($this->throwRestrictException) {
278
                    throw new IntegrityException('Update restricted.');
279
                }
280
                break;
281
            case static::$onCascade:
282
                $event->isValid = $sender->updateChildren();
283
                break;
284
            case static::$onSetNull:
285
                $event->isValid = $sender->updateChildren(null);
286
                break;
287
            case static::$onNoAction:
288
            default:
289
                $event->isValid = true;
290
                break;
291
        }
292
    }
293
294
    /**
295
     * Get parent query.
296
     * Or get parent instance if access by magic property.
297
     * @return ActiveQuery
298
     */
299
    public function getParent()
300
    {
301
        return $this->hasOne(static::class, [$this->refIdAttribute => $this->parentAttribute]);
0 ignored issues
show
Bug introduced by
It seems like hasOne() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
302
    }
303
304
    /**
305
     * Set parent.
306
     * Don't forget save model after setting it.
307
     * @param static $parent
0 ignored issues
show
introduced by
The type SelfBlameableTrait for parameter $parent is a trait, and thus cannot be used for type-hinting in PHP. Maybe consider adding an interface and use that for type-hinting?
Loading history...
308
     * @return false|string
309
     */
310
    public function setParent($parent)
311
    {
312
        if (empty($parent) || $this->guid == $parent->guid || $parent->hasAncestor($this) || $parent->hasReachedAncestorLimit()) {
0 ignored issues
show
Bug introduced by
The property guid does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
313
            return false;
314
        }
315
        unset($this->parent);
316
        unset($parent->children);
317
        $this->trigger(static::$eventParentChanged);
0 ignored issues
show
Bug introduced by
It seems like trigger() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
318
        $parent->trigger(static::$eventChildAdded);
0 ignored issues
show
Bug introduced by
It seems like trigger() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
319
        return $this->{$this->parentAttribute} = $parent->guid;
320
    }
321
322
    /**
323
     * Check whether this model has parent.
324
     * @return boolean
325
     */
326
    public function hasParent()
327
    {
328
        return $this->parent !== null;
329
    }
330
331
    /**
332
     * Check whether if $ancestor is the ancestor of myself.
333
     * Note, Itself will not be regarded as the its ancestor.
334
     * @param static $ancestor
0 ignored issues
show
introduced by
The type SelfBlameableTrait for parameter $ancestor is a trait, and thus cannot be used for type-hinting in PHP. Maybe consider adding an interface and use that for type-hinting?
Loading history...
335
     * @return boolean
336
     */
337
    public function hasAncestor($ancestor)
338
    {
339
        if (!$this->hasParent()) {
340
            return false;
341
        }
342
        if ($this->parent->guid == $ancestor->guid) {
343
            return true;
344
        }
345
        return $this->parent->hasAncestor($ancestor);
346
    }
347
348
    /**
349
     * Get ancestor chain. (Ancestors' GUID Only!)
350
     * If this model has ancestor, the return array consists all the ancestor in order.
351
     * The first element is parent, and the last element is root, otherwise return empty array.
352
     * If you want to get ancestor model, you can simplify instance a query and specify the
353
     * condition with the return value. But it will not return models under the order of ancestor chain.
354
     * @param string[] $ancestor
355
     * @return string[]
356
     */
357
    public function getAncestorChain($ancestor = [])
358
    {
359
        if (!is_string($this->parentAttribute)) {
360
            return [];
361
        }
362
        if (!$this->hasParent()) {
363
            return $ancestor;
364
        }
365
        $ancestor[] = $this->parent->guid;
366
        return $this->parent->getAncestorChain($ancestor);
367
    }
368
369
    /**
370
     * Get ancestors with specified ancestor chain.
371
     * @param string[] $ancestor Ancestor chain.
372
     * @return static[]|null
373
     */
374
    public static function getAncestorModels($ancestor)
375
    {
376
        if (empty($ancestor) || !is_array($ancestor)) {
377
            return null;
378
        }
379
        $models = [];
380
        foreach ($ancestor as $self) {
381
            $models[] = static::findOne($self);
382
        }
383
        return $models;
384
    }
385
386
    /**
387
     * Get ancestors.
388
     * @return static[]|null
389
     */
390
    public function getAncestors()
391
    {
392
        return (is_string($this->parentAttribute) && !empty($this->parentAttribute)) ? $this->getAncestorModels($this->getAncestorChain()) : null;
393
    }
394
395
    /**
396
     * Check whether if this model has common ancestor with $model.
397
     * @param static $model
0 ignored issues
show
introduced by
The type SelfBlameableTrait for parameter $model is a trait, and thus cannot be used for type-hinting in PHP. Maybe consider adding an interface and use that for type-hinting?
Loading history...
398
     * @return boolean
399
     */
400
    public function hasCommonAncestor($model)
401
    {
402
        return (is_string($this->parentAttribute) && !empty($this->parentAttribute)) ? $this->getCommonAncestor($model) !== null : false;
403
    }
404
405
    /**
406
     * Get common ancestor. If there isn't common ancestor, null will be given.
407
     * @param static $model
0 ignored issues
show
introduced by
The type SelfBlameableTrait for parameter $model is a trait, and thus cannot be used for type-hinting in PHP. Maybe consider adding an interface and use that for type-hinting?
Loading history...
408
     * @return static
409
     */
410
    public function getCommonAncestor($model)
411
    {
412
        if (empty($this->parentAttribute) || !is_string($this->parentAttribute) || empty($model) || !$model->hasParent()) {
413
            return null;
414
        }
415
        $ancestor = $this->getAncestorChain();
416
        if (in_array($model->parent->guid, $ancestor)) {
417
            return $model->parent;
418
        }
419
        return $this->getCommonAncestor($model->parent);
420
    }
421
422
    /**
423
     * Get children query.
424
     * Or get children instances if access magic property.
425
     * @return ActiveQuery
426
     */
427
    public function getChildren()
428
    {
429
        return $this->hasMany(static::class, [$this->parentAttribute => $this->refIdAttribute])->inverseOf('parent');
0 ignored issues
show
Bug introduced by
It seems like hasMany() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
430
    }
431
432
    /**
433
     * Get children which parent attribute point to old guid.
434
     * @return static[]
435
     */
436
    public function getOldChildren()
437
    {
438
        return static::find()->where([$this->parentAttribute => $this->getOldAttribute($this->refIdAttribute)])->all();
0 ignored issues
show
Bug introduced by
It seems like getOldAttribute() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
439
    }
440
441
    /**
442
     * Update all children, not grandchildren (descendants).
443
     * If onUpdateType is on cascade, the children will be updated automatically.
444
     * @param mixed $value set guid if false, set empty string if empty() return
445
     * true, otherwise set it to $parentAttribute.
446
     * @return IntegrityException|boolean true if all update operations
447
     * succeeded to execute, or false if anyone of them failed. If not production
448
     * environment or enable debug mode, it will return exception.
449
     * @throws IntegrityException throw if anyone update failed.
450
     * The exception message only contains the first error.
451
     */
452
    public function updateChildren($value = false)
453
    {
454
        $children = $this->getOldChildren();
455
        if (empty($children)) {
456
            return true;
457
        }
458
        $parentAttribute = $this->parentAttribute;
459
        $transaction = $this->getDb()->beginTransaction();
0 ignored issues
show
Bug introduced by
It seems like getDb() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
460
        try {
461
            foreach ($children as $child) {
462
                if ($value === false) {
463
                    $refIdAttribute = $this->refIdAttribute;
464
                    $child->$parentAttribute = $this->$refIdAttribute;
465
                } elseif (empty($value)) {
466
                    $child->$parentAttribute = '';
467
                } else {
468
                    $child->$parentAttribute = $value;
469
                }
470
                if (!$child->save()) {
0 ignored issues
show
Bug introduced by
It seems like save() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
471
                    throw new IntegrityException('Update failed:', $child->getErrors());
0 ignored issues
show
Bug introduced by
It seems like getErrors() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
472
                }
473
            }
474
            $transaction->commit();
475
        } catch (IntegrityException $ex) {
476
            $transaction->rollBack();
477
            if (YII_DEBUG || YII_ENV !== YII_ENV_PROD) {
478
                Yii::error($ex->errorInfo, static::class . '\update');
479
                return $ex;
480
            }
481
            Yii::warning($ex->errorInfo, static::class . '\update');
482
            return false;
483
        }
484
        return true;
485
    }
486
487
    /**
488
     * Delete all children, not grandchildren (descendants).
489
     * If onDeleteType is `on cascade`, the children will be deleted automatically.
490
     * If onDeleteType is `on restrict` and contains children, the deletion will
491
     * be restricted.
492
     * @return IntegrityException|boolean true if all delete operations
493
     * succeeded to execute, or false if anyone of them failed. If not production
494
     * environment or enable debug mode, it will return exception.
495
     * @throws IntegrityException throw if anyone delete failed.
496
     * The exception message only contains the first error.
497
     */
498
    public function deleteChildren()
499
    {
500
        $children = $this->children;
501
        if (empty($children)) {
502
            return true;
503
        }
504
        $transaction = $this->getDb()->beginTransaction();
0 ignored issues
show
Bug introduced by
It seems like getDb() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
505
        try {
506
            foreach ($children as $child) {
507
                if (!$child->delete()) {
508
                    throw new IntegrityException('Delete failed:', $child->getErrors());
509
                }
510
            }
511
            $transaction->commit();
512
        } catch (IntegrityException $ex) {
513
            $transaction->rollBack();
514
            if (YII_DEBUG || YII_ENV !== YII_ENV_PROD) {
515
                Yii::error($ex->errorInfo, static::class . '\delete');
516
                return $ex;
517
            }
518
            Yii::warning($ex->errorInfo, static::class . '\delete');
519
            return false;
520
        }
521
        return true;
522
    }
523
524
    /**
525
     * Update children's parent attribute.
526
     * Event triggered before updating.
527
     * @param ModelEvent $event
528
     * @return boolean
529
     */
530 2
    public function onParentRefIdChanged($event)
531
    {
532 2
        $sender = $event->sender;
533 2
        if ($sender->isAttributeChanged($sender->refIdAttribute)) {
534
            return $sender->onUpdateChildren($event);
535
        }
536 2
    }
537
538
    /**
539
     * Attach events associated with self blameable attribute.
540
     */
541 21
    protected function initSelfBlameableEvents()
542
    {
543 21
        $this->on(static::EVENT_BEFORE_UPDATE, [$this, 'onParentRefIdChanged']);
0 ignored issues
show
Bug introduced by
It seems like on() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
544 21
        $this->on(static::EVENT_BEFORE_DELETE, [$this, 'onDeleteChildren']);
0 ignored issues
show
Bug introduced by
It seems like on() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
545 21
    }
546
}
547