OrganizationRelationship   A
last analyzed

Complexity

Total Complexity 41

Size/Duplication

Total Lines 303
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 41
lcom 1
cbo 11
dl 0
loc 303
ccs 0
cts 160
cp 0
rs 9.1199
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getCollection() 0 13 2
A createCollectionFromRelations() 0 27 4
A existingRelationships() 0 8 1
A getQuery() 0 5 2
A associationQuery() 0 9 2
A create() 0 10 2
A delta() 0 31 5
A hasChanged() 0 7 4
A sync() 0 15 2
A insertCollection() 0 11 3
A updateCollection() 0 12 3
A findKey() 0 12 3
A findRelationshipKey() 0 15 4
A resolveObjectInternal() 0 12 3

How to fix   Complexity   

Complex Class

Complex classes like OrganizationRelationship 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 OrganizationRelationship, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * @copyright  Copyright (c) Flipbox Digital Limited
5
 * @license    https://flipboxfactory.com/software/organization/license
6
 * @link       https://www.flipboxfactory.com/software/organization/
7
 */
8
9
namespace flipbox\organizations\relationships;
10
11
use craft\elements\db\ElementQueryInterface;
12
use craft\elements\User;
13
use craft\helpers\ArrayHelper;
14
use flipbox\organizations\elements\Organization;
15
use flipbox\organizations\Organizations;
16
use flipbox\organizations\queries\OrganizationQuery;
17
use flipbox\organizations\queries\UserAssociationQuery;
18
use flipbox\organizations\records\UserAssociation;
19
use Tightenco\Collect\Support\Collection;
20
use yii\db\QueryInterface;
21
22
/**
23
 * Manages Organizations associated to Users
24
 *
25
 * @author Flipbox Factory <[email protected]>
26
 * @since 2.0.0
27
 * *
28
 * @method UserAssociation findOrCreate($object)
29
 * @method UserAssociation findOne($object = null)
30
 * @method UserAssociation findOrFail($object)
31
 */
