Completed
Push — master ( a859d8...30587f )
by vistart
08:26
created

IDTrait::generateId()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5.0187

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 10
cts 11
cp 0.9091
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 12
nc 4
nop 0
crap 5.0187
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
use rhosocial\base\helpers\Number;
15
use Yii;
16
use yii\base\ModelEvent;
17
18
/**
19
 * Entity features concerning ID.
20
 * @property-read array $idRules
21
 * @property mixed $ID
22
 * @version 1.0
23
 * @author vistart <[email protected]>
24
 */
25
trait IDTrait
26
{
27
    /**
28
     * @var string OPTIONAL. The attribute that will receive the IDentifier No.
29
     * You can set this property to false if you don't use this feature.
30
     */
31
    public $idAttribute = 'id';
32
    public static $idTypeString = 0;
33
    public static $idTypeInteger = 1;
34
    public static $idTypeAutoIncrement = 2;
35
    
36
    /**
37
     * @var integer type of id attribute.
38
     */
39
    public $idAttributeType = 0;
40
    
41
    /**
42
     * @var boolean Determines whether its ID has been pre-assigned. It will not
43
     * generate or assign ID if true.
44
     */
45
    public $idPreassigned = false;
46
    
47
    /**
48
     * @var string The prefix of ID. When ID type is Auto Increment, this feature
49
     * is skipped.
50
     */
51
    public $idAttributePrefix = '';
52
    
53
    /**
54
     * @var integer OPTIONAL. The length of id attribute value, and max length
55
     * of this attribute in rules. If you set $idAttribute to false or ID type
56
     * to Auto Increment, this property will be ignored.
57
     */
58
    public $idAttributeLength = 4;
59
    
60
    /**
61
     * @var boolean Determine whether the ID is safe for validation.
62
     */
63
    protected $idAttributeSafe = false;
64
    
65
    /**
66
     * Get ID.
67
     * @return string|integer
68
     */
69 29
    public function getID()
70
    {
71 29
        $idAttribute = $this->idAttribute;
72 29
        return (is_string($idAttribute) && !empty($idAttribute)) ? $this->$idAttribute : null;
73
    }
74
    
75
    /**
76
     * Set id.
77
     * @param string|integer $identity
78
     * @return string|integer
79
     */
80 356
    public function setID($identity)
81
    {
82 356
        $idAttribute = $this->idAttribute;
83 356
        return (is_string($idAttribute) && !empty($idAttribute)) ? $this->$idAttribute = $identity : null;
84
    }
85
    
86
    /**
87
     * Attach `onInitGuidAttribute` event.
88
     * @param string $eventName
89
     */
90 371
    protected function attachInitIDEvent($eventName)
91
    {
92 371
        $this->on($eventName, [$this, 'onInitIDAttribute']);
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...
93 371
    }
94
    
95
    /**
96
     * Initialize the ID attribute with new generated ID.
97
     * If the model's id is pre-assigned, then it will return directly.
98
     * If the model's id is auto-increment, the id attribute will be marked safe.
99
     * This method is ONLY used for being triggered by event. DO NOT call,
100
     * override or modify it directly, unless you know the consequences.
101
     * @param ModelEvent $event
102
     */
103 371
    public function onInitIDAttribute($event)
104
    {
105 371
        $sender = $event->sender;
106
        /* @var $sender static */
107 371
        if ($sender->idPreassigned) {
108 225
            return;
109
        }
110 371
        if ($sender->idAttributeType === static::$idTypeAutoIncrement) {
111 75
            $sender->idAttributeSafe = true;
112 75
            return;
113
        }
114 356
        $idAttribute = $sender->idAttribute;
115 356
        if (is_string($idAttribute) && !empty($idAttribute) &&
116 356
            is_int($sender->idAttributeLength) &&
117 356
            $sender->idAttributeLength > 0) {
118 356
            $sender->setID($sender->generateId());
0 ignored issues
show
Bug introduced by
It seems like $sender->generateId() targeting rhosocial\base\models\traits\IDTrait::generateId() can also be of type false or null; however, rhosocial\base\models\traits\IDTrait::setID() does only seem to accept string|integer, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

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

An additional type check may prevent trouble.

Loading history...
119
        }
120 356
    }
121
    
122
    /**
123
     * Generate the ID. You can override this method to implement your own
124
     * generation algorithm.
125
     * @return string the generated ID.
126
     */
127 356
    public function generateId()
128
    {
129 356
        if ($this->idAttributeType == static::$idTypeInteger) {
130
            do {
131 294
                $result = Number::randomNumber($this->idAttributePrefix, $this->idAttributeLength);
132 294
            } while ($this->checkIdExists((int) $result));
133 294
            return $result;
134
        }
135 204
        if ($this->idAttributeType == static::$idTypeString) {
136 204
            return $this->idAttributePrefix .
137 204
                Yii::$app->security->generateRandomString($this->idAttributeLength - strlen($this->idAttributePrefix));
138
        }
139 1
        if ($this->idAttributeType == static::$idTypeAutoIncrement) {
140 1
            return null;
141
        }
142
        return false;
143
    }
144
    
145
    /**
146
     * Check if $identity existed.
147
     * @param mixed $identity
148
     * @return boolean
149
     */
150 295
    public function checkIdExists($identity)
151
    {
152 295
        if ($identity == null) {
153 4
            return false;
154
        }
155 295
        return (static::findOne([$this->idAttribute => $identity]) !== null);
156
    }
157
    
158
    /**
159
     * Get the rules associated with id attribute.
160
     * @return array
161
     */
162 346
    public function getIdRules()
163
    {
164 346
        if ($this->idAttribute == false) {
165
            return [];
166
        }
167 346
        if ($this->idAttributeSafe || $this->idAttributeType === static::$idTypeAutoIncrement) {
168
            return [
169 74
                [[$this->idAttribute], 'safe'],
170
            ];
171
        }
172 330
        if (is_string($this->idAttribute) && !empty($this->idAttribute) &&
173 330
            is_int($this->idAttributeLength) &&
174 330
            $this->idAttributeLength > 0) {
175
            $rules = [
176 330
                [[$this->idAttribute], 'required'],
177 330
                [[$this->idAttribute], 'unique'],
178
            ];
179 330
            if ($this->idAttributeType === static::$idTypeInteger) {
180 283
                $rules[] = [
181 283
                    [$this->idAttribute], 'number', 'integerOnly' => true
182
                ];
183
            }
184 330
            if ($this->idAttributeType === static::$idTypeString) {
185 93
                $rules[] = [[$this->idAttribute], 'string',
186 93
                    'max' => $this->idAttributeLength,];
187
            }
188 330
            return $rules;
189
        }
190
        return [];
191
    }
192
    
193 1
    public static function compositeIDs($models)
194
    {
195 1
        if (!is_array($models) && $models instanceof static) {
196 1
            return $models->getID();
197
        }
198 1
        $ids = [];
199 1
        foreach ($models as $model) {
200 1
            if ($model instanceof static) {
201 1
                $ids[] = $model->getID();
202
            }
203
        }
204 1
        return $ids;
205
    }
206
}
207
208