Passed
Push — master ( b5549b...886b4c )
by vistart
04:08
created

UserOrganizationTrait::createBaseOrganization()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 19
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 3.0032

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 13
cts 14
cp 0.9286
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 15
nc 3
nop 8
crap 3.0032

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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\permissions\SetUpOrganization;
18
use rhosocial\organization\rbac\permissions\SetUpDepartment;
19
use rhosocial\organization\rbac\permissions\RevokeOrganization;
20
use rhosocial\organization\rbac\permissions\RevokeDepartment;
21
use rhosocial\organization\rbac\roles\DepartmentAdmin;
22
use rhosocial\organization\rbac\roles\DepartmentCreator;
23
use rhosocial\organization\rbac\roles\OrganizationAdmin;
24
use rhosocial\organization\rbac\roles\OrganizationCreator;
25
use Yii;
26
use yii\base\Event;
27
use yii\base\InvalidConfigException;
28
use yii\base\InvalidParamException;
29
30
/**
31
 * @property string $guidAttribute GUID Attribute.
32
 * @property-read Member[] $ofMembers
33
 * @property-read Organization[] $atOrganizations
34
 * @property-read Organization[] $creatorsAtOrganizations
35
 * @property-read Organization[] $administratorsAtOrganizations
36
 *
37
 * @version 1.0
38
 * @author vistart <[email protected]>
39
 */
