Completed
Push — master ( 0281fd...d368a1 )
by vistart
06:50
created

BlameableTrait   D

Complexity

Total Complexity 114

Size/Duplication

Total Lines 716
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 83.14%

Importance

Changes 1
Bugs 0 Features 1
Metric Value
wmc 114
lcom 1
cbo 9
dl 0
loc 716
ccs 212
cts 255
cp 0.8314
rs 4.4444
c 1
b 0
f 1

35 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 setContent() 0 14 4
A getContentCanBeEdited() 0 7 2
A getBlameableRulesCacheKey() 0 4 1
A getBlameableRulesCacheTag() 0 4 1
B getBlameableRules() 0 26 5
C getBlameableAttributeRules() 0 30 7
A getDescriptionRules() 0 16 3
C getContentRules() 0 31 8
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 4 1
A getHost() 0 6 1
B setHost() 0 13 6
A setUser() 0 4 1
A getUpdater() 0 10 3
B setUpdater() 0 16 8
A onContentChanged() 0 6 1
A onGetCurrentUserGuid() 0 13 3
B onInitContentType() 0 14 6
A onInitDescription() 0 12 4
B initBlameableEvents() 0 15 7
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 BserUserModel $host
44
 * @property BaseUserModel $user
45
 * @property BaseUserModel $updater
46
 * @version 1.0
47
 * @author vistart <[email protected]>
48
 */
