Completed
Push — master ( 82df8e...bcf569 )
by vistart
74:36 queued 71:28
created

User::getProfile()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

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