40
trait UserOrganizationTrait
41
{
42
    public $organizationClass = Organization::class;
43
    public $memberClass = Member::class;
44
    private $noInitOrganization;
45
    private $noInitMember;
46
    public $lastSetUpOrganization;
47
    /**
48
     * @return Organization
49
     */
50
    protected function getNoInitOrganization()
51
    {
52
        if (!$this->noInitOrganization) {
53
            $class = $this->organizationClass;
54
            $this->noInitOrganization = $class::buildNoInitModel();
55
        }
56
        return $this->noInitOrganization;
57
    }
58
    /**
59
     * @return Member
60
     */
61 28
    protected function getNoInitMember()
62
    {
63 28
        if (!$this->noInitMember) {
64 28
            $class = $this->memberClass;
65 28
            $this->noInitMember = $class::buildNoInitModel();
66
        }
67 28
        return $this->noInitMember;
68
    }
69
70
    /**
71
     * 
72
     * @return MemberQuery
73
     */
74 28
    public function getOfMembers()
75
    {
76 28
        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...
77
    }
78
79
    /**
80
     * 
81
     * @return MemberQuery
82
     */
83 16
    public function getOfCreators()
84
    {
85 16
        return $this->getOfMembers()->andWhere(['role' => [(new DepartmentCreator)->name, (new OrganizationCreator)->name]]);
86
    }
87
88
    /**
89
     * 
90
     * @return MemberQuery
91
     */
92 2
    public function getOfAdministrators()
93
    {
94 2
        return $this->getOfMembers()->andWhere(['role' => [(new DepartmentAdmin)->name, (new OrganizationAdmin)->name]]);
95
    }
96
97
    /**
98
     * Get query of organization of which this user has been a member.
99
     * If you access this method as magic-property `atOrganizations`, you will
100
     * get all organizations the current user has joined in.
101
     * @return OrganizationQuery
102
     */
103 12
    public function getAtOrganizations()
104
    {
105 12
        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...
106
    }
107
108
    /**
109
     * 
110
     * @return OrganizationQuery
111
     */
112 16
    public function getCreatorsAtOrganizations()
113
    {
114 16
        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...
115
    }
116
117
    /**
118
     * 
119
     * @return OrganizationQuery
120
     */
121 2
    public function getAdministratorsAtOrganizations()
122
    {
123 2
        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...
124
    }
125
126
    /**
127
     * Set up organization.
128
     * @param string $name
129
     * @param string $nickname
130
     * @param integer $gravatar_type
131
     * @param string $gravatar
132
     * @param string $timezone
133
     * @param string $description
134
     * @return boolean Whether indicate the setting-up succeeded or not.
135
     * @throws InvalidParamException
136
     * @throws \Exception
137
     */
138 35
    public function setUpOrganization($name, $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...
139
    {
140 35
        $accessChecker = Yii::$app->authManager;
141 35
        if (!$accessChecker->checkAccess($this, (new SetUpOrganization)->name)) {
142
            throw new InvalidParamException("You do not have permission to set up organization.");
143
        }
144 35
        $transaction = Yii::$app->db->beginTransaction();
145
        try {
146 35
            $models = $this->createOrganization($name, null, $nickname = '', $gravatar_type = 0, $gravatar = '', $timezone = 'UTC', $description = '');
147 35
            $this->setUpBaseOrganization($models);
148 34
            $transaction->commit();
149 1
        } catch (\Exception $ex) {
150 1
            $transaction->rollBack();
151 1
            Yii::error($ex->getMessage(), __METHOD__);
152 1
            throw $ex;
153
        }
154 34
        $this->lastSetUpOrganization = is_array($models) ? $models[0] : $models;
155 34
        return true;
156
    }
157
158
    /**
159
     * Set up organization.
160
     * @param string $name
161
     * @param Organization $parent
162
     * @param string $nickname
163
     * @param integer $gravatar_type
164
     * @param string $gravatar
165
     * @param string $timezone
166
     * @param string $description
167
     * @return boolean Whether indicate the setting-up succeeded or not.
168
     * @throws InvalidParamException
169
     * @throws \Exception
170
     */
171 8
    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...
172
    {
173 8
        if (!($parent instanceof $this->organizationClass)) {
174 1
            throw new InvalidParamException('Invalid Parent Parameter.');
175
        }
176 7
        $accessChecker = Yii::$app->authManager;
177 7
        if (!$accessChecker->checkAccess($this, (new SetUpDepartment)->name, ['organization' => $parent])) {
178 1
            throw new InvalidParamException("You do not have permission to set up department.");
179
        }
180 7
        $transaction = Yii::$app->db->beginTransaction();
181
        try {
182 7
            $models = $this->createDepartment($name, $parent, $nickname = '', $gravatar_type = 0, $gravatar = '', $timezone = 'UTC', $description = '');
183 7
            $this->setUpBaseOrganization($models);
184 6
            $transaction->commit();
185 1
        } catch (\Exception $ex) {
186 1
            $transaction->rollBack();
187 1
            Yii::error($ex->getMessage(), __METHOD__);
188 1
            throw $ex;
189
        }
190 6
        $this->lastSetUpOrganization = is_array($models) ? $models[0] : $models;
191 6
        return true;
192
    }
193
194
    /**
195
     * Set up base organization.
196
     * @param Organization $models
197
     * @return boolean
198
     * @throws InvalidConfigException
199
     * @throws \Exception
200
     */
201 35
    protected function setUpBaseOrganization($models)
202
    {
203 35
        $model = null;
204 35
        $associatedModels = [];
205 35
        if (is_array($models)) {
206 2
            if (!array_key_exists(0, $models)) {
207 2
                throw new InvalidConfigException('Invalid Organization Model.');
208
            }
209
            $model = $models[0];
210
            $associatedModels = array_key_exists('associatedModels', $models) ? $models['associatedModels'] : [];
211 34
        } elseif ($models instanceof $this->organizationClass) {
212 34
            $model = $models;
213
        }
214 34
        $result = $model->register($associatedModels);
215 34
        if ($result instanceof \Exception) {
216
            throw $result;
217
        }
218 34
        if ($result !== true) {
219
            throw new \Exception('Failed to set up.');
220
        }
221 34
        return true;
222
    }
223
224
    /**
225
     * Create organization.
226
     * @param string $name
227
     * @param Organization $parent
228
     * @param string $nickname
229
     * @param string $gravatar_type
230
     * @param string $gravatar
231
     * @param string $timezone
232
     * @param string $description
233
     * @return Organization
234
     */
235 34
    public function createOrganization($name, $parent = null, $nickname = '', $gravatar_type = 0, $gravatar = '', $timezone = 'UTC', $description = '')
236
    {
237 34
        return $this->createBaseOrganization($name, $parent, $nickname, $gravatar_type, $gravatar, $timezone, $description);
238
    }
239
240
    /**
241
     * Create department.
242
     * @param string $name
243
     * @param Organization $parent
244
     * @param string $nickname
245
     * @param string $gravatar_type
246
     * @param string $gravatar
247
     * @param string $timezone
248
     * @param string $description
249
     * @return Organization
250
     */
251 6
    public function createDepartment($name, $parent = null, $nickname = '', $gravatar_type = 0, $gravatar = '', $timezone = 'UTC', $description = '')
252
    {
253 6
        return $this->createBaseOrganization($name, $parent, $nickname, $gravatar_type, $gravatar, $timezone, $description, Organization::TYPE_DEPARTMENT);
254
    }
255
256
    /**
257
     * Create Base Organization.
258
     * @param string $name
259
     * @param Organization $parent
260
     * @param string $nickname
261
     * @param integer $gravatar_type
262
     * @param string $gravatar
263
     * @param string $timezone
264
     * @param string $description
265
     * @param integer $type
266
     * @return Organization
267
     * @throws InvalidParamException throw if setting parent failed. Possible reasons include:
268
     * - The parent is itself.
269
     * - The parent has already been its ancestor.
270
     * - The current organization has reached the limit of ancestors.
271
     */
272 34
    protected function createBaseOrganization($name, $parent = null, $nickname = '', $gravatar_type = 0, $gravatar = '', $timezone = 'UTC', $description = '', $type = Organization::TYPE_ORGANIZATION)
273
    {
274 34
        $class = $this->organizationClass;
275
        $profileConfig = [
276 34
            'name' => $name,
277 34
            'nickname' => $nickname,
278 34
            'gravatar_type' => $gravatar_type,
279 34
            'gravatar' => $gravatar,
280 34
            'timezone' => $timezone,
281 34
            'description' => $description,
282
        ];
283 34
        $organization = new $class(['type' => $type, 'creatorModel' => $this, 'profileConfig' => $profileConfig]);
284 34
        if (empty($parent)) {
285 34
            $organization->setNullParent();
286 6
        } elseif ($organization->setParent($parent) === false) {
287
            throw new InvalidParamException("Failed to set parent.");
288
        }
289 34
        return $organization;
290
    }
291
292
    /**
293
     * Revoke organization or department.
294
     * @param static|string|integer $organization
295
     * @param boolean $revokeIfHasChildren
296
     * @throws InvalidParamException throw if current user is not the creator of organization.
297
     */
298 9
    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...
299
    {
300 9
        if (!($organization instanceof $this->organizationClass))
301
        {
302 2
            $class = $this->organizationClass;
303 2
            if (is_numeric($organization)) {
304 1
                $organization = $class::find()->id($organization)->one();
305 1
            } elseif (is_string($organization)) {
306 1
                $organization = $class::find()->guid($organization)->one();
307
            }
308
        }
309 9
        if (!($organization instanceof $this->organizationClass)) {
310
            throw new InvalidParamException('Invalid Organization.');
311
        }
312 9
        if (!Yii::$app->authManager->checkAccess(
313
                $this,
314 9
                $organization->type == Organization::TYPE_ORGANIZATION ? (new RevokeOrganization)->name : (new RevokeDepartment)->name,
315 9
                ['organization' => $organization])) {
316 1
            throw new InvalidParamException("You do not have permission to revoke it.");
317
        }
318 8
        $transaction = Yii::$app->db->beginTransaction();
319
        try {
320 8
            $result = $organization->deregister();
321 8
            if ($result instanceof \Exception){
322
                throw $result;
323
            }
324 8
            if ($result !== true) {
325
                throw new InvalidParamException("Failed to revoke.");
326
            }
327 8
            $transaction->commit();
328
        } catch (\Exception $ex) {
329
            $transaction->rollBack();
330
            Yii::error($ex->getMessage(), __METHOD__);
331
            throw $ex;
332
        }
333 8
        return true;
334
    }
335
336
    /**
337
     * 
338
     * @param Organization $organization
339
     */
340 14
    public function isOrganizationCreator($organization)
341
    {
342 14
        $member = $organization->getMember($this);
343 14
        if (!$member) {
344 2
            return false;
345
        }
346 14
        return $member->isCreator();
347
    }
348
349
    /**
350
     * 
351
     * @param Organization $organization
352
     */
353 5
    public function isOrganizationAdministrator($organization)
354
    {
355 5
        $member = $organization->getMember($this);
356 5
        if (!$member) {
357 3
            return false;
358
        }
359 5
        return $member->isAdministrator();
360
    }
361
362
    /**
363
     * Attach events associated with organization.
364
     */
365 36
    public function initOrganizationEvents()
366
    {
367 36
        $this->on(static::EVENT_BEFORE_DELETE, [$this, "onRevokeOrganizationsByCreator"]);
0 ignored issues
show
Bug introduced by
It seems like on() 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...
368 36
    }
369
370
    /**
371
     * Revoke Organization Event.
372
     * It should be triggered when deleting (not deregistering).
373
     * @param Event $event
374
     */
375 15
    public function onRevokeOrganizationsByCreator($event)
376
    {
377 15
        $sender = $event->sender;
378
        /* @var $sender static */
379 15
        $organizations = $this->creatorsAtOrganizations;
380 15
        foreach ($organizations as $org)
381
        {
382 2
            $sender->revokeOrganization($org);
383
        }
384 15
    }
385
}
386