Completed
Push — master ( b11769...ebfd49 )
by vistart
03:50
created

UserOrganizationTrait   B

Complexity

Total Complexity 41

Size/Duplication

Total Lines 303
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 71.17%

Importance

Changes 1
Bugs 0 Features 1
Metric Value
wmc 41
c 1
b 0
f 1
lcom 1
cbo 8
dl 0
loc 303
ccs 79
cts 111
cp 0.7117
rs 8.2769

17 Methods

Rating   Name   Duplication   Size   Complexity  
A getNoInitOrganization() 0 8 2
A getNoInitMember() 0 8 2
A getOfMembers() 0 4 1
A getOfCreators() 0 4 1
A getOfAdministrators() 0 4 1
A getAtOrganizations() 0 4 1
A getCreatorsAtOrganizations() 0 4 1
A getAdministratorsAtOrganizations() 0 4 1
A setUpOrganization() 0 15 3
A setUpDepartment() 0 18 4
C setUpBaseOrganization() 0 22 7
A createOrganization() 0 4 1
A createDepartment() 0 4 1
A createBaseOrganization() 0 19 3
C revokeOrganization() 0 31 8
A isOrganizationCreator() 0 8 2
A isOrganizationAdministrator() 0 8 2

How to fix   Complexity   

Complex Class

Complex classes like UserOrganizationTrait often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use UserOrganizationTrait, and based on these observations, apply Extract Interface, too.

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\organization;
14
15
use rhosocial\organization\queries\MemberQuery;
16
use rhosocial\organization\queries\OrganizationQuery;
17
use rhosocial\organization\rbac\roles\DepartmentCreator;
18
use rhosocial\organization\rbac\roles\OrganizationCreator;
19
use Yii;
20
use yii\base\InvalidConfigException;
21
use yii\base\InvalidParamException;
22
23
/**
24
 * @property string $guidAttribute GUID Attribute.
25
 * @property-read Member[] $ofMembers
26
 * @property-read Organization[] $atOrganizations
27
 *
28
 * @version 1.0
29
 * @author vistart <[email protected]>
30
 */
