Completed
Push — develop ( 7f03d4...6a69af )
by Nate
09:17
created

createCollectionFromRelations()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

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