Completed
Push — master ( 5c5f72...8fe9d2 )
by vistart
07:22
created

Organization::getMemberLimit()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 9
ccs 4
cts 5
cp 0.8
rs 9.6666
cc 2
eloc 5
nc 2
nop 0
crap 2.032
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\base\models\traits\SelfBlameableTrait;
16
use rhosocial\base\models\queries\BaseBlameableQuery;
17
use rhosocial\base\models\queries\BaseUserQuery;
18
use rhosocial\organization\exceptions\DisallowMemberJoinOtherException;
19
use rhosocial\organization\exceptions\ExcludeOtherMembersException;
20
use rhosocial\organization\exceptions\OnlyAcceptCurrentOrgMemberException;
21
use rhosocial\organization\exceptions\OnlyAcceptSuperiorOrgMemberException;
22
use rhosocial\user\User;
23
use rhosocial\organization\rbac\roles\DepartmentAdmin;
24
use rhosocial\organization\rbac\roles\DepartmentCreator;
25
use rhosocial\organization\rbac\roles\OrganizationAdmin;
26
use rhosocial\organization\rbac\roles\OrganizationCreator;
27
use rhosocial\organization\queries\MemberQuery;
28
use rhosocial\organization\queries\OrganizationQuery;
29
use Yii;
30
use yii\base\Event;
31
use yii\base\InvalidParamException;
32
use yii\db\IntegrityException;
33
34
/**
35
 * Base Organization.
36
 * This class is an abstract class that can not be instantiated directly.
37
 * You can use [[Organization]] or [[Department]] instead.
38
 *
39
 * @method Member createMember(array $config) Create member who is subordinate to this.
40
 * @property int $type Whether indicate this instance is an organization or a department.
41
 * @property int $eom Fit for [[$isExcludeOtherMembers]]. Do not modify it directly.
42
 * @property int $djo Fit for [[$isDisallowMemberJoinOther]]. Do not modify it directly.
43
 * @property int $oacm Fit for [[$isOnlyAcceptCurrentOrgMember]]. Do not modify it directly.
44
 * @property int $oasm Fit for [[$isOnlyAcceptSuperiorOrgMember]]. Do not modify it directly.
45
 *
46
 * @property bool $isExcludeOtherMembers Determine whether the other organization and its subordinate departments
47
 * members could join in the current organization and its subordinate departments. (Only fit for Organization)
48
 * @property bool $isDisallowMemberJoinOther Determine whether the current organization and its subordinate
49
 * departments members could join in the other organization and its subordinate departments. (Only fit for Organization)
50
 * @property bool $isOnlyAcceptCurrentOrgMember Determine whether the current department only accept the member of
51
 * the top level organization. (Only fit for Department)
52
 * @property bool $isOnlyAcceptSuperiorOrgMember Determine whether the current department only accept the member of
53
 * the superior organization or department. (Only fit for Department)
54
 *
55
 * @property-read Member[] $members Get all member models of this organization/department.
56
 * @property-read User[] $memberUsers Get all members of this organization/department.
57
 * @property-read User $creator Get creator of this organization/department.
58
 * @property-read User[] $administrators Get administrators of this organization/department.
59
 * @property-read SubordinateLimit subordinateLimit
60
 * @property-read MemberLimit memberLimit
61
 * @property-read static|null $topOrganization The top level organization of current organization or departments.
62
 *
63
 * @version 1.0
64
 * @author vistart <[email protected]>
65
 */