49
trait BlameableTrait
50
{
51
    use ConfirmationTrait,
52
        SelfBlameableTrait;
53
54
    private $blameableLocalRules = [];
55
    private $blameableLocalBehaviors = [];
56
57
    /**
58
     * @var boolean|string|array Specify the attribute(s) name of content(s). If
59
     * there is only one content attribute, you can assign its name. Or there
60
     * is multiple attributes associated with contents, you can assign their
61
     * names in array. If you don't want to use this feature, please assign
62
     * false.
63
     * For example:
64
     * ```php
65
     * public $contentAttribute = 'comment'; // only one field named as 'comment'.
66
     * ```
67
     * or
68
     * ```php
69
     * public $contentAttribute = ['year', 'month', 'day']; // multiple fields.
70
     * ```
71
     * or
72
     * ```php
73
     * public $contentAttribute = false; // no need of this feature.
74
     * ```
75
     * If you don't need this feature, you should add rules corresponding with
76
     * `content` in `rules()` method of your user model by yourself.
77
     */
78
    public $contentAttribute = 'content';
79
80
    /**
81
     * @var array built-in validator name or validatation method name and
82
     * additional parameters.
83
     */
84
    public $contentAttributeRule = ['string', 'max' => 255];
85
86
    /**
87
     * @var boolean|string Specify the field which stores the type of content.
88
     */
89
    public $contentTypeAttribute = false;
90
91
    /**
92
     * @var boolean|array Specify the logic type of content, not data type. If
93
     * your content doesn't need this feature. please specify false. If the
94
     * $contentAttribute is specified to false, this attribute will be skipped.
95
     * ```php
96
     * public $contentTypes = [
97
     *     'public',
98
     *     'private',
99
     *     'friend',
100
     * ];
101
     * ```
102
     */
103
    public $contentTypes = false;
104
105
    /**
106
     * @var boolean|string This attribute speicfy the name of description
107
     * attribute. If this attribute is assigned to false, this feature will be
108
     * skipped.
109
     */
110
    public $descriptionAttribute = false;
111
112
    /**
113
     * @var string
114
     */
115
    public $initDescription = '';
116
117
    /**
118
     * @var string the attribute that will receive current user ID value. This
119
     * attribute must be assigned.
120
     */
121
    public $createdByAttribute = "user_guid";
122
123
    /**
124
     * @var string the attribute that will receive current user ID value.
125
     * Set this property to false if you do not want to record the updater ID.
126
     */
127
    public $updatedByAttribute = "user_guid";
128
129
    /**
130
     * @var boolean Add combinated unique rule if assigned to true.
131
     */
132
    public $idCreatorCombinatedUnique = true;
133
134
    /**
135
     * @var boolean|string The name of user class which own the current entity.
136
     * If this attribute is assigned to false, this feature will be skipped, and
137
     * when you use create() method of UserTrait, it will be assigned with
138
     * current user class.
139
     */
140
    //public $userClass;
141
    
142
    /**
143
     * @var boolean|string 
144
     */
145
    public $hostClass;
146
    public static $cacheKeyBlameableRules = 'blameable_rules';
147
    public static $cacheTagBlameableRules = 'tag_blameable_rules';
148
    public static $cacheKeyBlameableBehaviors = 'blameable_behaviors';
149
    public static $cacheTagBlameableBehaviors = 'tag_blameable_behaviors';
150
151
    /**
152
     * @inheritdoc
153
     * ------------
154
     * The classical rules is like following:
155
     * [
156
     *     ['guid', 'required'],
157
     *     ['guid', 'unique'],
158
     *     ['guid', 'string', 'max' => 36],
159
     *
160
     *     ['id', 'required'],
161
     *     ['id', 'unique'],
162
     *     ['id', 'string', 'max' => 4],
163
     *
164
     *     ['created_at', 'safe'],
165
     *     ['updated_at', 'safe'],
166
     *
167
     *     ['ip_type', 'in', 'range' => [4, 6]],
168
     *     ['ip', 'number', 'integerOnly' => true, 'min' => 0],
169
     * ]
170
     * @return array
171
     */
172 122
    public function rules()
173
    {
174 122
        return $this->getBlameableRules();
175
    }
176
177
    /**
178
     * @inheritdoc
179
     */
180 129
    public function behaviors()
181
    {
182 129
        return $this->getBlameableBehaviors();
183
    }
184
185
    /**
186
     * Get total of contents which owned by their owner.
187
     * @return integer
188
     */
189 1
    public function countOfOwner()
190
    {
191 1
        $createdByAttribute = $this->createdByAttribute;
192 1
        return static::find()->where([$createdByAttribute => $this->$createdByAttribute])->count();
193
    }
194
195
    /**
196
     * Get content.
197
     * @return mixed
198
     */
199 2
    public function getContent()
200
    {
201 2
        $contentAttribute = $this->contentAttribute;
202 2
        if ($contentAttribute === false) {
203
            return null;
204
        }
205 2
        if (is_array($contentAttribute)) {
206
            $content = [];
207
            foreach ($contentAttribute as $key => $value) {
208
                $content[$key] = $this->$value;
209
            }
210
            return $content;
211
        }
212 2
        return $this->$contentAttribute;
213
    }
214
215
    /**
216
     * Set content.
217
     * @param mixed $content
218
     */
219 50
    public function setContent($content)
220
    {
221 50
        $contentAttribute = $this->contentAttribute;
222 50
        if ($contentAttribute === false) {
223
            return;
224
        }
225 50
        if (is_array($contentAttribute)) {
226
            foreach ($contentAttribute as $key => $value) {
227
                $this->$value = $content[$key];
228
            }
229
            return;
230
        }
231 50
        $this->$contentAttribute = $content;
232 50
    }
233
234
    /**
235
     * Determines whether content could be edited. Your should implement this
236
     * method by yourself.
237
     * @return boolean
238
     * @throws NotSupportedException
239
     */
240
    public function getContentCanBeEdited()
241
    {
242
        if ($this->contentAttribute === false) {
243
            return false;
244
        }
245
        throw new NotSupportedException("This method is not implemented.");
246
    }
247
248
    /**
249
     * Get blameable rules cache key.
250
     * @return string cache key.
251
     */
252 122
    public function getBlameableRulesCacheKey()
253
    {
254 122
        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...
255
    }
256
257
    /**
258
     * Get blameable rules cache tag.
259
     * @return string cache tag
260
     */
261 122
    public function getBlameableRulesCacheTag()
262
    {
263 122
        return static::class . $this->cachePrefix . static::$cacheTagBlameableRules;
264
    }
265
266
    /**
267
     * Get the rules associated with content to be blamed.
268
     * @return array rules.
269
     */
270 122
    public function getBlameableRules()
271
    {
272 122
        $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...
273 122
        if ($cache) {
274 122
            $this->blameableLocalRules = $cache->get($this->getBlameableRulesCacheKey());
275
        }
276
        // 若当前规则不为空,且是数组,则认为是规则数组,直接返回。
277 122
        if (!empty($this->blameableLocalRules) && is_array($this->blameableLocalRules)) {
278 60
            return $this->blameableLocalRules;
279
        }
280
281
        // 父类规则与确认规则合并。
282 122
        if ($cache) {
283 122
            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...
284
        }
285 122
        $rules = array_merge(
286 122
            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...
287 122
            $this->getConfirmationRules(),
288 122
            $this->getBlameableAttributeRules(),
289 122
            $this->getDescriptionRules(),
290 122
            $this->getContentRules(),
291 122
            $this->getSelfBlameableRules()
292
        );
293 122
        $this->setBlameableRules($rules);
294 122
        return $this->blameableLocalRules;
295
    }
296
297
    /**
298
     * Get the rules associated with `createdByAttribute`, `updatedByAttribute`
299
     * and `idAttribute`-`createdByAttribute` combination unique.
300
     * @return array rules.
301
     */
302 122
    public function getBlameableAttributeRules()
303
    {
304 122
        $rules = [];
305
        // 创建者和上次修改者由 BlameableBehavior 负责,因此标记为安全。
306 122
        if (!is_string($this->createdByAttribute) || empty($this->createdByAttribute)) {
307
            throw new NotSupportedException('You must assign the creator.');
308
        }
309 122
        $rules[] = [
310 122
            [$this->createdByAttribute],
311 122
            'safe',
312
        ];
313
314 122
        if (is_string($this->updatedByAttribute) && !empty($this->updatedByAttribute)) {
315 67
            $rules[] = [
316 67
                [$this->updatedByAttribute],
317 67
                'safe',
318
            ];
319
        }
320
321 122
        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...
322 122
            $rules ['id'] = [
323 122
                [$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...
324 122
                    $this->createdByAttribute],
325 122
                'unique',
326 122
                '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...
327 122
                    $this->createdByAttribute],
328
            ];
329
        }
