Completed
Push — master ( c17da1...c000da )
by vistart
04:06
created

User   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 243
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 8

Test Coverage

Coverage 90.91%

Importance

Changes 2
Bugs 0 Features 1
Metric Value
wmc 26
c 2
b 0
f 1
lcom 3
cbo 8
dl 0
loc 243
rs 10
ccs 70
cts 77
cp 0.9091

14 Methods

Rating   Name   Duplication   Size   Complexity  
A getSearchModel() 0 8 3
A attributeLabels() 0 21 1
A tableName() 0 4 1
A init() 0 8 1
A getCacheTag() 0 5 2
A onInvalidTags() 0 11 2
A createProfile() 0 13 4
A hasProfile() 0 7 4
A getProfile() 0 10 2
A onGenerateId() 0 6 1
A generateIdBehavior() 0 10 1
A behaviors() 0 9 2
A getIdRules() 0 6 1
A find() 0 4 1
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\user;
14
15
use rhosocial\base\models\models\BaseUserModel;
16
use rhosocial\base\models\queries\BaseBlameableQuery;
17
use rhosocial\user\models\log\UserLoginTrait;
18
use rhosocial\user\models\UserUsernameTrait;
19
use rhosocial\user\security\UserPasswordHistoryTrait;
20
use Yii;
21
use yii\base\Event;
22
use yii\base\InvalidConfigException;
23
use yii\behaviors\AttributeBehavior;
24
use yii\caching\TagDependency;
25
use yii\db\ActiveRecord;
26
use yii\widgets\ActiveForm;
27
28
/**
29
 * Common User Model.
30
 * This model should be stored in a relational database. You can create a foreign
31
 * key constraint on other models and this model.
32
 *
33
 * If you're using MySQL, we recommend that you create a data table using the following statement:
34
 *
35
 * ```
36
 * CREATE TABLE `user` (
37
 *   `guid` varbinary(16) NOT NULL COMMENT 'GUID',
38
 *   `id` varchar(16) COLLATE utf8_unicode_ci NOT NULL COMMENT 'ID',
39
 *   `pass_hash` varchar(80) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Password Hash',
40
 *   `ip` varbinary(16) NOT NULL DEFAULT '0' COMMENT 'IP',
41
 *   `ip_type` tinyint(3) unsigned NOT NULL DEFAULT '4' COMMENT 'IP Address Type',
42
 *   `created_at` datetime NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT 'Create Time',
43
 *   `updated_at` datetime NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT 'Update Time',
44
 *   `auth_key` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'Authentication Key',
45
 *   `access_token` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'Access Token',
46
 *   `password_reset_token` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'Password Reset Token',
47
 *   `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT 'Status',
48
 *   `type` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT 'Type',
49
 *   `source` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'Source',
50
 *   PRIMARY KEY (`guid`),
51
 *   UNIQUE KEY `user_id_unique` (`id`),
52
 *   UNIQUE KEY `user_username_unique` (`username`),
53
 *   KEY `user_auth_key_normal` (`auth_key`),
54
 *   KEY `user_access_token_normal` (`access_token`),
55
 *   KEY `user_password_reset_token` (`password_reset_token`),
56
 *   KEY `user_create_time_normal` (`created_at`)
57
 * ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='User';
58
 * ```
59
 *
60
 * The fields of User table in database are following:
61
 * @property string $guid User's GUID. This property is used to uniquely identify a user.
62
 * This property is automatically generated when the class is created, we do not
63
 * recommend that you modify this property, unless you know the consequences of doing so.
64
 * This property is also regareded as the foreign key target of other models associated
65
 * with this model. If you have to modify this property, the foreign key constraints
66
 * should be updating and deleting on cascade.
67
 * @property string $id User Identifier No. It is a 8-digit number beginning with 4 by default.
68
 * @property string $pass_hash Password Hash.
69
 * We strongly recommend you NOT to change this property directly!
70
 * If you want to set or reset password, please use setPassword() magic property instead.
71
 * @property integer $ip IP address.
72
 * @property integer $ipType
73
 * @property string $createdAt
74
 * @property string $updatedAt
75
 * @property string $auth_key
76
 * @property string $access_token
77
 * @property string $password_reset_token
78
 * @property integer $status
79
 * @property integer $type
80
 * @property string $source
81
 *
82
 * @property-read Profile $profile Profile. This magic property is read-only.
83
 * If you want to modify anyone property of Profile model, please get it first,
84
 * then change and save it, like following:
85
 * ```php
86
 * $profile = $user->profile;
87
 * $profile->nickname = 'vistart';
88
 * $profile->save();
89
 * ```
90
 * If $profileClass is `false`, `null` returned.
91
 * @version 1.0
92
 * @author vistart <[email protected]>
93
 */