32
class OrganizationRelationship implements ElementRelationshipInterface
33
{
34
    use RelationshipTrait;
35
36
    /**
37
     * @var User
38
     */
39
    private $user;
40
41
    /**
42
     * @param User $user
43
     */
44
    public function __construct(User $user)
45
    {
46
        $this->user = $user;
47
    }
48
49
50
    /************************************************************
51
     * COLLECTION
52
     ************************************************************/
53
54
    /**
55
     * @inheritDoc
56
     * @return Organization[]|Collection
57
     */
58
    public function getCollection(): Collection
59
    {
60
        if (null === $this->relations) {
61
            return new Collection(
62
                $this->getQuery()
63
                    ->anyStatus()
64
                    ->limit(null)
65
                    ->all()
66
            );
67
        }
68
69
        return $this->createCollectionFromRelations();
70
    }
71
72
    /**
73
     * @return Collection
74
     */
75
    protected function createCollectionFromRelations()
76
    {
77
        $ids = $this->getRelationships()->pluck('organizationId')->all();
78
        if (empty($ids)) {
79
            return $this->getRelationships()->pluck('organization');
80
        }
81
82
        // 'eager' load where we'll pre-populate all of the elements
83
        $elements = $this->getQuery()
84
            ->id($ids)
85
            ->indexBy('id')
86
            ->anyStatus()
87
            ->limit(null)
88
            ->all();
89
90
        return $this->getRelationships()
91
            ->transform(function (UserAssociation $association) use ($elements) {
92
                if (!$association->isOrganizationSet() && isset($elements[$association->getOrganizationId()])) {
93
                    $association->setOrganization($elements[$association->getOrganizationId()]);
94
                }
95
96
                $association->setUser($this->user);
97
98
                return $association;
99
            })
100
            ->pluck('organization');
101
    }
102
103
    /**
104
     * @inheritDoc
105
     * @return Collection
106
     */
107
    protected function existingRelationships(): Collection
108
    {
109
        $relationships = $this->associationQuery()
110
            ->with('types')
111
            ->all();
112
113
        return $this->createRelations($relationships);
114
    }
115
116
117
    /************************************************************
118
     * QUERY
119
     ************************************************************/
120
121
    /**
122
     * @inheritDoc
123
     * @return OrganizationQuery
124
     */
125
    public function getQuery(): ElementQueryInterface
126
    {
127
        return Organization::find()
128
            ->userId($this->user->getId() ?: false);
0 ignored issues
show
Security Bug introduced by
It seems like $this->user->getId() ?: false can also be of type false; however, flipbox\craft\ember\quer...ttributeTrait::userId() does only seem to accept string|array<integer,str...ft\elements\User>>|null, did you maybe forget to handle an error condition?
Loading history...
129
    }
130
131
    /**
132
     * @return UserAssociationQuery
133
     */
134
    private function associationQuery(): UserAssociationQuery
135
    {
136
        return UserAssociation::find()
137
            ->setUserId($this->user->getId() ?: false)
0 ignored issues
show
Security Bug introduced by
It seems like $this->user->getId() ?: false can also be of type false; however, flipbox\craft\ember\quer...ibuteTrait::setUserId() does only seem to accept string|array<integer,str...ft\elements\User>>|null, did you maybe forget to handle an error condition?
Loading history...
138
            ->orderBy([
139
                'organizationOrder' => SORT_ASC
140
            ])
141
            ->limit(null);
142
    }
143
144
145
    /************************************************************
146
     * CREATE
147
     ************************************************************/
148
149
    /**
150
     * @param $object
151
     * @return UserAssociation
152
     */
153
    protected function create($object): UserAssociation
154
    {
155
        if ($object instanceof UserAssociation) {
156
            return $object;
157
        }
158
159
        return (new UserAssociation())
160
            ->setOrganization($this->resolveObject($object))
161
            ->setUser($this->user);
162
    }
163
164
165
    /*******************************************
166
     * DELTA
167
     *******************************************/
168
169
    /**
170
     * @inheritDoc
171
     */
172
    protected function delta(): array
173
    {
174
        $existingAssociations = $this->associationQuery()
175
            ->indexBy('organizationId')
176
            ->all();
177
178
        $associations = [];
179
        $order = 1;
180
181
        /** @var UserAssociation $newAssociation */
182
        foreach ($this->getRelationships() as $newAssociation) {
183
            $association = ArrayHelper::remove(
184
                $existingAssociations,
185
                $newAssociation->getOrganizationId()
186
            );
187
188
            $newAssociation->organizationOrder = $order++;
189
190
            /** @var UserAssociation $association */
191
            $association = $association ?: $newAssociation;
192
193
            // Has anything changed?
194
            if (!$association->getIsNewRecord() && !$this->hasChanged($newAssociation, $association)) {
195
                continue;
196
            }
197
198
            $associations[] = $this->sync($association, $newAssociation);
199
        }
200
201
        return [$associations, $existingAssociations];
202
    }
203
204
    /**
205
     * @param UserAssociation $new
206
     * @param UserAssociation $existing
207
     * @return bool
208
     */
209
    private function hasChanged(UserAssociation $new, UserAssociation $existing): bool
210
    {
211
        return (Organizations::getInstance()->getSettings()->getEnforceUserSortOrder() &&
212
                $new->organizationOrder != $existing->organizationOrder) ||
213
            $new->state != $existing->state ||
214
            $new->getTypes()->isMutated();
215
    }
216
217
    /**
218
     * @param UserAssociation $from
219
     * @param UserAssociation $to
220
     *
221
     * @return UserAssociation
222
     */
223
    private function sync(UserAssociation $to, UserAssociation $from): UserAssociation
224
    {
225
        $to->organizationOrder = $from->organizationOrder;
226
        $to->state = $from->state;
227
228
        if ($from->getTypes()->isMutated()) {
229
            $to->getTypes()->clear()->add(
230
                $from->getTypes()->getCollection()
231
            );
232
        }
233
234
        $to->ignoreSortOrder();
235
236
        return $to;
237
    }
238
239
    /*******************************************
240
     * COLLECTION UTILS
241
     *******************************************/
242
243
    /**
244
     * Position the relationship based on the sort order
245
     *
246
     * @inheritDoc
247
     */
248
    protected function insertCollection(Collection $collection, UserAssociation $association)
249
    {
250
        if (Organizations::getInstance()->getSettings()->getEnforceUserSortOrder() &&
251
            $association->organizationOrder > 0
252
        ) {
253
            $collection->splice($association->organizationOrder - 1, 0, [$association]);
254
            return;
255
        }
256
257
        $collection->push($association);
258
    }
259
260
    /**
261
     * @inheritDoc
262
     */
263
    protected function updateCollection(Collection $collection, UserAssociation $association)
264
    {
265
        if (!Organizations::getInstance()->getSettings()->getEnforceUserSortOrder()) {
266
            return;
267
        }
268
269
        if (null !== ($key = $this->findKey($association))) {
270
            $collection->offsetUnset($key);
271
        }
272
273
        $this->insertCollection($collection, $association);
274
    }
275
276
277
    /*******************************************
278
     * UTILS
279
     *******************************************/
280
281
    /**
282
     * @param UserAssociation|Organization|int|array|null $object
283
     * @return int|null
284
     */
285
    protected function findKey($object = null)
286
    {
287
        if ($object instanceof UserAssociation) {
288
            return $this->findRelationshipKey($object->getOrganizationId());
289
        }
290
291
        if (null === ($element = $this->resolveObject($object))) {
292
            return null;
293
        }
294
295
        return $this->findRelationshipKey($element->getId());
296
    }
297
298
    /**
299
     * @param $identifier
300
     * @return int|string|null
301
     */
302
    private function findRelationshipKey($identifier)
303
    {
304
        if (null === $identifier) {
305
            return null;
306
        }
307
308
        /** @var UserAssociation $association */
309
        foreach ($this->getRelationships()->all() as $key => $association) {
310
            if ($association->getOrganizationId() == $identifier) {
311
                return $key;
312
            }
313
        }
314
315
        return null;
316
    }
317
318
    /**
319
     * @param UserAssociation|Organization|int|array $organization
320
     * @return Organization|null
321
     */
322
    protected function resolveObjectInternal($organization)
323
    {
324
        if ($organization instanceof UserAssociation) {
325
            return $organization->getOrganization();
326
        }
327
328
        if ($organization instanceof Organization) {
329
            return $organization;
330
        }
331
332
        return Organization::findOne($organization);
333
    }
334
}
335