Completed
Push — master ( 1d1fd8...eec31e )
by vistart
05:27
created

RegistrationTrait   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 220
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 71.95%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 36
lcom 1
cbo 3
dl 0
loc 220
ccs 59
cts 82
cp 0.7195
rs 8.8
c 1
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A getAuthManager() 0 5 2
B deregister() 0 26 6
A getSourceRules() 0 10 2
A onInitSourceAttribute() 0 6 1
D register() 0 45 18
A getSource() 0 5 2
A setSource() 0 5 2
A setSourceRules() 0 6 3
1
<?php
2
3
/**
4
 *  _   __ __ _____ _____ ___  ____  _____
5
 * | | / // // ___//_  _//   ||  __||_   _|
6
 * | |/ // /(__  )  / / / /| || |     | |
7
 * |___//_//____/  /_/ /_/ |_||_|     |_|
8
 * @link https://vistart.me/
9
 * @copyright Copyright (c) 2016 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 76
    public function getAuthManager()
83
    {
84 76
        $authManagerId = $this->authManagerId;
85 76
        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 77
    public function register($associatedModels = [], $authRoles = [])
107
    {
108 77
        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 77
        $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 77
        $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 77
            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.', $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 76
            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 76
            if (!empty($associatedModels) && is_array($associatedModels)) {
131 3
                foreach ($associatedModels as $model) {
132 3
                    if (!$model->save()) {
133
                        throw new IntegrityException('Registration Error(s) Occured.', $model->getErrors());
134
                    }
135 3
                }
136 3
            }
137 76
            $transaction->commit();
138 77
        } 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 76
        $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 76
        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 76
    public function deregister()
166
    {
167 76
        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 75
        $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 75
        $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 75
            $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 75
            if ($result != 1) {
175
                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...
176
            }
177 75
            $transaction->commit();
178 75
        } catch (\Exception $ex) {
179
            $transaction->rollBack();
180
            $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...
181
            if (YII_DEBUG || YII_ENV !== YII_ENV_PROD) {
182
                Yii::error($ex->getMessage(), static::class . '\deregister');
183
                return $ex;
184
            }
185
            Yii::warning($ex->getMessage(), static::class . '\deregister');
186
            return false;
187
        }
188 75
        $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...
189 75
        return $result == 1;
190
    }
191
192
    /**
193
     * Get source.
194
     * @return string
195
     */
196 1
    public function getSource()
197
    {
198 1
        $sourceAttribute = $this->sourceAttribute;
199 1
        return is_string($sourceAttribute) ? $this->$sourceAttribute : null;
200
    }
201
202
    /**
203
     * Set source.
204
     * @param string $source
205
     */
206 2
    public function setSource($source)
207
    {
208 2
        $sourceAttribute = $this->sourceAttribute;
209 2
        return is_string($sourceAttribute) ? $this->$sourceAttribute = $source : null;
210
    }
211
212
    /**
213
     * Get the rules associated with source attribute.
214
     * @return array rules.
215
     */
216 87
    public function getSourceRules()
217
    {
218 87
        if (empty($this->_sourceRules)) {
219 86
            $this->_sourceRules = [
220 86
                [[$this->sourceAttribute], 'required'],
221 86
                [[$this->sourceAttribute], 'string'],
222
            ];
223 86
        }
224 87
        return $this->_sourceRules;
225
    }
226
227
    /**
228
     * Set the rules associated with source attribute.
229
     * @param array $rules
230
     */
231 1
    public function setSourceRules($rules)
232
    {
233 1
        if (!empty($rules) && is_array($rules)) {
234 1
            $this->_sourceRules = $rules;
235 1
        }
236 1
    }
237
238
    /**
239
     * Initialize the source attribute with $sourceSelf.
240
     * This method is ONLY used for being triggered by event. DO NOT call,
241
     * override or modify it directly, unless you know the consequences.
242
     * @param ModelEvent $event
243
     */
244 93
    public function onInitSourceAttribute($event)
245
    {
246 93
        $sender = $event->sender;
247 93
        $sourceAttribute = $sender->sourceAttribute;
248 93
        $sender->$sourceAttribute = static::$sourceSelf;
249 93
    }
250
}
251