Completed
Push — master ( 15c8d0...bf148f )
by vistart
04:55
created

RegistrationTrait   B

Complexity

Total Complexity 37

Size/Duplication

Total Lines 223
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 74.67%

Importance

Changes 2
Bugs 0 Features 1
Metric Value
wmc 37
lcom 1
cbo 3
dl 0
loc 223
ccs 56
cts 75
cp 0.7467
rs 8.6
c 2
b 0
f 1

8 Methods

Rating   Name   Duplication   Size   Complexity  
A getAuthManager() 0 5 2
D register() 0 45 18
C deregister() 0 29 7
A getSource() 0 5 2
A setSource() 0 5 2
A getSourceRules() 0 10 2
A setSourceRules() 0 6 3
A onInitSourceAttribute() 0 6 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\base\models\traits;
14
15
use Yii;
16
use yii\base\ModelEvent;
17
use yii\db\Exception;
18
use yii\db\IntegrityException;
19
use yii\rbac\BaseManager;
20
use yii\rbac\Role;
21
22
/**
23
 * User features concerning registration.
24
 *
25
 * @property-read mixed $authManager
26
 * @property mixed $source
27
 * @property array $sourceRules rules associated with source attribute.
28
 * @version 1.0
29
 * @author vistart <[email protected]>
30
 */
