Completed
Push — master ( eec31e...9249bd )
by vistart
05:05
created

BlameableTrait   D

Complexity

Total Complexity 115

Size/Duplication

Total Lines 706
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 74.31%

Importance

Changes 0
Metric Value
wmc 115
lcom 1
cbo 9
dl 0
loc 706
ccs 214
cts 288
cp 0.7431
rs 4.4444
c 0
b 0
f 0

34 Methods

Rating   Name   Duplication   Size   Complexity  
A rules() 0 4 1
A behaviors() 0 4 1
A countOfOwner() 0 5 1
A getContent() 0 15 4
A getContentCanBeEdited() 0 7 2
A getBlameableRulesCacheKey() 0 4 1
A getBlameableRulesCacheTag() 0 4 1
A getDescriptionRules() 0 16 3
A setBlameableRules() 0 9 2
A getBlameableBehaviorsCacheKey() 0 4 1
A getBlameableBehaviorsCacheTag() 0 4 1
B getBlameableBehaviors() 0 22 5
A setBlameableBehaviors() 0 10 2
A getDescription() 0 5 2
A setDescription() 0 5 2
A getUser() 0 7 1
B setUser() 0 13 6
B setUpdater() 0 16 8
A onContentChanged() 0 5 1
A onGetCurrentUserGuid() 0 13 3
B onInitContentType() 0 14 6
A onInitDescription() 0 12 4
B initBlameableEvents() 0 15 7
A setContent() 0 14 4
A hasEverEdited() 0 9 3
B getBlameableRules() 0 26 5
C getBlameableAttributeRules() 0 30 7
C getContentRules() 0 31 8
A getUpdater() 0 10 3
D enabledFields() 0 27 11
A findAllByIdentityInBatch() 0 7 2
A findOneById() 0 12 4
A countByIdentity() 0 4 1
A getPagination() 0 9 2

How to fix   Complexity   

Complex Class

Complex classes like BlameableTrait often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use BlameableTrait, and based on these observations, apply Extract Interface, too.

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 rhosocial\base\helpers\Number;
16
use rhosocial\base\models\queries\BaseUserQuery;
17
use yii\base\InvalidParamException;
18
use yii\base\ModelEvent;
19
use yii\base\NotSupportedException;
20
use yii\behaviors\BlameableBehavior;
21
use yii\caching\TagDependency;
22
use yii\data\Pagination;
23
24
/**
25
 * This trait is used for building blameable model. It contains following features:
26
 * 1.单列内容;多列内容待定;
27
 * 2.内容类型;具体类型应当自定义;
28
 * 3.内容规则;自动生成;
29
 * 4.归属用户 GUID;
30
 * 5.创建用户 GUID;
31
 * 6.上次更新用户 GUID;
32
 * 7.Confirmation features, provided by [[ConfirmationTrait]];
33
 * @property-read array $blameableAttributeRules Get all rules associated with
34
 * blameable.
35
 * @property array $blameableRules Get or set all the rules associated with
36
 * creator, updater, content and its ID, as well as all the inherited rules.
37
 * @property array $blameableBehaviors Get or set all the behaviors assoriated
38
 * with creator and updater, as well as all the inherited behaviors.
39
 * @property-read array $descriptionRules Get description property rules.
40
 * @property-read mixed $content Content.
41
 * @property-read boolean $contentCanBeEdited Whether this content could be edited.
42
 * @property-read array $contentRules Get content rules.
43
 * @property BaseUserModel $user
44
 * @property BaseUserModel $updater
45
 * @version 1.0
46
 * @author vistart <[email protected]>
47
 */