330 122
        return $rules;
331
    }
332
333
    /**
334
     * Get the rules associated with `description` attribute.
335
     * @return array rules.
336
     */
337 122
    public function getDescriptionRules()
338
    {
339 122
        $rules = [];
340 122
        if (is_string($this->descriptionAttribute) && !empty($this->descriptionAttribute)) {
341 55
            $rules[] = [
342 55
                [$this->descriptionAttribute],
343 55
                'string'
344
            ];
345 55
            $rules[] = [
346 55
                [$this->descriptionAttribute],
347 55
                'default',
348 55
                'value' => $this->initDescription,
349
            ];
350
        }
351 122
        return $rules;
352
    }
353
354
    /**
355
     * Get the rules associated with `content` and `contentType` attributes.
356
     * @return array rules.
357
     */
358 122
    public function getContentRules()
359
    {
360 122
        if (!$this->contentAttribute) {
361 40
            return [];
362
        }
363 90
        $rules = [];
364 90
        $rules[] = [$this->contentAttribute, 'required'];
365 90
        if ($this->contentAttributeRule) {
366 90
            if (is_string($this->contentAttributeRule)) {
367
                $this->contentAttributeRule = [$this->contentAttributeRule];
368
            }
369 90
            if (is_array($this->contentAttributeRule)) {
370 90
                $rules[] = array_merge([$this->contentAttribute], $this->contentAttributeRule);
371
            }
372
        }
373
374 90
        if (!$this->contentTypeAttribute) {
375 75
            return $rules;
376
        }
377
378 15
        if (is_array($this->contentTypes) && !empty($this->contentTypes)) {
379 15
            $rules[] = [[
380 15
                $this->contentTypeAttribute],
381 15
                'required'];
382 15
            $rules[] = [[
383 15
                $this->contentTypeAttribute],
384 15
                'in',
385 15
                'range' => array_keys($this->contentTypes)];
386
        }
387 15
        return $rules;
388
    }
389
390
    /**
391
     * Set blameable rules.
392
     * @param array $rules
393
     */
394 122
    protected function setBlameableRules($rules = [])
395
    {
396 122
        $this->blameableLocalRules = $rules;
397 122
        $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...
398 122
        if ($cache) {
399 122
            $tagDependency = new TagDependency(['tags' => [$this->getBlameableRulesCacheTag()]]);
400 122
            $cache->set($this->getBlameableRulesCacheKey(), $rules, 0, $tagDependency);
401
        }
402 122
    }
403
404
    /**
405
     * Get blameable behaviors cache key.
406
     * @return string cache key.
407
     */