66
class Organization extends User
67
{
68
    use SelfBlameableTrait;
69
70
    const TYPE_ORGANIZATION = 1;
71
    const TYPE_DEPARTMENT = 2;
72
73
    /**
74
     * @var boolean Organization does not need password and corresponding features.
75
     */
76
    public $passwordHashAttribute = false;
77
78
    /**
79
     * @var boolean Organization does not need password and corresponding features.
80
     */
81
    public $passwordResetTokenAttribute = false;
82
83
    /**
84
     * @var boolean Organization does not need password and corresponding features.
85
     */
86
    public $passwordHistoryClass = false;
87
88
    /**
89
     * @var boolean Organization does not need source.
90
     */
91
    public $sourceAttribute = false;
92
93
    /**
94
     * @var boolean Organization does not need auth key.
95
     */
96
    public $authKeyAttribute = false;
97
98
    /**
99
     * @var boolean Organization does not need access token.
100
     */
101
    public $accessTokenAttribute = false;
102
103
    /**
104
     *
105
     * @var boolean Organization does not need login log.
106
     */
107
    public $loginLogClass = false;
108
109
    public $profileClass = Profile::class;
110
111
    public $memberClass = Member::class;
112
    public $subordinateLimitClass = SubordinateLimit::class;
113
    public $memberLimitClass = MemberLimit::class;
114
    public $searchClass = OrganizationSearch::class;
115
    /**
116
     * @var Member
117
     */
118
    private $noInitMember;
119
    /**
120
     * @var SubordinateLimit
121
     */
122
    private $noInitSubordinateLimit;
123
    /**
124
     * @var MemberLimit
125
     */
126
    private $noInitMemberLimit;
127
    public $creatorModel;
128
    public $profileConfig;
129
130
    const EVENT_BEFORE_ADD_MEMBER = 'eventBeforeAddMember';
131
    const EVENT_AFTER_ADD_MEMBER = 'eventAfterAddMember';
132
    const EVENT_BEFORE_REMOVE_MEMBER = 'eventBeforeRemoveMember';
133
    const EVENT_AFTER_REMOVE_MEMBER = 'eventAfterRemoveMember';
134
135
    /**
136
     * @return Member
137
     */
138 51
    public function getNoInitMember()
139
    {
140 51
        if (!$this->noInitMember) {
141 51
            $class = $this->memberClass;
142 51
            $this->noInitMember = $class::buildNoInitModel();
143
        }
144 51
        return $this->noInitMember;
145
    }
146
147
    /**
148
     * @return SubordinateLimit
149
     */
150 2
    public function getNoInitSubordinateLimit()
151
    {
152 2
        if (!$this->noInitSubordinateLimit) {
153 2
            $class = $this->subordinateLimitClass;
154 2
            $this->noInitSubordinateLimit = $class::buildNoInitModel();
155
        }
156 2
        return $this->noInitSubordinateLimit;
157
    }
158
159
    /**
160
     * @return MemberLimit
161
     */
162 1
    public function getNoInitMemberLimit()
163
    {
164 1
        if (!$this->noInitMemberLimit) {
165 1
            $class = $this->memberLimitClass;
166 1
            $this->noInitMemberLimit = $class::buildNoInitModel();
167
        }
168 1
        return $this->noInitMemberLimit;
169
    }
170
171
    /**
172
     * @return null|OrganizationSearch
173
     */
174
    public function getSearchModel()
175
    {
176
        $class = $this->searchClass;
177
        if (empty($class) || !class_exists($class)) {
178
            return null;
179
        }
180
        return new $class;
181
    }
182
183 52
    public function init()
184
    {
185 52
        $this->parentAttribute = 'parent_guid';
186 52
        if (class_exists($this->memberClass)) {
187 52
            $this->addSubsidiaryClass('Member', ['class' => $this->memberClass]);
188
        }
189 52
        if ($this->skipInit) {
190 52
            return;
191
        }
192 52
        $this->on(static::$eventAfterRegister, [$this, 'onAddProfile'], $this->profileConfig);
193 52
        $this->on(static::$eventAfterRegister, [$this, 'onAssignCreator'], $this->creatorModel);
194 52
        $this->on(static::EVENT_BEFORE_DELETE, [$this, 'onRevokeCreator']);
195 52
        $this->on(static::EVENT_BEFORE_DELETE, [$this, 'onRevokeAdministrators']);
196 52
        $this->on(static::EVENT_BEFORE_DELETE, [$this, 'onRevokePermissions']);
197 52
        $this->initSelfBlameableEvents();
198 52
        parent::init();
199 52
    }
200
201
    /**
202
     * @inheritdoc
203
     */
204 1
    public function attributeLabels()
205
    {
206
        return [
207 1
            'guid' => Yii::t('user', 'GUID'),
208 1
            'id' => Yii::t('user', 'ID'),
209 1
            'ip' => Yii::t('user', 'IP Address'),
210 1
            'ip_type' => Yii::t('user', 'IP Address Type'),
211 1
            'parent' => Yii::t('organization', 'Parent'),
212 1
            'created_at' => Yii::t('user', 'Creation Time'),
213 1
            'updated_at' => Yii::t('user', 'Last Updated Time'),
214 1
            'status' => Yii::t('user', 'Status'),
215 1
            'type' => Yii::t('user', 'Type'),
216 1
            'isExcludeOtherMembers' => Yii::t('organization', 'Exclude Other Members'),
217 1
            'isDisallowMemberJoinOther' => Yii::t('organization', 'Disallow Member to Join in Other Organizations'),
218 1
            'isOnlyAcceptCurrentOrgMember' => Yii::t('organization', 'Only Accept Current Organization Members'),
219 1
            'isOnlyAcceptSuperiorOrgMember' => Yii::t('organization', 'Only Accept Superior Organization Members'),
220
        ];
221
    }
222
223
    /**
224
     * @inheritdoc
225
     */
226 52
    public static function tableName()
227
    {
228 52
        return '{{%organization}}';
229
    }
230
231
    /**
232
     * Find.
233
     * Friendly to IDE.
234
     * @return OrganizationQuery
235
     */
236 52
    public static function find()
237
    {
238 52
        return parent::find();
239
    }
240
241 51
    protected function getTypeRules()
242
    {
243
        return [
244 51
            ['type', 'default', 'value' => static::TYPE_ORGANIZATION],
245
            ['type', 'required'],
246 51
            ['type', 'in', 'range' => [static::TYPE_ORGANIZATION, static::TYPE_DEPARTMENT]],
247
            [['eom', 'djo', 'oacm', 'oasm'], 'default', 'value' => 0],
248
        ];
249
    }
250
251 51
    public function rules()
252
    {
253 51
        return array_merge(parent::rules(), $this->getTypeRules(), $this->getSelfBlameableRules());
254
    }
255
256
    /**
257
     * Get Member Query.
258
     * @return MemberQuery
259
     */
260 50
    public function getMembers()
261
    {
262 50
        return $this->hasMany($this->memberClass, [
263 50
            $this->getNoInitMember()->createdByAttribute => $this->guidAttribute
264 50
        ])->inverseOf('organization');
265
    }
266
267
    /**
268
     * Get organization member users' query.
269
     * @return BaseUserQuery
270
     */
271 6
    public function getMemberUsers()
272
    {
273 6
        $noInit = $this->getNoInitMember();
274 6
        $class = $noInit->memberUserClass;
275 6
        $noInitUser = $class::buildNoInitModel();
276 6
        return $this->hasMany($class, [
277 6
            $noInitUser->guidAttribute => $this->getNoInitMember()->memberAttribute
278 6
        ])->via('members')->inverseOf('atOrganizations');
279
    }
280
281
    /**
282
     * Get subordinate limit query.
283
     * @return null|BaseBlameableQuery
284
     */
285 2
    public function getSubordinateLimit()
286
    {
287 2
        if (empty($this->subordinateLimitClass)) {
288
            return null;
289
        }
290 2
        return $this->hasOne($this->subordinateLimitClass, [
291 2
            $this->getNoInitSubordinateLimit()->createdByAttribute => $this->guidAttribute
292
        ]);
293
    }
294
295
    /**
296
     * Get member limit query.
297
     * @return null|BaseBlameableQuery
298
     */
299 1
    public function getMemberLimit()
300
    {
301 1
        if (empty($this->memberLimitClass)) {
302
            return null;
303
        }
304 1
        return $this->hasOne($this->memberLimitClass, [
305 1
            $this->getNoInitMemberLimit()->createdByAttribute => $this->guidAttribute
306
        ]);
307
    }
308
309
    /**
310
     * Get member with specified user.
311
     * @param User|string|integer $user
312
     * @return Member Null if `user` is not in this organization.
313
     */
314 50
    public function getMember($user)
315
    {
316 50
        return $this->getMembers()->user($user)->one();
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->getMembers()->user($user)->one(); of type yii\db\ActiveRecord|array|null adds the type array to the return on line 316 which is incompatible with the return type documented by rhosocial\organization\Organization::getMember of type rhosocial\organization\Member|null.
Loading history...
317
    }
318
319
    /**
320
     * Add member to organization.
321
     * @param Member|User|string|integer $member Member or User model, or User ID or GUID.
322
     * If member is created, it will be re-assigned to this parameter.
323
     * @see createMemberModel
324
     * @see createMemberModelWithUser
325
     * @return boolean
326
     * @throws DisallowMemberJoinOtherException
327
     * @throws ExcludeOtherMembersException
328
     * @throws OnlyAcceptCurrentOrgMemberException
329
     * @throws OnlyAcceptSuperiorOrgMemberException
330
     */
331 50
    public function addMember(&$member)
332
    {
333 50
        if ($this->getIsNewRecord()) {
334
            return false;
335
        }
336 50
        if ($this->hasReachedMemberLimit()) {
337 1
            return false;
338
        }
339 50
        $user = null;
340 50
        if ($member instanceof Member) {
341
            if ($member->getIsNewRecord()) {
342
                return false;
343
            }
344
            $user = $member->memberUser;
345
        }
346 50
        if ($member instanceof User) {
347 50
            $user = $member;
348
        }
349 50
        if (is_string($member) || is_int($member)) {
350
            $class = Yii::$app->user->identityClass;
351
            $user = $class::find()->guidOrId($member)->one();
352
        }
353 50
        if ($this->hasMember($user)) {
354
            return false;
355
        }
356 50
        $orgs = $user->getAtOrganizations()->all();
357
        /* @var $orgs Organization[] */
358 50
        foreach ($orgs as $org) {
359 31
            if ($org->topOrganization->isDisallowMemberJoinOther && !$org->topOrganization->equals($this->topOrganization)) {
360 1
                throw new DisallowMemberJoinOtherException(Yii::t('organization', "An organization in which the user is located does not allow its members to join other organizations."));
361
            }
362 31
            if ($this->topOrganization->isExcludeOtherMembers && !$org->topOrganization->equals($this->topOrganization)) {
363 31
                throw new ExcludeOtherMembersException(Yii::t('organization', "The organization does not allow users who have joined other organizations to join."));
364
            }
365
        }
366 50
        if ($this->isDepartment() && $this->isOnlyAcceptCurrentOrgMember && !$this->topOrganization->hasMember($user)) {
367 1
            throw new OnlyAcceptCurrentOrgMemberException(Yii::t('organization' ,'This department is only accepted by members of the organization.'));
368
        }
369 50
        if ($this->isDepartment() && $this->isOnlyAcceptSuperiorOrgMember && !$this->parent->hasMember($user)) {
370 1
            throw new OnlyAcceptSuperiorOrgMemberException(Yii::t('organization', 'This department only accepts members of the parent organization or department.'));
371
        }
372
373 50
        $this->trigger(self::EVENT_BEFORE_ADD_MEMBER);
374 50
        $model = null;
375 50
        if ($member instanceof Member) {
376
            $model = $this->createMemberModel($member);
377 50
        } elseif (($member instanceof User) || is_string($member) || is_int($member)) {
378 50
            $model = $this->createMemberModelWithUser($member);
379
        }
380 50
        $member = $model;
381 50
        $result = ($member instanceof Member) ? $member->save() : false;
382 50
        $this->trigger(self::EVENT_AFTER_ADD_MEMBER);
383 50
        return $result;
384
    }
385
386
    /**
387
     * Create member model, and set organization with this.
388
     * @param Member $member If this parameter is not new record, it's organization
389
     * will be set with this, and return it. Otherwise, it will extract `User`
390
     * model and create new `Member` model.
391
     * @see createMemberModelWithUser
392
     * @return Member
393
     */
394
    public function createMemberModel($member)
395
    {
396
        if (!$member->getIsNewRecord()) {
397
            $member->setOrganization($this);
0 ignored issues
show
Documentation introduced by
$this is of type this<rhosocial\organization\Organization>, but the function expects a object<rhosocial\organization\BaseOrganization>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
398
            return $member;
399
        }
400
        return $this->createMemberModelWithUser($member->memberUser);
401
    }
402
403
    /**
404
     * Create member model with user, and set organization with this.
405
     * @param User|string|integer $user
406
     * @return Member
407
     */
408 50
    public function createMemberModelWithUser($user)
409
    {
410
        $config = [
411 50
            'memberUser' => $user,
412 50
            'organization' => $this,
413 50
            'nickname' => '',
414
        ];
415 50
        $member = $this->createMember($config);
416 50
        $member->nickname = $member->memberUser->profile->nickname;
417 50
        return $member;
418
    }
419
420
    /**
421
     * Remove member.
422
     * Note: the creator cannot be removed.
423
     * @param Member|User $member
424
     * @return boolean
425
     */
426 4
    public function removeMember(&$member)
427
    {
428 4
        if ($this->getIsNewRecord()) {
429
            return false;
430
        }
431 4
        $this->trigger(self::EVENT_BEFORE_REMOVE_MEMBER);
432 4
        if ($member instanceof $this->memberClass) {
433 4
            $member = $member->{$member->memberAttribute};
434
        }
435 4
        $member = $this->getMember($member);
436 4
        if (!$member || $member->isCreator()) {
437
            return false;
438
        }
439 4
        $result = $member->delete() > 0;
440 4
        $this->trigger(self::EVENT_AFTER_REMOVE_MEMBER);
441 4
        return $result;
442
    }
443
444
    /**
445
     * Remove administrator.
446
     * @param Member|User|integer|string $member Member instance, or User instance or its GUID or ID.
447
     * @param boolean $keep Keep member after administrator being revoked.
448
     * @return boolean
449
     * @throws IntegrityException
450
     */
451
    public function removeAdministrator(&$member, $keep = true)
452
    {
453
        if ($this->getIsNewRecord()) {
454
            return false;
455
        }
456
        if ($member instanceof $this->memberClass) {
457
            $member = $member->{$member->memberAttribute};
458
        }
459
        $member = $this->getMember($member);
460
        if ($member && $member->isAdministrator()) {
461
            if ($keep) {
462
                return $member->revokeAdministrator();
463
            }
464
            return $this->removeMember($member);
465
        }
466
        return false;
467
    }
468
469
    /**
470
     * 
471
     * @param Event $event
472
     * @throws IntegrityException
473
     * @return boolean
474
     */
475 51
    public function onAddProfile($event)
476
    {
477 51
        $profile = $event->sender->createProfile($event->data);
478 51
        if (!$profile->save()) {
479
            throw new IntegrityException('Profile Save Failed.');
480
        }
481 51
        return true;
482
    }
483
484
    /**
485
     * 
486
     * @param Event $event
487
     */
488 51
    public function onAssignCreator($event)
489
    {
490 51
        return $event->sender->addCreator($event->data);
491
    }
492
493
    /**
494
     * 
495
     * @param Event $event
496
     * @return boolean
497
     */
498 20
    public function onRevokeCreator($event)
499
    {
500 20
        $sender = $event->sender;
501
        /* @var $sender static */
502 20
        $member = $sender->getMemberCreators()->one();
503
        /* @var $member Member */
504 20
        $role = $this->isOrganization() ? (new OrganizationCreator)->name : (new DepartmentCreator)->name;
505 20
        return $member->revokeRole($role);
0 ignored issues
show
Documentation introduced by
$role is of type string, but the function expects a object<rhosocial\user\rbac\Role>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
506
    }
507
508
    /**
509
     * 
510
     * @param Event $event
511
     * @return boolean
512
     */
513 20
    public function onRevokeAdministrators($event)
514
    {
515 20
        $sender = $event->sender;
516
        /* @var $sender static */
517 20
        $members = $sender->getMemberAdministrators()->all();
518
        /* @var $members Member[] */
519 20
        foreach ($members as $member)
520
        {
521 1
            $member->revokeAdministrator();
522
        }
523 20
        return true;
524
    }
525
526
    /**
527
     * 
528
     * @param Event $event
529
     */
530 20
    public function onRevokePermissions($event)
0 ignored issues
show
Unused Code introduced by
The parameter $event 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...
531
    {
532
        
533 20
    }
534
535
    /**
536
     * Check whether current instance is an organization.
537
     * @return boolean
538
     */
539 50
    public function isOrganization()
540
    {
541 50
        return $this->type == static::TYPE_ORGANIZATION;
542
    }
543
544
    /**
545
     * Check whether current instance if a department.
546
     * @return boolean
547
     */
548 50
    public function isDepartment()
549
    {
550 50
        return $this->type == static::TYPE_DEPARTMENT;
551
    }
552
553
    /**
554
     * Check whether the current organization has a member.
555
     * @param User|string|integer $user User instance, GUID or ID.
556
     * @return boolean
557
     */
558 50
    public function hasMember($user)
559
    {
560 50
        return !empty($this->getMember($user));
561
    }
562
563
    /**
564
     * Get member query which role is specified `Creator`.
565
     * @return MemberQuery
566
     */
567 24
    public function getMemberCreators()
568
    {
569 24
        return $this->getMembers()->andWhere(['role' => [(new DepartmentCreator)->name, (new OrganizationCreator)->name]]);
570
    }
571
572
    /**
573
     * Get member query which role is specified `Administrator`.
574
     * @return MemberQuery
575
     */
576 22
    public function getMemberAdministrators()
577
    {
578 22
        return $this->getMembers()->andWhere(['role' => [(new DepartmentAdmin)->name, (new OrganizationAdmin)->name]]);
579
    }
580
581
    /**
582
     * Get user query which role is specified `Creator`.
583
     * @return BaseUserQuery
584
     */
585 4
    public function getCreator()
586
    {
587 4
        $noInit = $this->getNoInitMember();
588 4
        $class = $noInit->memberUserClass;
589 4
        $noInitUser = $class::buildNoInitModel();
590 4
        return $this->hasOne($class, [
591 4
            $noInitUser->guidAttribute => $this->getNoInitMember()->memberAttribute
592 4
        ])->via('memberCreators')->inverseOf('creatorsAtOrganizations');
593
    }
594
595
    /**
596
     * Get user query which role is specified `Administrator`.
597
     * @return BaseUserQuery
598
     */
599 2
    public function getAdministrators()
600
    {
601 2
        $noInit = $this->getNoInitMember();
602 2
        $class = $noInit->memberUserClass;
603 2
        $noInitUser = $class::buildNoInitModel();
604 2
        return $this->hasMany($class, [
605 2
            $noInitUser->guidAttribute => $this->getNoInitMember()->memberAttribute
606 2
        ])->via('memberAdministrators')->inverseOf('administratorsAtOrganizations');
607
    }
608
609
    /**
610
     * 
611
     * @param User $user
612
     * @return boolean
613
     * @throws \Exception
614
     * @throws IntegrityException
615
     */
616 51
    protected function addCreator($user)
617
    {
618 51
        if (!$user) {
619 1
            throw new InvalidParamException('Creator Invalid.');
620
        }
621 50
        $member = $user;
622 50
        $transaction = Yii::$app->db->beginTransaction();
623
        try {
624 50
            if (!$this->addMember($member)) {
625
                throw new IntegrityException('Failed to add member.');
626
            }
627 50
            $role = $this->isOrganization() ? (new OrganizationCreator)->name : (new DepartmentCreator)->name;
628 50
            $member->assignRole($role);
0 ignored issues
show
Documentation introduced by
$role is of type string, but the function expects a object<rhosocial\user\rbac\Role>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug introduced by
The method assignRole does only exist in rhosocial\organization\Member, but not in rhosocial\user\User.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
629 50
            if (!$member->save()) {
630
                throw new IntegrityException('Failed to assign creator.');
631
            }
632 50
            $transaction->commit();
633
        } catch (\Exception $ex) {
634
            $transaction->rollBack();
635
            Yii::error($ex->getMessage(), __METHOD__);
636
            throw $ex;
637
        }
638 50
        return true;
639
    }
640
641
    /**
642
     * Add administrator.
643
     * @param User|integer|string $user User instance, or its GUID or ID.
644
     * @return boolean
645
     * @throws \Exception
646
     * @throws IntegrityException
647
     */
648 17
    public function addAdministrator($user)
649
    {
650 17
        $transaction = Yii::$app->db->beginTransaction();
651
        try {
652 17
            if (!$this->hasMember($user) && !$this->addMember($user)) {
653
                throw new IntegrityException(Yii::t('organization', 'Failed to add member.'));
654
            }
655 17
            $member = $this->getMember($user);
656 17
            $member->assignAdministrator();
657 17
            $transaction->commit();
658 2
        } catch (\Exception $ex) {
659 2
            $transaction->rollBack();
660 2
            Yii::error($ex->getMessage(), __METHOD__);
661 2
            throw $ex;
662
        }
663 17
        return true;
664
    }
665
666
    /**
667
     * Check whether the current organization has administrator.
668
     * @param User|integer|string $user
669
     * @return boolean
670
     */
671 2
    public function hasAdministrator($user)
672
    {
673 2
        $member = $this->getMember($user);
674 2
        if (!$member) {
675
            return false;
676
        }
677 2
        return $member->isAdministrator();
678
    }
679
680
    /**
681
     * Check whether this organization has reached the upper limit of subordinates.
682
     * @return boolean
683
     */
684 18
    public function hasReachedSubordinateLimit()
685
    {
686 18
        $class = $this->subordinateLimitClass;
687 18
        if (empty($class)) {
688
            return false;
689
        }
690 18
        $limit = $class::getLimit($this);
691 18
        if ($limit === false) {
692
            return false;
693
        }
694 18
        $count = (int)$this->getChildren()->count();
695 18
        return $count >= $limit;
696
    }
697
698
    /**
699
     * Check whether this organization has reached the upper limit of members.
700
     * @return boolean
701
     */
702 50
    public function hasReachedMemberLimit()
703
    {
704 50
        $class = $this->memberLimitClass;
705 50
        if (empty($class)) {
706
            return false;
707
        }
708 50
        $limit = $class::getLimit($this);
709 50
        if ($limit === false) {
710
            return false;
711
        }
712 50
        $count = (int)$this->getMembers()->count();
713 50
        return $count >= $limit;
714
    }
715
716 31
    public function getIsExcludeOtherMembers()
717
    {
718 31
        return $this->eom > 0;
719
    }
720 2
    public function setIsExcludeOtherMembers($value = true)
721
    {
722 2
        $this->eom = ($value) ? 1 : 0;
723 2
    }
724 31
    public function getisDisallowMemberJoinOther()
725
    {
726 31
        return $this->djo > 0;
727
    }
728 2
    public function setisDisallowMemberJoinOther($value = true)
729
    {
730 2
        $this->djo = ($value) ? 1 : 0;
731 2
    }
732 17
    public function getIsOnlyAcceptCurrentOrgMember()
733
    {
734 17
        return $this->oacm > 0;
735
    }
736 2
    public function setIsOnlyAcceptCurrentOrgMember($value = true)
737
    {
738 2
        $this->oacm = ($value) ? 1 : 0;
739 2
    }
740 17
    public function getIsOnlyAcceptSuperiorOrgMember()
741
    {
742 17
        return $this->oasm > 0;
743
    }
744 2
    public function setIsOnlyAcceptSuperiorOrgMember($value = true)
745
    {
746 2
        $this->oasm = ($value) ? 1 : 0;
747 2
    }
748
749
    /**
750
     * @return $this|null|static
751
     */
752 31
    public function getTopOrganization()
753
    {
754 31
        if ($this->isOrganization()) {
755 31
            return $this;
756
        }
757 17
        $chain = $this->getAncestorChain();
758 17
        return static::findOne(end($chain));
759
    }
760
761
    /**
762
     * Check whether the subordinates have the [[$user]]
763
     * Note, this operation may consume the quantity of database selection.
764
     * @param User $user
765
     * @return bool
766
     */
767 2
    public function hasMemberInSubordinates($user)
768
    {
769 2
        if ($this->getChildren()->joinWith(['memberUsers mu_alias'])
770 2
            ->andWhere(['mu_alias.' . $user->guidAttribute => $user->getGUID()])->exists()) {
771 1
            return true;
772
        }
773 2
        $children = $this->children;
774
        /* @var $children static[] */
775 2
        foreach ($children as $child) {
776 2
            if ($child->hasMemberInSubordinates($user)) {
777 2
                return true;
778
            }
779
        }
780 2
        return false;
781
    }
782
}
783