48
trait BlameableTrait
49
{
50
    use ConfirmationTrait,
51
        SelfBlameableTrait;
52
53
    private $blameableLocalRules = [];
54
    private $blameableLocalBehaviors = [];
55
56
    /**
57
     * @var boolean|string|array Specify the attribute(s) name of content(s). If
58
     * there is only one content attribute, you can assign its name. Or there
59
     * is multiple attributes associated with contents, you can assign their
60
     * names in array. If you don't want to use this feature, please assign
61
     * false.
62
     * For example:
63
     * ```php
64
     * public $contentAttribute = 'comment'; // only one field named as 'comment'.
65
     * ```
66
     * or
67
     * ```php
68
     * public $contentAttribute = ['year', 'month', 'day']; // multiple fields.
69
     * ```
70
     * or
71
     * ```php
72
     * public $contentAttribute = false; // no need of this feature.
73
     * ```
74
     * If you don't need this feature, you should add rules corresponding with
75
     * `content` in `rules()` method of your user model by yourself.
76
     */
77
    public $contentAttribute = 'content';
78
79
    /**
80
     * @var array built-in validator name or validatation method name and
81
     * additional parameters.
82
     */
83
    public $contentAttributeRule = ['string', 'max' => 255];
84
85
    /**
86
     * @var boolean|string Specify the field which stores the type of content.
87
     */
88
    public $contentTypeAttribute = false;
89
90
    /**
91
     * @var boolean|array Specify the logic type of content, not data type. If
92
     * your content doesn't need this feature. please specify false. If the
93
     * $contentAttribute is specified to false, this attribute will be skipped.
94
     * ```php
95
     * public $contentTypes = [
96
     *     'public',
97
     *     'private',
98
     *     'friend',
99
     * ];
100
     * ```
101
     */
102
    public $contentTypes = false;
103
104
    /**
105
     * @var boolean|string This attribute speicfy the name of description
106
     * attribute. If this attribute is assigned to false, this feature will be
107
     * skipped.
108
     */
109
    public $descriptionAttribute = false;
110
111
    /**
112
     * @var string
113
     */
114
    public $initDescription = '';
115
116
    /**
117
     * @var string the attribute that will receive current user ID value. This
118
     * attribute must be assigned.
119
     */
120
    public $createdByAttribute = "user_guid";
121
122
    /**
123
     * @var string the attribute that will receive current user ID value.
124
     * Set this property to false if you do not want to record the updater ID.
125
     */
126
    public $updatedByAttribute = "user_guid";
127
128
    /**
129
     * @var boolean Add combinated unique rule if assigned to true.
130
     */
131
    public $idCreatorCombinatedUnique = true;
132
133
    /**
134
     * @var boolean|string The name of user class which own the current entity.
135
     * If this attribute is assigned to false, this feature will be skipped, and
136
     * when you use create() method of UserTrait, it will be assigned with
137
     * current user class.
138
     */
139
    public $userClass;
140
    public static $cacheKeyBlameableRules = 'blameable_rules';
141
    public static $cacheTagBlameableRules = 'tag_blameable_rules';
142
    public static $cacheKeyBlameableBehaviors = 'blameable_behaviors';
143
    public static $cacheTagBlameableBehaviors = 'tag_blameable_behaviors';
144
145
    /**
146
     * @inheritdoc
147
     * ------------
148
     * The classical rules is like following:
149
     * [
150
     *     ['guid', 'required'],
151
     *     ['guid', 'unique'],
152
     *     ['guid', 'string', 'max' => 36],
153
     *
154
     *     ['id', 'required'],
155
     *     ['id', 'unique'],
156
     *     ['id', 'string', 'max' => 4],
157
     *
158
     *     ['created_at', 'safe'],
159
     *     ['updated_at', 'safe'],
160
     *
161
     *     ['ip_type', 'in', 'range' => [4, 6]],
162
     *     ['ip', 'number', 'integerOnly' => true, 'min' => 0],
163
     * ]
164
     * @return array
165
     */
166 12
    public function rules()
167
    {
168 12
        return $this->getBlameableRules();
169
    }
170
171
    /**
172
     * @inheritdoc
173
     */
174 12
    public function behaviors()
175
    {
176 12
        return $this->getBlameableBehaviors();
177
    }
178
179
    /**
180
     * Get total of contents which owned by their owner.
181
     * @return integer
182
     */
183 1
    public function countOfOwner()
184
    {
185 1
        $createdByAttribute = $this->createdByAttribute;
186 1
        return static::find()->where([$createdByAttribute => $this->$createdByAttribute])->count();
187
    }
188
189
    /**
190
     * Get content.
191
     * @return mixed
192
     */
193
    public function getContent()
194
    {
195
        $contentAttribute = $this->contentAttribute;
196
        if ($contentAttribute === false) {
197
            return null;
198
        }
199
        if (is_array($contentAttribute)) {
200
            $content = [];
201
            foreach ($contentAttribute as $key => $value) {
202
                $content[$key] = $this->$value;
203
            }
204
            return $content;
205
        }
206
        return $this->$contentAttribute;
207
    }
208
209
    /**
210
     * Set content.
211
     * @param mixed $content
212
     */
213 9
    public function setContent($content)
214
    {
215 9
        $contentAttribute = $this->contentAttribute;
216 9
        if ($contentAttribute === false) {
217
            return;
218
        }
219 9
        if (is_array($contentAttribute)) {
220
            foreach ($contentAttribute as $key => $value) {
221
                $this->$value = $content[$key];
222
            }
223
            return;
224
        }
225 9
        $this->$contentAttribute = $content;
226 9
    }
227
228
    /**
229
     * Determines whether content could be edited. Your should implement this
230
     * method by yourself.
231
     * @return boolean
232
     * @throws NotSupportedException
233
     */
234
    public function getContentCanBeEdited()
235
    {
236
        if ($this->contentAttribute === false) {
237
            return false;
238
        }
239
        throw new NotSupportedException("This method is not implemented.");
240
    }
241
242
    /**
243
     * Check it has been ever edited.
244
     * @return boolean Whether this content has ever been edited.
245
     */
246 1
    public function hasEverEdited()
247
    {
248 1
        $createdAtAttribute = $this->createdByAttribute;
249 1
        $updatedAtAttribute = $this->updatedByAttribute;
250 1
        if (!$createdAtAttribute || !$updatedAtAttribute) {
251
            return false;
252
        }
253 1
        return $this->$createdAtAttribute !== $this->$updatedAtAttribute;
254
    }
255
256
    /**
257
     * Get blameable rules cache key.
258
     * @return string cache key.
259
     */
260 12
    public function getBlameableRulesCacheKey()
261
    {
262 12
        return static::class . $this->cachePrefix . static::$cacheKeyBlameableRules;
0 ignored issues
show
Bug introduced by
The property cachePrefix 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...
263
    }
264
265
    /**
266
     * Get blameable rules cache tag.
267
     * @return string cache tag
268
     */
269 12
    public function getBlameableRulesCacheTag()
270
    {
271 12
        return static::class . $this->cachePrefix . static::$cacheTagBlameableRules;
272
    }
273
274
    /**
275
     * Get the rules associated with content to be blamed.
276
     * @return array rules.
277
     */
278 12
    public function getBlameableRules()
279
    {
280 12
        $cache = $this->getCache();
0 ignored issues
show
Bug introduced by
It seems like getCache() 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...
281 12
        if ($cache) {
282 12
            $this->blameableLocalRules = $cache->get($this->getBlameableRulesCacheKey());
283 12
        }
284
        // 若当前规则不为空,且是数组,则认为是规则数组,直接返回。
285 12
        if (!empty($this->blameableLocalRules) && is_array($this->blameableLocalRules)) {
286 9
            return $this->blameableLocalRules;
287
        }
288
289
        // 父类规则与确认规则合并。
290 12
        if ($cache) {
291 12
            TagDependency::invalidate($cache, [$this->getEntityRulesCacheTag()]);
0 ignored issues
show
Bug introduced by
It seems like getEntityRulesCacheTag() 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...
292 12
        }
293 12
        $rules = array_merge(
294 12
            parent::rules(),
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (rules() instead of getBlameableRules()). Are you sure this is correct? If so, you might want to change this to $this->rules().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
295 12
            $this->getConfirmationRules(),
296 12
            $this->getBlameableAttributeRules(),
297 12
            $this->getDescriptionRules(),
298 12
            $this->getContentRules(),
299 12
            $this->getSelfBlameableRules()
300 12
        );
301 12
        $this->setBlameableRules($rules);
302 12
        return $this->blameableLocalRules;
303
    }
304
305
    /**
306
     * Get the rules associated with `createdByAttribute`, `updatedByAttribute`
307
     * and `idAttribute`-`createdByAttribute` combination unique.
308
     * @return array rules.
309
     */
310 12
    public function getBlameableAttributeRules()
311
    {
312 12
        $rules = [];
313
        // 创建者和上次修改者由 BlameableBehavior 负责,因此标记为安全。
314 12
        if (!is_string($this->createdByAttribute) || empty($this->createdByAttribute)) {
315
            throw new NotSupportedException('You must assign the creator.');
316
        }
317 12
        $rules[] = [
318 12
            [$this->createdByAttribute],
319 12
            'safe',
320
        ];
321
322 12
        if (is_string($this->updatedByAttribute) && !empty($this->updatedByAttribute)) {
323 9
            $rules[] = [
324 9
                [$this->updatedByAttribute],
325 9
                'safe',
326
            ];
327 9
        }
328
329 12
        if ($this->idCreatorCombinatedUnique && is_string($this->idAttribute)) {
0 ignored issues
show
Bug introduced by
The property idAttribute does not seem to exist. Did you mean refIdAttribute?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
330 12
            $rules ['id'] = [
331 12
                [$this->idAttribute,
0 ignored issues
show
Bug introduced by
The property idAttribute does not seem to exist. Did you mean refIdAttribute?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
332 12
                    $this->createdByAttribute],
333 12
                'unique',
334 12
                'targetAttribute' => [$this->idAttribute,
0 ignored issues
show
Bug introduced by
The property idAttribute does not seem to exist. Did you mean refIdAttribute?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
335 12
                    $this->createdByAttribute],
336
            ];
337 12
        }
338 12
        return $rules;
339
    }
340
341
    /**
342
     * Get the rules associated with `description` attribute.
343
     * @return array rules.
344
     */
345 12
    public function getDescriptionRules()
346
    {
347 12
        $rules = [];
348 12
        if (is_string($this->descriptionAttribute) && !empty($this->descriptionAttribute)) {
349 3
            $rules[] = [
350 3
                [$this->descriptionAttribute],
351
                'string'
352 3
            ];
353 3
            $rules[] = [
354 3
                [$this->descriptionAttribute],
355 3
                'default',
356 3
                'value' => $this->initDescription,
357
            ];
358 3
        }
359 12
        return $rules;
360
    }
361
362
    /**
363
     * Get the rules associated with `content` and `contentType` attributes.
364
     * @return array rules.
365
     */
366 12
    public function getContentRules()
367
    {
368 12
        if (!$this->contentAttribute) {
369
            return [];
370
        }
371 12
        $rules = [];
372 12
        $rules[] = [$this->contentAttribute, 'required'];
373 12
        if ($this->contentAttributeRule) {
374 12
            if (is_string($this->contentAttributeRule)) {
375
                $this->contentAttributeRule = [$this->contentAttributeRule];
376
            }
377 12
            if (is_array($this->contentAttributeRule)) {
378 12
                $rules[] = array_merge([$this->contentAttribute], $this->contentAttributeRule);
379 12
            }
380 12
        }
381
382 12
        if (!$this->contentTypeAttribute) {
383 9
            return $rules;
384
        }
385
386 3
        if (is_array($this->contentTypes) && !empty($this->contentTypes)) {
387 3
            $rules[] = [[
388 3
                $this->contentTypeAttribute],
389 3
                'required'];
390 3
            $rules[] = [[
391 3
                $this->contentTypeAttribute],
392 3
                'in',
393 3
                'range' => array_keys($this->contentTypes)];
394 3
        }
395 3
        return $rules;
396
    }
397
398
    /**
399
     * Set blameable rules.
400
     * @param array $rules
401
     */
402 12
    protected function setBlameableRules($rules = [])
403
    {
404 12
        $this->blameableLocalRules = $rules;
405 12
        $cache = $this->getCache();
0 ignored issues
show
Bug introduced by
It seems like getCache() 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...
406 12
        if ($cache) {
407 12
            $tagDependency = new TagDependency(['tags' => [$this->getBlameableRulesCacheTag()]]);
408 12
            $cache->set($this->getBlameableRulesCacheKey(), $rules, 0, $tagDependency);
409 12
        }
410 12
    }
411
412
    /**
413
     * Get blameable behaviors cache key.
414
     * @return string cache key.
415
     */
416 12
    public function getBlameableBehaviorsCacheKey()
417
    {
418 12
        return static::class . $this->cachePrefix . static::$cacheKeyBlameableBehaviors;
419
    }
420
421
    /**
422
     * Get blameable behaviors cache tag.
423
     * @return string cache tag.
424
     */
425 12
    public function getBlameableBehaviorsCacheTag()
426
    {
427 12
        return static::class . $this->cachePrefix . static::$cacheTagBlameableBehaviors;
428
    }
429
430
    /**
431
     * Get blameable behaviors. If current behaviors array is empty, the init
432
     * array will be given.
433
     * @return array
434
     */
435 12
    public function getBlameableBehaviors()
436
    {
437 12
        $cache = $this->getCache();
0 ignored issues
show
Bug introduced by
It seems like getCache() 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...
438 12
        if ($cache) {
439 12
            $this->blameableLocalBehaviors = $cache->get($this->getBlameableBehaviorsCacheKey());
440 12
        }
441 12
        if (empty($this->blameableLocalBehaviors) || !is_array($this->blameableLocalBehaviors)) {
442 12
            if ($cache) {
443 12
                TagDependency::invalidate($cache, [$this->getEntityBehaviorsCacheTag()]);
0 ignored issues
show
Bug introduced by
The method getEntityBehaviorsCacheTag() does not exist on rhosocial\base\models\traits\BlameableTrait. Did you maybe mean behaviors()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
444 12
            }
445 12
            $behaviors = parent::behaviors();
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (behaviors() instead of getBlameableBehaviors()). Are you sure this is correct? If so, you might want to change this to $this->behaviors().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
446 12
            $behaviors['blameable'] = [
447 12
                'class' => BlameableBehavior::class,
448 12
                'createdByAttribute' => $this->createdByAttribute,
449 12
                'updatedByAttribute' => $this->updatedByAttribute,
450 12
                'value' => [$this,
451 12
                    'onGetCurrentUserGuid'],
452
            ];
453 12
            $this->setBlameableBehaviors($behaviors);
454 12
        }
455 12
        return $this->blameableLocalBehaviors;
456
    }
457
458
    /**
459
     * Set blameable behaviors.
460
     * @param array $behaviors
461
     */
462 12
    protected function setBlameableBehaviors($behaviors = [])
463
    {
464 12
        $this->blameableLocalBehaviors = $behaviors;
465 12
        $cache = $this->getCache();
0 ignored issues
show
Bug introduced by
It seems like getCache() 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...
466 12
        if ($cache) {
467 12
            $tagDependencyConfig = ['tags' => [$this->getBlameableBehaviorsCacheTag()]];
468 12
            $tagDependency = new TagDependency($tagDependencyConfig);
469 12
            $cache->set($this->getBlameableBehaviorsCacheKey(), $behaviors, 0, $tagDependency);
470 12
        }
471 12
    }
472
473
    /**
474
     * Set description.
475
     * @return string description.
476
     */
477
    public function getDescription()
478
    {
479
        $descAttribute = $this->descriptionAttribute;
480
        return is_string($descAttribute) ? $this->$descAttribute : null;
481
    }
482
483
    /**
484
     * Get description.
485
     * @param string $desc description.
486
     * @return string|null description if enabled, or null if disabled.
487
     */
488
    public function setDescription($desc)
489
    {
490
        $descAttribute = $this->descriptionAttribute;
491
        return is_string($descAttribute) ? $this->$descAttribute = $desc : null;
492
    }
493
494
    /**
495
     * Get blame who owned this blameable model.
496
     * NOTICE! This method will not check whether `$userClass` exists. You should
497
     * specify it in `init()` method.
498
     * @return BaseUserQuery user.
499
     */
500 3
    public function getUser()
501
    {
502 3
        $userClass = $this->userClass;
503 3
        $model = $userClass::buildNoInitModel();
504
        /* @var $model BaseUserModel */
505 3
        return $this->hasOne($userClass::className(), [$model->guidAttribute => $this->createdByAttribute]);
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...
506
    }
507
    
508
    /**
509
     *
510
     * @param BaseUserModel|string $user
511
     * @return boolean
512
     */
513 12
    public function setUser($user)
514
    {
515 12
        if ($user instanceof $this->userClass || $user instanceof \yii\web\IdentityInterface) {
516 12
            return $this->{$this->createdByAttribute} = $user->getGUID();
517
        }
518
        if (is_string($user) && preg_match(Number::GUID_REGEX, $user)) {
519
            return $this->{$this->createdByAttribute} = Number::guid_bin($user);
520
        }
521
        if (strlen($user) == 16) {
522
            return $this->{$this->createdByAttribute} = $user;
523
        }
524
        return false;
525
    }
526
527
    /**
528
     * Get updater who updated this blameable model recently.
529
     * NOTICE! This method will not check whether `$userClass` exists. You should
530
     * specify it in `init()` method.
531
     * @return BaseUserQuery user.
532
     */
533 1
    public function getUpdater()
534
    {
535 1
        if (!is_string($this->updatedByAttribute) || empty($this->updatedByAttribute)) {
536
            return null;
537
        }
538 1
        $userClass = $this->userClass;
539 1
        $model = $userClass::buildNoInitModel();
540
        /* @var $model BaseUserModel */
541 1
        return $this->hasOne($userClass::className(), [$model->guidAttribute => $this->updatedByAttribute]);
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...
542
    }
543
    
544
    /**
545
     *
546
     * @param BaseUserModel|string $user
547
     * @return boolean
548
     */
549
    public function setUpdater($user)
550
    {
551
        if (!is_string($this->updatedByAttribute) || empty($this->updatedByAttribute)) {
552
            return false;
553
        }
554
        if ($user instanceof $this->userClass || $user instanceof \yii\web\IdentityInterface) {
555
            return $this->{$this->updatedByAttribute} = $user->getGUID();
556
        }
557
        if (is_string($user) && preg_match(Number::GUID_REGEX, $user)) {
558
            return $this->{$this->updatedByAttribute} = Number::guid_bin($user);
559
        }
560
        if (strlen($user) == 16) {
561
            return $this->{$this->updatedByAttribute} = $user;
562
        }
563
        return false;
564
    }
565
566
    /**
567
     * This event is triggered before the model update.
568
     * This method is ONLY used for being triggered by event. DO NOT call,
569
     * override or modify it directly, unless you know the consequences.
570
     * @param ModelEvent $event
571
     */
572
    public function onContentChanged($event)
573
    {
574
        $sender = $event->sender;
575
        $sender->resetConfirmation();
576
    }
577
578
    /**
579
     * Return the current user's GUID if current model doesn't specify the owner
580
     * yet, or return the owner's GUID if current model has been specified.
581
     * This method is ONLY used for being triggered by event. DO NOT call,
582
     * override or modify it directly, unless you know the consequences.
583
     * @param ModelEvent $event
584
     * @return string the GUID of current user or the owner.
585
     */
586 12
    public function onGetCurrentUserGuid($event)
587
    {
588 12
        $sender = $event->sender;
589
        /* @var $sender static */
590 12
        if (isset($sender->attributes[$sender->createdByAttribute])) {
591 12
            return $sender->attributes[$sender->createdByAttribute];
0 ignored issues
show
Bug introduced by
The property attributes 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...
592
        }
593
        $identity = \Yii::$app->user->identity;
594
        /* @var $identity BaseUserModel */
595
        if ($identity) {
596
            return $identity->getGUID();
597
        }
598
    }
599
600
    /**
601
     * Initialize type of content. the first of element[index is 0] of
602
     * $contentTypes will be used.
603
     * @param ModelEvent $event
604
     */
605 3
    public function onInitContentType($event)
606
    {
607 3
        $sender = $event->sender;
608
        /* @var $sender static */
609 3
        if (!is_string($sender->contentTypeAttribute) || empty($sender->contentTypeAttribute)) {
610
            return;
611
        }
612 3
        $contentTypeAttribute = $sender->contentTypeAttribute;
613 3
        if (!isset($sender->$contentTypeAttribute) &&
614 3
            !empty($sender->contentTypes) &&
615 3
            is_array($sender->contentTypes)) {
616 3
            $sender->$contentTypeAttribute = $sender->contentTypes[0];
617 3
        }
618 3
    }
619
620
    /**
621
     * Initialize description property with $initDescription.
622
     * @param ModelEvent $event
623
     */
624 3
    public function onInitDescription($event)
625
    {
626 3
        $sender = $event->sender;
627
        /* @var $sender static */
628 3
        if (!is_string($sender->descriptionAttribute) || empty($sender->descriptionAttribute)) {
629
            return;
630
        }
631 3
        $descriptionAttribute = $sender->descriptionAttribute;
632 3
        if (empty($sender->$descriptionAttribute)) {
633 3
            $sender->$descriptionAttribute = $sender->initDescription;
634 3
        }
635 3
    }
636
637
    /**
638
     * Attach events associated with blameable model.
639
     */
640 12
    public function initBlameableEvents()
641
    {
642 12
        $this->on(static::$eventConfirmationChanged, [$this, "onConfirmationChanged"]);
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...
643 12
        $this->on(static::$eventNewRecordCreated, [$this, "onInitConfirmation"]);
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...
644 12
        $contentTypeAttribute = $this->contentTypeAttribute;
645 12
        if (is_string($contentTypeAttribute) && !empty($contentTypeAttribute) && !isset($this->$contentTypeAttribute)) {
646 3
            $this->on(static::$eventNewRecordCreated, [$this, "onInitContentType"]);
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...
647 3
        }
648 12
        $descriptionAttribute = $this->descriptionAttribute;
649 12
        if (is_string($descriptionAttribute) && !empty($descriptionAttribute) && !isset($this->$descriptionAttribute)) {
650 3
            $this->on(static::$eventNewRecordCreated, [$this, 'onInitDescription']);
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...
651 3
        }
652 12
        $this->on(static::EVENT_BEFORE_UPDATE, [$this, "onContentChanged"]);
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...
653 12
        $this->initSelfBlameableEvents();
654 12
    }
655
656
    /**
657
     * @inheritdoc
658
     */
659 1
    public function enabledFields()
660
    {
661 1
        $fields = parent::enabledFields();
662 1
        if (is_string($this->createdByAttribute) && !empty($this->createdByAttribute)) {
663 1
            $fields[] = $this->createdByAttribute;
664 1
        }
665 1
        if (is_string($this->updatedByAttribute) && !empty($this->updatedByAttribute) &&
666 1
            $this->createdByAttribute != $this->updatedByAttribute) {
667
            $fields[] = $this->updatedByAttribute;
668
        }
669 1
        if (is_string($this->contentAttribute)) {
670 1
            $fields[] = $this->contentAttribute;
671 1
        }
672 1
        if (is_array($this->contentAttribute)) {
673
            $fields = array_merge($fields, $this->contentAttribute);
674
        }
675 1
        if (is_string($this->descriptionAttribute)) {
676
            $fields[] = $this->descriptionAttribute;
677
        }
678 1
        if (is_string($this->confirmationAttribute)) {
679
            $fields[] = $this->confirmationAttribute;
680
        }
681 1
        if (is_string($this->parentAttribute)) {
682
            $fields[] = $this->parentAttribute;
683
        }
684 1
        return $fields;
685
    }
686
687
    /**
688
     * Find all follows by specified identity. If `$identity` is null, the logged-in
689
     * identity will be taken.
690
     * @param string|integer $pageSize If it is 'all`, then will find all follows,
691
     * the `$currentPage` parameter will be skipped. If it is integer, it will be
692
     * regarded as sum of models in one page.
693
     * @param integer $currentPage The current page number, begun with 0.
694
     * @param {$this->userClass} $identity
0 ignored issues
show
Documentation introduced by
The doc-type {$this->userClass} could not be parsed: Unknown type name "{$this-" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
695
     * @return static[] If no follows, null will be given, or return follow array.
696
     */
697 1
    public static function findAllByIdentityInBatch($pageSize = 'all', $currentPage = 0, $identity = null)
698
    {
699 1
        if ($pageSize === 'all') {
700 1
            return static::findByIdentity($identity)->all();
701
        }
702 1
        return static::findByIdentity($identity)->page($pageSize, $currentPage)->all();
703
    }
704
705
    /**
706
     * Find one follow by specified identity. If `$identity` is null, the logged-in
707
     * identity will be taken. If $identity doesn't has the follower, null will
708
     * be given.
709
     * @param integer $id user id.
710
     * @param boolean $throwException
711
     * @param {$this->userClass} $identity
0 ignored issues
show
Documentation introduced by
The doc-type {$this->userClass} could not be parsed: Unknown type name "{$this-" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
712
     * @return static
713
     * @throws InvalidParamException
714
     */
715 1
    public static function findOneById($id, $throwException = true, $identity = null)
716
    {
717 1
        $query = static::findByIdentity($identity);
718 1
        if (!empty($id)) {
719 1
            $query = $query->id($id);
720 1
        }
721 1
        $model = $query->one();
722 1
        if (!$model && $throwException) {
723 1
            throw new InvalidParamException('Model Not Found.');
724
        }
725 1
        return $model;
726
    }
727
728
    /**
729
     * Get total of follows of specified identity.
730
     * @param {$this->userClass} $identity
0 ignored issues
show
Documentation introduced by
The doc-type {$this->userClass} could not be parsed: Unknown type name "{$this-" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
731
     * @return integer total.
732
     */
733 1
    public static function countByIdentity($identity = null)
734
    {
735 1
        return (int)(static::findByIdentity($identity)->count());
736
    }
737
738
    /**
739
     * Get pagination, used for building contents page by page.
740
     * @param integer $limit
741
     * @param {$this->userClass} $identity
0 ignored issues
show
Documentation introduced by
The doc-type {$this->userClass} could not be parsed: Unknown type name "{$this-" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
742
     * @return Pagination
743
     */
744
    public static function getPagination($limit = 10, $identity = null)
745
    {
746
        $limit = (int) $limit;
747
        $count = static::countByIdentity($identity);
748
        if ($limit > $count) {
749
            $limit = $count;
750
        }
751
        return new Pagination(['totalCount' => $count, 'pageSize' => $limit]);
752
    }
753
}
754