408 129
    public function getBlameableBehaviorsCacheKey()
409
    {
410 129
        return static::class . $this->cachePrefix . static::$cacheKeyBlameableBehaviors;
411
    }
412
413
    /**
414
     * Get blameable behaviors cache tag.
415
     * @return string cache tag.
416
     */
417 129
    public function getBlameableBehaviorsCacheTag()
418
    {
419 129
        return static::class . $this->cachePrefix . static::$cacheTagBlameableBehaviors;
420
    }
421
422
    /**
423
     * Get blameable behaviors. If current behaviors array is empty, the init
424
     * array will be given.
425
     * @return array
426
     */
427 129
    public function getBlameableBehaviors()
428
    {
429 129
        $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...
430 129
        if ($cache) {
431 129
            $this->blameableLocalBehaviors = $cache->get($this->getBlameableBehaviorsCacheKey());
432
        }
433 129
        if (empty($this->blameableLocalBehaviors) || !is_array($this->blameableLocalBehaviors)) {
434 129
            if ($cache) {
435 129
                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...
436
            }
437 129
            $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...
438 129
            $behaviors['blameable'] = [
439 129
                'class' => BlameableBehavior::class,
440 129
                'createdByAttribute' => $this->createdByAttribute,
441 129
                'updatedByAttribute' => $this->updatedByAttribute,
442 129
                'value' => [$this,
443 129
                    'onGetCurrentUserGuid'],
444
            ];
445 129
            $this->setBlameableBehaviors($behaviors);
446
        }
447 129
        return $this->blameableLocalBehaviors;
448
    }
449
450
    /**
451
     * Set blameable behaviors.
452
     * @param array $behaviors
453
     */
454 129
    protected function setBlameableBehaviors($behaviors = [])
455
    {
456 129
        $this->blameableLocalBehaviors = $behaviors;
457 129
        $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...
458 129
        if ($cache) {
459 129
            $tagDependencyConfig = ['tags' => [$this->getBlameableBehaviorsCacheTag()]];
460 129
            $tagDependency = new TagDependency($tagDependencyConfig);
461 129
            $cache->set($this->getBlameableBehaviorsCacheKey(), $behaviors, 0, $tagDependency);
462
        }
463 129
    }
464
465
    /**
466
     * Set description.
467
     * @return string description.
468
     */
469 1
    public function getDescription()
470
    {
471 1
        $descAttribute = $this->descriptionAttribute;
472 1
        return is_string($descAttribute) ? $this->$descAttribute : null;
473
    }
474
475
    /**
476
     * Get description.
477
     * @param string $desc description.
478
     * @return string|null description if enabled, or null if disabled.
479
     */
480 1
    public function setDescription($desc)
481
    {
482 1
        $descAttribute = $this->descriptionAttribute;
483 1
        return is_string($descAttribute) ? $this->$descAttribute = $desc : null;
484
    }
485
486
    /**
487
     * Get blame who owned this blameable model.
488
     * NOTICE! This method will not check whether `$userClass` exists. You should
489
     * specify it in `init()` method.
490
     * @return BaseUserQuery user.
491
     */
492 9
    public function getUser()
493
    {
494 9
        return $this->getHost();
495
    }
496
    
497
    /**
498
     * 
499
     * @return BaseUserQuery
500
     */
501 26
    public function getHost()