31
trait UserOrganizationTrait
32
{
33
    public $organizationClass = Organization::class;
34
    public $memberClass = Member::class;
35
    private $noInitOrganization;
36
    private $noInitMember;
37
    public $lastSetUpOrganization;
38
    /**
39
     * @return Organization
40
     */
41
    protected function getNoInitOrganization()
42
    {
43
        if (!$this->noInitOrganization) {
44
            $class = $this->organizationClass;
45
            $this->noInitOrganization = $class::buildNoInitModel();
46
        }
47
        return $this->noInitOrganization;
48
    }
49
    /**
50
     * @return Member
51
     */
52 17
    protected function getNoInitMember()
53
    {
54 17
        if (!$this->noInitMember) {
55 17
            $class = $this->memberClass;
56 17
            $this->noInitMember = $class::buildNoInitModel();
57
        }
58 17
        return $this->noInitMember;
59
    }
60
61
    /**
62
     * 
63
     * @return MemberQuery
64
     */
65 17
    public function getOfMembers()
66
    {
67 17
        return $this->hasMany($this->memberClass, [$this->getNoInitMember()->memberAttribute => $this->guidAttribute])->inverseOf('memberUser');
0 ignored issues
show
Bug introduced by
It seems like hasMany() 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...
68
    }
69
70
    /**
71
     * 
72
     * @return MemberQuery
73
     */
74
    public function getOfCreators()
75
    {
76
        return $this->getOfMembers()->andWhere(['role' => [(new DepartmentCreator)->name, (new OrganizationCreator)->name]]);
77
    }
78
79
    /**
80
     * 
81
     * @return MemberQuery
82
     */
83
    public function getOfAdministrators()
84
    {
85
        return $this->getOfMembers()->andWhere(['role' => [(new DepartmentAdmin)->name, (new OrganizationAdmin)->name]]);
86
    }
87
88
    /**
89
     * 
90
     * @return OrganizationQuery
91
     */
92 8
    public function getAtOrganizations()
93
    {
94 8
        return $this->hasMany($this->organizationClass, [$this->guidAttribute => $this->getNoInitMember()->createdByAttribute])->via('ofMembers');
0 ignored issues
show
Bug introduced by
It seems like hasMany() 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...
95
    }
96
97
    /**
98
     * 
99
     * @return OrganizationQuery
100
     */
101
    public function getCreatorsAtOrganizations()
102
    {
103
        return $this->hasMany($this->organizationClass, [$this->guidAttribute => $this->getNoInitMember()->createdByAttribute])->via('ofCreators');
0 ignored issues
show
Bug introduced by
It seems like hasMany() 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...
104
    }
105
106
    /**
107
     * 
108
     * @return OrganizationQuery
109
     */
110
    public function getAdministratorsAtOrganizations()
111
    {
112
        return $this->hasMany($this->organizationClass, [$this->guidAttribute => $this->getNoInitMember()->createdByAttribute])->via('ofAdministrators');
0 ignored issues
show
Bug introduced by
It seems like hasMany() 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
    }
114
115
    /**
116
     * Set up organization.
117
     * @param string $name
118
     * @param Organization $parent
119
     * @param string $nickname
120
     * @param integer $gravatar_type
121
     * @param string $gravatar
122
     * @param string $timezone
123
     * @param string $description
124
     * @return boolean Whether indicate the setting-up succeeded or not.
125
     */
126 24
    public function setUpOrganization($name, $parent = null, $nickname = '', $gravatar_type = 0, $gravatar = '', $timezone = 'UTC', $description = '')
0 ignored issues
show
Unused Code introduced by
The parameter $nickname is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $gravatar_type is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $gravatar is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $timezone is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $description is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
127
    {
128 24
        $transaction = Yii::$app->db->beginTransaction();
129
        try {
130 24
            $models = $this->createOrganization($name, $parent, $nickname = '', $gravatar_type = 0, $gravatar = '', $timezone = 'UTC', $description = '');
131 24
            $this->setUpBaseOrganization($models);
132 23
            $transaction->commit();
133 1
        } catch (\Exception $ex) {
134 1
            $transaction->rollBack();
135 1
            Yii::error($ex->getMessage(), __METHOD__);
136 1
            throw $ex;
137
        }
138 23
        $this->lastSetUpOrganization = is_array($models) ? $models[0] : $models;
139 23
        return true;
140
    }
141
142
    /**
143
     * Set up organization.
144
     * @param string $name
145
     * @param Organization $parent
146
     * @param string $nickname
147
     * @param integer $gravatar_type
148
     * @param string $gravatar
149
     * @param string $timezone
150
     * @param string $description
151
     * @return boolean Whether indicate the setting-up succeeded or not.
152
     */
153 3
    public function setUpDepartment($name, $parent = null, $nickname = '', $gravatar_type = 0, $gravatar = '', $timezone = 'UTC', $description = '')
0 ignored issues
show
Unused Code introduced by
The parameter $nickname is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $gravatar_type is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $gravatar is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $timezone is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $description is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
154
    {
155 3
        if ($parent == null) {
156 1
            throw new InvalidConfigException('Invalid Parent Parameter.');
157
        }
158 2
        $transaction = Yii::$app->db->beginTransaction();
159
        try {
160 2
            $models = $this->createDepartment($name, $parent, $nickname = '', $gravatar_type = 0, $gravatar = '', $timezone = 'UTC', $description = '');
161 2
            $this->setUpBaseOrganization($models);
162 1
            $transaction->commit();
163 1
        } catch (\Exception $ex) {
164 1
            $transaction->rollBack();
165 1
            Yii::error($ex->getMessage(), __METHOD__);
166 1
            throw $ex;
167
        }
168 1
        $this->lastSetUpOrganization = is_array($models) ? $models[0] : $models;
169 1
        return true;
170
    }
171
172
    /**
173
     * Set up base organization.
174
     * @param Organization $models
175
     * @return boolean
176
     * @throws InvalidConfigException
177
     * @throws \Exception
178
     */
179 24
    protected function setUpBaseOrganization($models)
180
    {
181 24
        $model = null;
182 24
        $associatedModels = [];
183 24
        if (is_array($models)) {
184 2
            if (!array_key_exists(0, $models)) {
185 2
                throw new InvalidConfigException('Invalid Organization Model.');
186
            }
187
            $model = $models[0];
188
            $associatedModels = array_key_exists('associatedModels', $models) ? $models['associatedModels'] : [];
189
        } elseif ($models instanceof Organization) {
190 23
            $model = $models;
191
        }
192 23
        $result = $model->register($associatedModels);
193 23
        if ($result instanceof \Exception) {
194
            throw $result;
195
        }
196 23
        if ($result !== true) {
197
            throw new \Exception('Failed to set up.');
198
        }
199 23
        return true;
200
    }
201
202
    /**
203
     * Create organization.
204
     * @param string $name
205
     * @param Organization $parent
206
     * @param string $nickname
207
     * @param string $gravatar_type
208
     * @param string $gravatar
209
     * @param string $timezone
210
     * @param string $description
211
     * @return Organization
212
     */
213 23
    public function createOrganization($name, $parent = null, $nickname = '', $gravatar_type = 0, $gravatar = '', $timezone = 'UTC', $description = '')
214
    {
215 23
        return $this->createBaseOrganization($name, $parent, $nickname, $gravatar_type, $gravatar, $timezone, $description);
216
    }
217
218
    /**
219
     * Create department.
220
     * @param string $name
221
     * @param Organization $parent
222
     * @param string $nickname
223
     * @param string $gravatar_type
224
     * @param string $gravatar
225
     * @param string $timezone
226
     * @param string $description
227
     * @return Organization
228
     */
229 1
    public function createDepartment($name, $parent = null, $nickname = '', $gravatar_type = 0, $gravatar = '', $timezone = 'UTC', $description = '')
230
    {
231 1
        return $this->createBaseOrganization($name, $parent, $nickname, $gravatar_type, $gravatar, $timezone, $description, Organization::TYPE_DEPARTMENT);
232
    }
233
234
    /**
235
     * Create Base Organization.
236
     * @param string $name
237
     * @param Organization $parent
238
     * @param string $nickname
239
     * @param integer $gravatar_type
240
     * @param string $gravatar
241
     * @param string $timezone
242
     * @param string $description
243
     * @param integer $type
244
     * @return Organization
245
     * @throws InvalidParamException throw if setting parent failed. Possible reasons include:
246
     * - The parent is itself.
247
     * - The parent has already been its ancestor.
248
     * - The current organization has reached the limit of ancestors.
249
     */
250 23
    protected function createBaseOrganization($name, $parent = null, $nickname = '', $gravatar_type = 0, $gravatar = '', $timezone = 'UTC', $description = '', $type = Organization::TYPE_ORGANIZATION)
251
    {
252 23
        $class = $this->organizationClass;
253
        $profileConfig = [
254 23
            'name' => $name,
255 23
            'nickname' => $nickname,
256 23
            'gravatar_type' => $gravatar_type,
257 23
            'gravatar' => $gravatar,
258 23
            'timezone' => $timezone,
259 23
            'description' => $description,
260
        ];
261 23
        $organization = new $class(['type' => $type, 'creator' => $this, 'profileConfig' => $profileConfig]);
262 23
        if (empty($parent)) {
263 23
            $organization->setNullParent();
264 4
        } elseif ($organization->setParent($parent) === false) {
265
            throw new InvalidParamException("Failed to set parent.");
266
        }
267 23
        return $organization;
268
    }
269
270
    /**
271
     * Revoke organization.
272
     * @param static|string|integer $organization
273
     * @param boolean $revokeIfHasChildren
274
     * @throws InvalidParamException throw if current user is not the creator of organization.
275
     */
276 1
    public function revokeOrganization($organization, $revokeIfHasChildren = false)
0 ignored issues
show
Unused Code introduced by
The parameter $revokeIfHasChildren is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
277
    {
278 1
        if (!($organization instanceof $this->organizationClass))
279
        {
280
            $class = $this->organizationClass;
281
            if (is_int($organization)) {
282
                $organization = $class::find()->id($organization)->one();
283
            } elseif (is_string($organization)) {
284
                $organization = $class::find()->guid($organization)->one();
285
            }
286
        }
287 1
        if (!$this->isOrganizationCreator($organization)) {
288
            throw new InvalidParamException('You are not the creator of the this organization and have no right to revoke it.');
289
        }
290 1
        $transaction = Yii::$app->db->beginTransaction();
291
        try {
292 1
            $result = $organization->deregister();
293 1
            if ($result instanceof \Exception){
294
                throw $result;
295
            }
296 1
            if ($result !== true) {
297
                throw new InvalidParamException();
298
            }
299 1
            $transaction->commit();
300
        } catch (\Exception $ex) {
301
            $transaction->rollBack();
302
            Yii::error($ex->getMessage(), __METHOD__);
303
            throw $ex;
304
        }
305 1
        return true;
306
    }
307
308
    /**
309
     * 
310
     * @param Organization $organization
311
     */
312 1
    public function isOrganizationCreator($organization)
313
    {
314 1
        $member = $organization->getMember($this);
315 1
        if (!$member) {
316
            return false;
317
        }
318 1
        return $member->isCreator();
319
    }
320
321
    /**
322
     * 
323
     * @param Organization $organization
324
     */
325 2
    public function isOrganizationAdministrator($organization)
326
    {
327 2
        $member = $organization->getMember($this);
328 2
        if (!$member) {
329 2
            return false;
330
        }
331 2
        return $member->isAdministrator();
332
    }
333
}
334