Passed
Push — master ( a274db...6e4772 )
by vistart
03:53
created

Organization::getMemberAdministrators()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 0
cts 5
cp 0
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 0
crap 2
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\BaseUserQuery;
17
use rhosocial\user\User;
18
use rhosocial\user\rbac\Role;
19
use rhosocial\organization\rbac\roles\DepartmentAdmin;
20
use rhosocial\organization\rbac\roles\DepartmentCreator;
21
use rhosocial\organization\rbac\roles\OrganizationAdmin;
22
use rhosocial\organization\rbac\roles\OrganizationCreator;
23
use rhosocial\organization\queries\MemberQuery;
24
use rhosocial\organization\queries\DepartmentQuery;
25
use rhosocial\organization\queries\OrganizationQuery;
26
use Yii;
27
use yii\base\Event;
28
use yii\db\IntegrityException;
29
30
/**
31
 * Base Organization.
32
 * This class is an abstract class that can not be instantiated directly.
33
 * You can use [[Organization]] or [[Department]] instead.
34
 *
35
 * @method Member createMember(array $config) Create member who is subordinate to this.
36
 * @property integer $type Whether indicate this instance is an organization or a department.
37
 
38
 * @version 1.0
39
 * @author vistart <[email protected]>
40
 */
41
class Organization extends User
42
{
43
    use SelfBlameableTrait;
44
45
    const TYPE_ORGANIZATION = 1;
46
    const TYPE_DEPARTMENT = 2;
47
48
    /**
49
     * @var boolean Organization does not need password and corresponding features.
50
     */
51
    public $passwordHashAttribute = false;
52
53
    /**
54
     * @var boolean Organization does not need password and corresponding features.
55
     */
56
    public $passwordResetTokenAttribute = false;
57
58
    /**
59
     * @var boolean Organization does not need password and corresponding features.
60
     */
61
    public $passwordHistoryClass = false;
62
63
    /**
64
     * @var boolean Organization does not need source.
65
     */
66
    public $sourceAttribute = false;
67
68
    /**
69
     * @var boolean Organization does not need auth key.
70
     */
71
    public $authKeyAttribute = false;
72
73
    /**
74
     * @var boolean Organization does not need access token.
75
     */
76
    public $accessTokenAttribute = false;
77
78
    /**
79
     *
80
     * @var boolean Organization does not need login log.
81
     */
82
    public $loginLogClass = false;
83
84
    public $profileClass = Profile::class;
85
86
    public $memberClass = Member::class;
87
    private $noInitMember;
88
    /**
89
     * @return Member
90
     */
91 16
    protected function getNoInitMember()
92
    {
93 16
        if (!$this->noInitMember) {
94 16
            $class = $this->memberClass;
95 16
            $this->noInitMember = $class::buildNoInitModel();
96
        }
97 16
        return $this->noInitMember;
98
    }
99
100 25
    public function init()
101
    {
102 25
        $this->parentAttribute = 'parent_guid';
103 25
        if (class_exists($this->memberClass)) {
104 25
            $this->addSubsidiaryClass('Member', ['class' => $this->memberClass]);
105
        }
106 25
        if ($this->skipInit) {
107 25
            return;
108
        }
109 25
        $this->on(static::$eventBeforeDeregister, [$this, 'onRevokeCreatorBeforeDeregister']);
110 25
        parent::init();
111 25
    }
112
113
    /**
114
     * @inheritdoc
115
     */
116 1
    public function attributeLabels()
117
    {
118
        return [
119 1
            'guid' => Yii::t('app', 'GUID'),
120 1
            'id' => Yii::t('app', 'ID'),
121 1
            'ip' => Yii::t('app', 'IP'),
122 1
            'ip_type' => Yii::t('app', 'IP Address Type'),
123 1
            'parent' => Yii::t('app', 'Parent'),
124 1
            'created_at' => Yii::t('app', 'Create Time'),
125 1
            'updated_at' => Yii::t('app', 'Update Time'),
126 1
            'status' => Yii::t('app', 'Status'),
127 1
            'type' => Yii::t('app', 'Type'),
128
        ];
129
    }
130
131
    /**
132
     * @inheritdoc
133
     */
134 25
    public static function tableName()
135
    {
136 25
        return '{{%organization}}';
137
    }
138
139 24
    protected function getTypeRules()
140
    {
141
        return [
142 24
            ['type', 'default', 'value' => static::TYPE_ORGANIZATION],
143
            ['type', 'required'],
144 24
            ['type', 'in', 'range' => [static::TYPE_ORGANIZATION, static::TYPE_DEPARTMENT]],
145
        ];
146
    }
147
148 24
    public function rules()
149
    {
150 24
        return array_merge(parent::rules(), $this->getTypeRules(), $this->getSelfBlameableRules());
151
    }
152
153
    /**
154
     * Get Member Query.
155
     * @return MemberQuery
156
     */
157 16
    public function getMembers()
158
    {
159 16
        return $this->hasMany($this->memberClass, [$this->getNoInitMember()->createdByAttribute => $this->guidAttribute])->inverseOf('organization');
160
    }
161
162
    /**
163
     * Get organization member users' query.
164
     * @return BaseUserQuery
165
     */
166 2
    public function getMemberUsers()
167
    {
168 2
        $noInit = $this->getNoInitMember();
169 2
        $class = $noInit->memberUserClass;
170 2
        $noInitUser = $class::buildNoInitModel();
171 2
        return $this->hasMany($class, [$noInitUser->guidAttribute => $this->getNoInitMember()->memberAttribute])->via('members')->inverseOf('atOrganizations');
172
    }
173
174
    /**
175
     * Get member with specified user.
176
     * @param User|string|integer $user
177
     * @return Member Null if `user` is not in this organization.
178
     */
179 7
    public function getMember($user)
180
    {
181 7
        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 181 which is incompatible with the return type documented by rhosocial\organization\Organization::getMember of type rhosocial\organization\Member|null.
Loading history...
182
    }
183
184
    /**
185
     * Add member to organization.
186
     * @param Member|User|string|integer $member
187
     * @see createMemberModel
188
     * @see createMemberModelWithUser
189
     * @return boolean
190
     */
191 5
    public function addMember(&$member)
192
    {
193 5
        if ($this->getIsNewRecord()) {
194
            return false;
195
        }
196 5
        $model = null;
197 5
        if ($member instanceof Member) {
198
            $model = $this->createMemberModel($member);
199
        }
200 5
        if (($member instanceof User) || is_string($member) || is_int($member)) {
201 5
            $model = $this->createMemberModelWithUser($member);
202
        }
203 5
        $member = $model;
204 5
        return ($member instanceof Member) ? $member->save() : false;
205
    }
206
207
    /**
208
     * Create member model, and set organization with this.
209
     * @param Member $member If this parameter is not new record, it's organization
210
     * will be set with this, and return it. Otherwise, it will extract `User`
211
     * model and create new `Member` model.
212
     * @see createMemberModelWithUser
213
     * @return Member
214
     */
215
    public function createMemberModel($member)
216
    {
217
        if (!$member->getIsNewRecord()) {
218
            $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...
219
            return $member;
220
        }
221
        return $this->createMemberModelWithUser($member->memberUser);
222
    }
223
224
    /**
225
     * Create member model with user, and set organization with this.
226
     * @param User|string|integer $user
227
     * @return Member
228
     */
229 23
    public function createMemberModelWithUser($user)
230
    {
231
        $config = [
232 23
            'memberUser' => $user,
233 23
            'organization' => $this,
234 23
            'nickname' => '',
235
        ];
236 23
        if ($user->profile) {
237 23
            $config['nickname'] = $user->profile->nickname;
238
        }
239 23
        return $this->createMember($config);
240
    }
241
242
    /**
243
     * Remove member.
244
     * @param Member|User $member
245
     * @return boolean
246
     */
247 1
    public function removeMember(&$member)
248
    {
249 1
        if ($this->getIsNewRecord()) {
250
            return false;
251
        }
252 1
        if ($member instanceof $this->memberClass) {
253 1
            $member = $member->{$member->memberAttribute};
254
        }
255 1
        $member = $this->getMember($member);
256 1
        return $member && $member->delete() > 0;
257
    }
258
259
    /**
260
     * 
261
     * @param Event $event
262
     */
263 13
    public function onRevokeCreatorBeforeDeregister($event)
264
    {
265 13
        $sender = $event->sender;
266
        /* @var $sender static */
267 13
        $member = $sender->getMemberCreators()->one();
268
        /* @var $member Member */
269 13
        $role = $this->type == static::TYPE_ORGANIZATION ? (new OrganizationCreator) : (new DepartmentCreator);
270 13
        return $member->revokeRole($role);
271
    }
272
273
    /**
274
     * 
275
     * @return boolean
276
     */
277 2
    public function isOrganization()
278
    {
279 2
        return $this->type == static::TYPE_ORGANIZATION;
280
    }
281
282
    /**
283
     * 
284
     * @return boolean
285
     */
286 2
    public function isDepartment()
287
    {
288 2
        return $this->type == static::TYPE_DEPARTMENT;
289
    }
290
291
    /**
292
     * Check whether the current organization has a member.
293
     * @param User $user
294
     * @return boolean
295
     */
296 1
    public function hasMember($user)
297
    {
298 1
        return !is_null($this->getMember($user));
299
    }
300
301
    /**
302
     * Get creator query.
303
     * @return MemberQuery
304
     */
305 13
    public function getMemberCreators()
306
    {
307 13
        $roleNames = [];
308 13
        $roleNames[] = (new DepartmentCreator)->name;
309 13
        $roleNames[] = (new OrganizationCreator)->name;
310 13
        return $this->getMembers()->andWhere(['role' => $roleNames]);
311
    }
312
313
    /**
314
     * Get administrator query.
315
     * @return MemberQuery
316
     */
317
    public function getMemberAdministrators()
318
    {
319
        $roleNames = [];
320
        $roleNames[] = (new DepartmentAdmin)->name;
321
        $roleNames[] = (new OrganizationAdmin)->name;
322
        return $this->getMembers()->andWhere(['role' => $roleNames]);
323
    }
324
325
    /**
326
     * 
327
     * @param User $user
328
     */
329 1
    public function addAdministrator($user)
330
    {
331 1
        $member = $user;
332 1
        $transaction = Yii::$app->db->beginTransaction();
333
        try {
334 1
            if (!$this->addMember($member)) {
335
                throw new IntegrityException('Failed to add member.');
336
            }
337 1
            $role = $this->type == static::TYPE_ORGANIZATION ? (new OrganizationAdmin) : (new DepartmentAdmin);
338 1
            $member->assignRole($role);
0 ignored issues
show
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...
339 1
            if (!$member->save()) {
340
                throw new IntegrityException('Failed to assign administrator.');
341
            }
342 1
            $transaction->commit();
343
        } catch (\Exception $ex) {
344
            $transaction->rollBack();
345
            Yii::error($ex->getMessage(), __METHOD__);
346
            throw $ex;
347
        }
348 1
        return true;
349
    }
350
}
351