502
    {
503 26
        $hostClass = $this->hostClass;
504 26
        $model = $hostClass::buildNoInitModel();
505 26
        return $this->hasOne($hostClass::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 string $host
511
     * @return type
512
     */
513 117
    public function setHost($host)
514
    {
515 117
        if ($host instanceof $this->hostClass || $host instanceof \yii\web\IdentityInterface) {
516 82
            return $this->{$this->createdByAttribute} = $host->getGUID();
517
        }
518 43
        if (is_string($host) && preg_match(Number::GUID_REGEX, $host)) {
519
            return $this->{$this->createdByAttribute} = Number::guid_bin($host);
520
        }
521 43
        if (strlen($host) == 16) {
522 43
            return $this->{$this->createdByAttribute} = $host;
523
        }
524
        return false;
525
    }
526
    
527
    /**
528
     *
529
     * @param BaseUserModel|string $user
530
     * @return boolean
531
     */
532 4
    public function setUser($user)
533
    {
534 4
        return $this->setHost($user);
535
    }
536
537
    /**
538
     * Get updater who updated this blameable model recently.
539
     * NOTICE! This method will not check whether `$userClass` exists. You should
540
     * specify it in `init()` method.
541
     * @return BaseUserQuery user.
542
     */
543 1
    public function getUpdater()
544
    {
545 1
        if (!is_string($this->updatedByAttribute) || empty($this->updatedByAttribute)) {
546
            return null;
547
        }
548 1
        $hostClass = $this->hostClass;
549 1
        $model = $hostClass::buildNoInitModel();
550
        /* @var $model BaseUserModel */
551 1
        return $this->hasOne($hostClass::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...
552
    }
553
    
554
    /**
555
     *
556
     * @param BaseUserModel|string $user
0 ignored issues
show
Bug introduced by
There is no parameter named $user. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
557
     * @return boolean
558
     */
559
    public function setUpdater($updater)
560
    {
561
        if (!is_string($this->updatedByAttribute) || empty($this->updatedByAttribute)) {
562
            return false;
563
        }
564
        if ($updater instanceof $this->hostClass || $updater instanceof \yii\web\IdentityInterface) {
565
            return $this->{$this->updatedByAttribute} = $updater->getGUID();
566
        }
567
        if (is_string($updater) && preg_match(Number::GUID_REGEX, $updater)) {
568
            return $this->{$this->updatedByAttribute} = Number::guid_bin($updater);
569
        }
570
        if (strlen($updater) == 16) {
571
            return $this->{$this->updatedByAttribute} = $updater;
572
        }
573
        return false;
574
    }
575
576
    /**
577
     * This event is triggered before the model update.
578
     * This method is ONLY used for being triggered by event. DO NOT call,
579
     * override or modify it directly, unless you know the consequences.
580
     * @param ModelEvent $event
581
     */
582 21
    public function onContentChanged($event)
583
    {
584 21
        $sender = $event->sender;
585
        /* @var $sender static */
586 21
        return $sender->resetConfirmation();
587
    }
588
589
    /**
590
     * Return the current user's GUID if current model doesn't specify the owner
591
     * yet, or return the owner's GUID if current model has been specified.
592
     * This method is ONLY used for being triggered by event. DO NOT call,
593
     * override or modify it directly, unless you know the consequences.
594
     * @param ModelEvent $event
595
     * @return string the GUID of current user or the owner.
596
     */
597 110
    public function onGetCurrentUserGuid($event)
598
    {
599 110
        $sender = $event->sender;
600
        /* @var $sender static */
601 110
        if (isset($sender->attributes[$sender->createdByAttribute])) {
602 110
            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...
603
        }
604
        $identity = \Yii::$app->user->identity;
605
        /* @var $identity BaseUserModel */
606
        if ($identity) {
607
            return $identity->getGUID();
608
        }
609
    }
610
611
    /**
612
     * Initialize type of content. the first of element[index is 0] of
613
     * $contentTypes will be used.
614
     * @param ModelEvent $event
615
     */
616 18
    public function onInitContentType($event)
617
    {
618 18
        $sender = $event->sender;
619
        /* @var $sender static */
620 18
        if (!is_string($sender->contentTypeAttribute) || empty($sender->contentTypeAttribute)) {
621
            return;
622
        }
623 18
        $contentTypeAttribute = $sender->contentTypeAttribute;
624 18
        if (!isset($sender->$contentTypeAttribute) &&
625 18
            !empty($sender->contentTypes) &&
626 18
            is_array($sender->contentTypes)) {
627 18
            $sender->$contentTypeAttribute = $sender->contentTypes[0];
628
        }
629 18
    }
630
631
    /**
632
     * Initialize description property with $initDescription.
633
     * @param ModelEvent $event
634
     */
635 61
    public function onInitDescription($event)
636
    {
637 61
        $sender = $event->sender;
638
        /* @var $sender static */
639 61
        if (!is_string($sender->descriptionAttribute) || empty($sender->descriptionAttribute)) {
640
            return;
641
        }
642 61
        $descriptionAttribute = $sender->descriptionAttribute;
643 61
        if (empty($sender->$descriptionAttribute)) {
644 61
            $sender->$descriptionAttribute = $sender->initDescription;
645
        }
646 61
    }
647
648
    /**
649
     * Attach events associated with blameable model.
650
     */
651 129
    public function initBlameableEvents()
652
    {
653 129
        $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...
654 129
        $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...
655 129
        $contentTypeAttribute = $this->contentTypeAttribute;
656 129
        if (is_string($contentTypeAttribute) && !empty($contentTypeAttribute) && !isset($this->$contentTypeAttribute)) {
657 18
            $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...
658
        }
659 129
        $descriptionAttribute = $this->descriptionAttribute;
660 129
        if (is_string($descriptionAttribute) && !empty($descriptionAttribute) && !isset($this->$descriptionAttribute)) {
661 61
            $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...
662
        }
663 129
        $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...
664 129
        $this->initSelfBlameableEvents();
665 129
    }
666
667
    /**
668
     * @inheritdoc
669
     */
670 32
    public function enabledFields()
671
    {
672 32
        $fields = parent::enabledFields();
673 32
        if (is_string($this->createdByAttribute) && !empty($this->createdByAttribute)) {
674 32
            $fields[] = $this->createdByAttribute;
675
        }
676 32
        if (is_string($this->updatedByAttribute) && !empty($this->updatedByAttribute) &&
677 32
            $this->createdByAttribute != $this->updatedByAttribute) {
678
            $fields[] = $this->updatedByAttribute;
679
        }
680 32
        if (is_string($this->contentAttribute)) {
681 32
            $fields[] = $this->contentAttribute;
682
        }
683 32
        if (is_array($this->contentAttribute)) {
684
            $fields = array_merge($fields, $this->contentAttribute);
685
        }
686 32
        if (is_string($this->descriptionAttribute)) {
687 1
            $fields[] = $this->descriptionAttribute;
688
        }
689 32
        if (is_string($this->confirmationAttribute)) {
690 1
            $fields[] = $this->confirmationAttribute;
691
        }
692 32
        if (is_string($this->parentAttribute)) {
693
            $fields[] = $this->parentAttribute;
694
        }
695 32
        return $fields;
696
    }
697
698
    /**
699
     * Find all follows by specified identity. If `$identity` is null, the logged-in
700
     * identity will be taken.
701
     * @param string|integer $pageSize If it is 'all`, then will find all follows,
702
     * the `$currentPage` parameter will be skipped. If it is integer, it will be
703
     * regarded as sum of models in one page.
704
     * @param integer $currentPage The current page number, begun with 0.
705
     * @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...
706
     * @return static[] If no follows, null will be given, or return follow array.
707
     */
708 1
    public static function findAllByIdentityInBatch($pageSize = 'all', $currentPage = 0, $identity = null)
709
    {
710 1
        if ($pageSize === 'all') {
711 1
            return static::findByIdentity($identity)->all();
712
        }
713 1
        return static::findByIdentity($identity)->page($pageSize, $currentPage)->all();
714
    }
715
716
    /**
717
     * Find one follow by specified identity. If `$identity` is null, the logged-in
718
     * identity will be taken. If $identity doesn't has the follower, null will
719
     * be given.
720
     * @param integer $id user id.
721
     * @param boolean $throwException
722
     * @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...
723
     * @return static
724
     * @throws InvalidParamException
725
     */
726 1
    public static function findOneById($id, $throwException = true, $identity = null)
727
    {
728 1
        $query = static::findByIdentity($identity);
729 1
        if (!empty($id)) {
730 1
            $query = $query->id($id);
731
        }
732 1
        $model = $query->one();
733 1
        if (!$model && $throwException) {
734 1
            throw new InvalidParamException('Model Not Found.');
735
        }
736 1
        return $model;
737
    }
738
739
    /**
740
     * Get total of follows of specified identity.
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 integer total.
743
     */
744 1
    public static function countByIdentity($identity = null)
745
    {
746 1
        return (int)(static::findByIdentity($identity)->count());
747
    }
748
749
    /**
750
     * Get pagination, used for building contents page by page.
751
     * @param integer $limit
752
     * @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...
753
     * @return Pagination
754
     */
755
    public static function getPagination($limit = 10, $identity = null)
756
    {
757
        $limit = (int) $limit;
758
        $count = static::countByIdentity($identity);
759
        if ($limit > $count) {
760
            $limit = $count;
761
        }
762
        return new Pagination(['totalCount' => $count, 'pageSize' => $limit]);
763
    }
764
}
765