31
trait RegistrationTrait
32
{
33
34
    /**
35
     * @event Event an event that is triggered after user is registered successfully.
36
     */
37
    public static $eventAfterRegister = "afterRegister";
38
39
    /**
40
     * @event Event an event that is triggered before registration.
41
     */
42
    public static $eventBeforeRegister = "beforeRegister";
43
44
    /**
45
     * @event Event an event that is triggered when registration failed.
46
     */
47
    public static $eventRegisterFailed = "registerFailed";
48
49
    /**
50
     * @event Event an event that is triggered after user is deregistered successfully.
51
     */
52
    public static $eventAfterDeregister = "afterDeregister";
53
54
    /**
55
     * @event Event an event that is triggered before deregistration.
56
     */
57
    public static $eventBeforeDeregister = "beforeDeregister";
58
59
    /**
60
     * @event Event an event that is triggered when deregistration failed.
61
     */
62
    public static $eventDeregisterFailed = "deregisterFailed";
63
64
    /**
65
     * @var string name of attribute which store the source. if you don't want to
66
     * record source, please assign false.
67
     */
68
    public $sourceAttribute = 'source';
69
    private $_sourceRules = [];
70
    public static $sourceSelf = '0';
71
72
    /**
73
     * @var string auth manager component id.
74
     */
75
    public $authManagerId = 'authManager';
76
77
    /**
78
     * Get auth manager. If auth manager not configured, Yii::$app->authManager
79
     * will be given.
80
     * @return BaseManager
81
     */
82 125
    public function getAuthManager()
83
    {
84 125
        $authManagerId = $this->authManagerId;
85 125
        return empty($authManagerId) ? Yii::$app->authManager : Yii::$app->$authManagerId;
86
    }
87
88
    /**
89
     * Register new user.
90
     * It is equivalent to store the current user and its associated models into
91
     * database synchronously. The registration will be terminated immediately
92
     * if any errors occur in the process, and all the earlier steps succeeded
93
     * are rolled back.
94
     * If auth manager configured, and auth role(s) provided, it(they) will be
95
     * assigned to user after registration.
96
     * If current user is not a new one(isNewRecord = false), the registration
97
     * will be skipped and return false.
98
     * The $eventBeforeRegister will be triggered before registration starts.
99
     * If registration finished, the $eventAfterRegister will be triggered. or
100
     * $eventRegisterFailed will be triggered when any errors occured.
101
     * @param array $associatedModels The models associated with user to be stored synchronously.
102
     * @param string|array $authRoles auth name, auth instance, auth name array or auth instance array.
103
     * @return boolean Whether the registration succeeds or not.
104
     * @throws IntegrityException when inserting user and associated models failed.
105
     */
106 126
    public function register($associatedModels = [], $authRoles = [])
107
    {
108 126
        if (!$this->getIsNewRecord()) {
0 ignored issues
show
Bug introduced by
It seems like getIsNewRecord() 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...
109 3
            return false;
110
        }
111 126
        $this->trigger(static::$eventBeforeRegister);
0 ignored issues
show
Bug introduced by
It seems like trigger() 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...
112 126
        $transaction = $this->getDb()->beginTransaction();
0 ignored issues
show
Bug introduced by
It seems like getDb() 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...
113
        try {
114 126
            if (!$this->save()) {
0 ignored issues
show
Bug introduced by
It seems like save() 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...
115 1
                throw new IntegrityException('Registration Error(s) Occured: User Save Failed.', $this->getErrors());
0 ignored issues
show
Bug introduced by
It seems like getErrors() 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...
116
            }
117 125
            if ($authManager = $this->getAuthManager() && !empty($authRoles)) {
118
                if (is_string($authRoles) || $authRoles instanceof Role || !is_array($authRoles)) {
119
                    $authRoles = [$authRoles];
120
                }
121
                foreach ($authRoles as $role) {
122
                    if (is_string($role)) {
123
                        $role = $authManager->getRole($role);
0 ignored issues
show
Bug introduced by
The method getRole cannot be called on $authManager (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
124
                    }
125
                    if ($role instanceof Role) {
126
                        $authManager->assign($role, $this->getGUID());
0 ignored issues
show
Bug introduced by
It seems like getGUID() 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...
Bug introduced by
The method assign cannot be called on $authManager (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
127
                    }
128
                }
129
            }
130 125
            if (!empty($associatedModels) && is_array($associatedModels)) {
131 51
                foreach ($associatedModels as $model) {
132 51
                    if (!$model->save()) {
133 51
                        throw new IntegrityException('Registration Error(s) Occured: Associated Models Save Failed.', $model->getErrors());
134
                    }
135
                }
136
            }
137 125
            $transaction->commit();
138 1
        } catch (\Exception $ex) {
139 1
            $transaction->rollBack();
140 1
            $this->trigger(static::$eventRegisterFailed);
0 ignored issues
show
Bug introduced by
It seems like trigger() 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...
141 1
            if (YII_DEBUG || YII_ENV !== YII_ENV_PROD) {
142 1
                Yii::error($ex->getMessage(), static::class . '\register');
143 1
                return $ex;
144
            }
145
            Yii::warning($ex->getMessage(), static::class . '\register');
146
            return false;
147
        }
148 125
        $this->trigger(static::$eventAfterRegister);
0 ignored issues
show
Bug introduced by
It seems like trigger() 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...
149 125
        return true;
150
    }
151
152
    /**
153
     * Deregister current user itself.
154
     * It is equivalent to delete current user and its associated models. BUT it
155
     * deletes current user ONLY, the associated models will not be deleted
156
     * forwardly. So you should set the foreign key of associated models' table
157
     * referenced from primary key of user table, and their association mode is
158
     * 'on update cascade' and 'on delete cascade'.
159
     * the $eventBeforeDeregister will be triggered before deregistration starts.
160
     * if deregistration finished, the $eventAfterDeregister will be triggered. or
161
     * $eventDeregisterFailed will be triggered when any errors occured.
162
     * @return boolean Whether deregistration succeeds or not.
163
     * @throws IntegrityException when deleting user failed.
164
     */
165 125
    public function deregister()
166
    {
167 125
        if ($this->getIsNewRecord()) {
0 ignored issues
show
Bug introduced by
It seems like getIsNewRecord() 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...
168 7
            return false;
169
        }
170 124
        $this->trigger(static::$eventBeforeDeregister);
0 ignored issues
show
Bug introduced by
It seems like trigger() 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...
171 124
        $transaction = $this->getDb()->beginTransaction();
0 ignored issues
show
Bug introduced by
It seems like getDb() 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...
172
        try {
173 124
            $result = $this->delete();
0 ignored issues
show
Bug introduced by
It seems like delete() 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...
174 124
            if ($result == 0) {
175
                throw new IntegrityException('User has not existed.');
176
            }
177 124
            if ($result != 1) {
178
                throw new IntegrityException('Deregistration Error(s) Occured.', $this->getErrors());
0 ignored issues
show
Bug introduced by
It seems like getErrors() 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...
179
            }
180 124
            $transaction->commit();
181
        } catch (\Exception $ex) {
182
            $transaction->rollBack();
183
            $this->trigger(static::$eventDeregisterFailed);
0 ignored issues
show
Bug introduced by
It seems like trigger() 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...
184
            if (YII_DEBUG || YII_ENV !== YII_ENV_PROD) {
185
                Yii::error($ex->getMessage(), static::class . '\deregister');
186
                return $ex;
187
            }
188
            Yii::warning($ex->getMessage(), static::class . '\deregister');
189
            return false;
190
        }
191 124
        $this->trigger(static::$eventAfterDeregister);
0 ignored issues
show
Bug introduced by
It seems like trigger() 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...
192 124
        return $result == 1;
193
    }
194
195
    /**
196
     * Get source.
197
     * @return string
198
     */
199 1
    public function getSource()
200
    {
201 1
        $sourceAttribute = $this->sourceAttribute;
202 1
        return is_string($sourceAttribute) ? $this->$sourceAttribute : null;
203
    }
204
205
    /**
206
     * Set source.
207
     * @param string $source
208
     */
209 2
    public function setSource($source)
210
    {
211 2
        $sourceAttribute = $this->sourceAttribute;
212 2
        return is_string($sourceAttribute) ? $this->$sourceAttribute = $source : null;
213
    }
214
215
    /**
216
     * Get the rules associated with source attribute.
217
     * @return array rules.
218
     */
219 136
    public function getSourceRules()
220
    {
221 136
        if (empty($this->_sourceRules)) {
222 135
            $this->_sourceRules = [
223 135
                [[$this->sourceAttribute], 'required'],
224 135
                [[$this->sourceAttribute], 'string'],
225
            ];
226
        }
227 136
        return $this->_sourceRules;
228
    }
229
230
    /**
231
     * Set the rules associated with source attribute.
232
     * @param array $rules
233
     */
234 1
    public function setSourceRules($rules)
235
    {
236 1
        if (!empty($rules) && is_array($rules)) {
237 1
            $this->_sourceRules = $rules;
238
        }
239 1
    }
240
241
    /**
242
     * Initialize the source attribute with $sourceSelf.
243
     * This method is ONLY used for being triggered by event. DO NOT call,
244
     * override or modify it directly, unless you know the consequences.
245
     * @param ModelEvent $event
246
     */
247 142
    public function onInitSourceAttribute($event)
248
    {
249 142
        $sender = $event->sender;
250 142
        $sourceAttribute = $sender->sourceAttribute;
251 142
        $sender->$sourceAttribute = static::$sourceSelf;
252 142
    }
253
}
254