Passed
Push — master ( 808a8e...13dd6b )
by vistart
02:51
created

BaseOrganization::init()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 9
cts 9
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 8
nc 8
nop 0
crap 4
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\organization\queries\MemberQuery;
19
use rhosocial\organization\queries\OrganizationQuery;
20
21
/**
22
 * Organization.
23
 * The organization can open sub-organizations & departments.
24
 * An organization can exist either alone or as a sub-organization of an
25
 * organization but not department.
26
 *
27
 * @method Member createMember(array $config) Create member who belongs to this.
28
 * @property integer $type Whether indicate this instance is an organization or a department.
29
 
30
 * @version 1.0
31
 * @author vistart <[email protected]>
32
 */
33
abstract class BaseOrganization extends User
34
{
35
    use SelfBlameableTrait;
36
37
    const TYPE_ORGANIZATION = 1;
38
    const TYPE_DEPARTMENT = 2;
39
40
    /**
41
     * @var boolean Organization does not need password and corresponding features.
42
     */
43
    public $passwordHashAttribute = false;
44
45
    /**
46
     * @var boolean Organization does not need password and corresponding features.
47
     */
48
    public $passwordResetTokenAttribute = false;
49
50
    /**
51
     * @var boolean Organization does not need password and corresponding features.
52
     */
53
    public $passwordHistoryClass = false;
54
55
    /**
56
     * @var boolean Organization does not need source.
57
     */
58
    public $sourceAttribute = false;
59
60
    /**
61
     * @var boolean Organization does not need auth key.
62
     */
63
    public $authKeyAttribute = false;
64
65
    /**
66
     * @var boolean Organization does not need access token.
67
     */
68
    public $accessTokenAttribute = false;
69
70
    /**
71
     *
72
     * @var boolean Organization does not need login log.
73
     */
74
    public $loginLogClass = false;
75
76
    public $profileClass = Profile::class;
77
78
    public $memberClass = Member::class;
79
    private $noInitMember;
80
    /**
81
     * @return Member
82
     */
83
    protected function getNoInitMember()
84
    {
85
        if (!$this->noInitMember) {
86
            $class = $this->memberClass;
87
            $this->noInitMember = $class::buildNoInitMember();
88
        }
89
        return $this->noInitMember;
90
    }
91
92 1
    public function init()
93
    {
94 1
        if (!is_string($this->queryClass)) {
95 1
            $this->queryClass = OrganizationQuery::class;
96
        }
97 1
        if (class_exists($this->memberClass)) {
98 1
            $this->addSubsidiaryClass('Member', ['class' => Member::class]);
99
        }
100 1
        if ($this->skipInit) {
101 1
            return;
102
        }
103 1
        parent::init();
104 1
    }
105
106
    /**
107
     * @inheritdoc
108
     */
109
    public function attributeLabels()
110
    {
111
        return [
112
            'guid' => Yii::t('app', 'GUID'),
113
            'id' => Yii::t('app', 'ID'),
114
            'ip' => Yii::t('app', 'IP'),
115
            'ip_type' => Yii::t('app', 'IP Address Type'),
116
            'parent' => Yii::t('app', 'Parent'),
117
            'created_at' => Yii::t('app', 'Create Time'),
118
            'updated_at' => Yii::t('app', 'Update Time'),
119
            'status' => Yii::t('app', 'Status'),
120
            'type' => Yii::t('app', 'Type'),
121
        ];
122
    }
123
124
    /**
125
     * @inheritdoc
126
     */
127 1
    public static function tableName()
128
    {
129 1
        return '{{%organization}}';
130
    }
131
132
    abstract protected function typeAttributeBehavior();
133
134
    /**
135
     * @inheritdoc
136
     */
137 1
    public function behaviors()
138
    {
139 1
        return array_merge(parent::behaviors(), $this->typeAttributeBehavior());
140
    }
141
142
    abstract protected function getTypeRules();
143
144 1
    public function rules()
145
    {
146 1
        return array_merge(parent::rules(), $this->getTypeRules(), $this->getSelfBlameableRules());
147
    }
148
149
    /**
150
     * Get Member Query.
151
     * @return MemberQuery
152
     */
153
    public function getMembers()
154
    {
155
        return $this->hasMany($this->memberClass, [$this->guidAttribute => $this->getNoInitMember()->createdByAttribute])->inverseOf('organization');
156
    }
157
158
    /**
159
     * 
160
     * @return BaseUserQuery
161
     */
162
    public function getMemberUsers()
163
    {
164
        $noInit = $this->getNoInitMember();
165
        $class = $noInit->memberUserClass;
166
        $noInitUser = $class::buildNoInitModel();
167
        return $this->hasMany($class, [$this->guidAttribute => $noInitUser->guidAttribute])->via('members')->inverseOf('atOrganizations');
168
    }
169
170
    /**
171
     * Add member to organization.
172
     * @param Member|User $member
173
     * @return Member
174
     */
175
    public function addMember($member)
176
    {
177
        if ($member instanceof Member) {
178
            return $this->createMemberModel($member);
179
        }
180
        if ($member instanceof User) {
181
            return $this->createMemberModelWithUser($member);
182
        }
183
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by rhosocial\organization\BaseOrganization::addMember of type rhosocial\organization\Member.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
184
    }
185
186
    /**
187
     * Create member model, and set organization with this.
188
     * @param Member $member
189
     * @return Member
190
     */
191
    protected function createMemberModel($member)
192
    {
193
        if (!$member->getIsNewRecord()) {
194
            $member->setOrganization($this);
0 ignored issues
show
Compatibility introduced by
$this of type object<rhosocial\organization\BaseOrganization> is not a sub-type of object<rhosocial\organization\Organization>. It seems like you assume a child class of the class rhosocial\organization\BaseOrganization to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
195
            return $member;
196
        }
197
        return $this->createMemberModelWithUser($member->memberUser);
198
    }
199
200
    /**
201
     * Create member model with user, and set organization with this.
202
     * @param User|string|integer $user
203
     * @return Member
204
     */
205
    protected function createMemberModelWithUser($user)
206
    {
207
        $config = [
208
            'memberUser' => $user,
209
            'organization' => $this,
210
            'nickname' => '',
211
        ];
212
        if ($user->profile) {
213
            $config['nickname'] = $user->profile->nickname;
214
        }
215
        return $this->createMember($config);
216
    }
217
}
218