94
class User extends BaseUserModel
95
{
96
    use UserPasswordHistoryTrait, UserLoginTrait, UserUsernameTrait;
97
98
    /**
99
     * @var string
100
     */
101
    public $searchClass = UserSearch::class;
102
103
    /**
104
     * @return null
105
     */
106
    public function getSearchModel()
107
    {
108
        $class = $this->searchClass;
109
        if (empty($class) || !class_exists($class)) {
110
            return null;
111
        }
112
        return new $class;
113
    }
114
115
    /**
116
     * @inheritdoc
117
     */
118 1
    public function attributeLabels()
119
    {
120
        $labels = [
121 1
            $this->guidAttribute => Yii::t('user', 'GUID'),
122 1
            $this->idAttribute => Yii::t('user', 'ID'),
123 1
            $this->passwordHashAttribute => Yii::t('user', 'Password Hash'),
124 1
            $this->ipAttribute => Yii::t('user', 'IP Address'),
125 1
            $this->ipTypeAttribute => Yii::t('user', 'IP Address Type'),
126 1
            $this->createdAtAttribute => Yii::t('user', 'Creation Time'),
127 1
            $this->updatedAtAttribute => Yii::t('user', 'Last Updated Time'),
128 1
            $this->authKeyAttribute => Yii::t('user', 'Authentication Key'),
129 1
            $this->accessTokenAttribute => Yii::t('user', 'Access Token'),
130 1
            $this->passwordResetTokenAttribute => Yii::t('user', 'Password Reset Token'),
131 1
            $this->statusAttribute => Yii::t('user', 'Status'),
132 1
            'type' => Yii::t('user', 'Type'),
133 1
            $this->sourceAttribute => Yii::t('user', 'Source'),
134 1
            'createdAt' => Yii::t('user', 'Registration Time'),
135 1
            'updatedAt' => Yii::t('user', 'Last Updated Time'),
136
        ];
137 1
        return $labels;
138
    }
139
140
    /**
141
     * @inheritdoc
142
     */
143
    public $idAttributeType = 1;
144
145
    /**
146
     * @inheritdoc
147
     */
148
    public $idAttributeLength = 8;
149
150
    /**
151
     * @inheritdoc
152
     */
153
    public $idAttributePrefix = '4';
154
155
    /**
156
     * @var bool
157
     */
158
    public $idPreassigned = true;
159
160
    /**
161
     * @inheritdoc
162
     */
163 44
    public static function tableName()
164
    {
165 44
        return '{{%user}}';
166
    }
167
168
    /**
169
     * @var string|false Profile class name. If you do not need profile model,
170
     * please set it false.
171
     */
172
    public $profileClass = false;
173
174
    /**
175
     * @inheritdoc
176
     * -----------
177
     * When the user is updated or deleted, the cache contents tagged with the corresponding user tag will be invalidated.
178
     */
179 44
    public function init()
180
    {
181 44
        $this->on(static::$eventAfterRegister, [$this, 'onAddPasswordToHistory']);
182 44
        $this->on(static::$eventAfterResetPassword, [$this, 'onAddPasswordToHistory']);
183 44
        $this->on(static::EVENT_AFTER_UPDATE, [$this, 'onInvalidTags']);
184 44
        $this->on(static::EVENT_AFTER_DELETE, [$this, 'onInvalidTags']);
185 44
        parent::init();
186 44
    }
187
188
    /**
189
     * @var string
190
     */
191
    public $cacheTagPrefix = 'tag_user_';
192
193
    /**
194
     * Get cache tag.
195
     * The cache tag ends with the user ID, but after the user ID is modified, the old ID will prevail.
196
     * @return string
197
     */
198 36
    public function getCacheTag()
199
    {
200 36
        return $this->cacheTagPrefix .
201 36
            ($this->isAttributeChanged($this->idAttribute) ? $this->getOldAttribute($this->idAttribute) : $this->getID());
202
    }
203
204
    /**
205
     * @param Event $event
206
     * @return bool|string|array
207
     */
208 36
    public function onInvalidTags($event)
209
    {
210
        try {
211 36
            $cache = Yii::$app->get('cache');
212
        } catch (InvalidConfigException $ex) {
213
            return true;
214
        }
215 36
        $sender = $event->sender;
216
        /*@var $sender static */
217 36
        return TagDependency::invalidate($cache, $sender->getCacheTag());
218
    }
219
220
    /**
221
     * Create profile.
222
     * If profile of this user exists, it will be returned instead of creating it.
223
     * Meanwhile, the $config parameter will be skipped.
224
     * @param array $config Profile configuration. Skipped if it exists.
225
     * @return Profile
226
     */
227 19
    public function createProfile($config = [])
228
    {
229 19
        $profileClass = $this->profileClass;
230 19
        if (empty($profileClass) || !is_string($this->profileClass)) {
231 1
            return null;
232
        }
233 18
        $profile = $profileClass::findOne($this->getGUID());
234 18
        if (!$profile) {
235 18
            $profile = $this->create($profileClass, $config);
236 18
            $profile->setGUID($this->getGUID());
237
        }
238 18
        return $profile;
239
    }
240
241
    /**
242
     * 
243
     * @return boolean
244
     */
245 4
    public function hasProfile()
246
    {
247 4
        if ($this->profileClass === false || !is_string($this->profileClass) || !class_exists($this->profileClass)) {
248 2
            return false;
249
        }
250 2
        return true;
251
    }
252
253
    /**
254
     * Get Profile query.
255
     * If you want to get profile model, please access this method in magic property way,
256
     * like following:
257
     *
258
     * ```php
259
     * $user->profile;
260
     * ```
261
     *
262
     * @return BaseBlameableQuery
263
     */
264 4
    public function getProfile()
265
    {
266 4
        if (!$this->hasProfile()) {
267 2
            return null;
268
        }
269 2
        $profileClass = $this->profileClass;
270 2
        $profileModel = $profileClass::buildNoInitModel();
271 2
        return $this->hasOne($profileClass,
272 2
                [$profileModel->createdByAttribute => $this->guidAttribute])->inverseOf('user');
273
    }
274
275
    /**
276
     * @param $event
277
     * @return mixed
278
     */
279 39
    public function onGenerateId($event)
280
    {
281 39
        $sender = $event->sender;
282
        /* @var $sender static */
283 39
        return $sender->generateId();
284
    }
285
286
    /**
287
     * @return array
288
     */
289 44
    public function generateIdBehavior()
290
    {
291
        return [
292 44
            'class' => AttributeBehavior::class,
293
            'attributes' => [
294 44
                ActiveRecord::EVENT_BEFORE_INSERT => $this->idAttribute,
295
            ],
296 44
            'value' => [$this, 'onGenerateId'],
297
        ];
298
    }
299
300
    /**
301
     * @return array
302
     */
303 44
    public function behaviors()
304
    {
305 44
        $behaviors = parent::behaviors();
306 44
        $behavior = $this->generateIdBehavior();
307 44
        if (!empty($behavior)) {
308 44
            $behaviors[] = $behavior;
309
        }
310 44
        return $behaviors;
311
    }
312
313
    /**
314
     * @var string
315
     */
316
    public static $idRegex = '\d{5,8}';
317
318
    /**
319
     * @return array
320
     */
321 2
    public function getIdRules()
322
    {
323
        return [
324 2
            [$this->idAttribute, 'safe'],
325
        ];
326
    }
327
328
    /**
329
     * Friendly to IDE.
330
     * @return UserQuery
331
     */
332 39
    public static function find()
333
    {
334 39
        return parent::find();
335
    }
336
}
337