Completed
Push — develop ( a87a8b...4077f9 )
by Nate
05:20
created

OrganizationRelationship::hasChanged()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 0
cts 7
cp 0
rs 10
c 0
b 0
f 0
cc 4
nc 4
nop 2
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 craft\helpers\Json;
14
use flipbox\craft\ember\helpers\QueryHelper;
15
use flipbox\organizations\elements\Organization;
16
use flipbox\organizations\Organizations;
17
use flipbox\organizations\queries\OrganizationQuery;
18
use flipbox\organizations\queries\UserAssociationQuery;
19
use flipbox\organizations\records\UserAssociation;
20
use Tightenco\Collect\Support\Collection;
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 RelationshipInterface
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->elementQuery()
63
                    ->all()
64
            );
65
        }
66
67
        return $this->getRelationships()
68
            ->sortBy('organizationOrder')
69
            ->pluck('organization');
70
    }
71
72
    /**
73
     * @inheritDoc
74
     * @return Collection
75
     */
76
    protected function existingRelationships(): Collection
77
    {
78
        $relationships = $this->associationQuery()
79
            ->with('types')
80
            ->all();
81
82
        // 'eager' load where we'll pre-populate all of the associations
83
        $elements = $this->elementQuery()
84
            ->id(array_keys($relationships))
85
            ->indexBy('id')
86
            ->all();
87
88
        return $this->createRelations($relationships)
89
            ->transform(function (UserAssociation $association) use ($elements) {
90
                if (isset($elements[$association->getOrganizationId()])) {
91
                    $association->setOrganization($elements[$association->getOrganizationId()]);
92
                }
93
94
                $association->setUser($this->user);
95
96
                return $association;
97
            });
98
    }
99
100
    /************************************************************
101
     * QUERY
102
     ************************************************************/
103
104
    /**
105
     * @return OrganizationQuery
106
     */
107
    private function elementQuery(): OrganizationQuery
108
    {
109
        return Organization::find()
110
            ->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...
111
            ->anyStatus()
112
            ->limit(null);
113
    }
114
115
    /**
116
     * @return UserAssociationQuery
117
     */
118
    private function associationQuery(): UserAssociationQuery
119
    {
120
        return UserAssociation::find()
121
            ->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...
122
            ->orderBy([
123
                'organizationOrder' => SORT_ASC
124
            ])
125
            ->limit(null);
126
    }
127
128
129
    /************************************************************
130
     * CREATE
131
     ************************************************************/
132
133
    /**
134
     * @param $object
135
     * @return UserAssociation
136
     */
137
    protected function create($object): UserAssociation
138
    {
139
        if ($object instanceof UserAssociation) {
140
            return $object;
141
        }
142
143
        return (new UserAssociation())
144
            ->setOrganization($this->resolveObject($object))
145
            ->setUser($this->user);
146
    }
147
148
149
    /*******************************************
150
     * DELTA
151
     *******************************************/
152
153
    /**
154
     * @inheritDoc
155
     */
156
    protected function delta(): array
157
    {
158
        $existingAssociations = $this->associationQuery()
159
            ->indexBy('organizationId')
160
            ->all();
161
162
        $associations = [];
163
        $order = 1;
164
165
        /** @var UserAssociation $newAssociation */
166
        foreach ($this->getRelationships() as $newAssociation) {
167
            $association = ArrayHelper::remove(
168
                $existingAssociations,
169
                $newAssociation->getOrganizationId()
170
            );
171
172
            $newAssociation->organizationOrder = $order++;
173
174
            /** @var UserAssociation $association */
175
            $association = $association ?: $newAssociation;
176
177
            // Has anything changed?
178
            if(!$association->getIsNewRecord() && !$this->hasChanged($newAssociation, $association)) {
179
                continue;
180
            }
181
182
            $associations[] = $this->sync($association, $newAssociation);
183
        }
184
185
        return [$associations, $existingAssociations];
186
    }
187
188
    /**
189
     * @param UserAssociation $new
190
     * @param UserAssociation $existing
191
     * @return bool
192
     */
193
    private function hasChanged(UserAssociation $new, UserAssociation $existing): bool
194
    {
195
        return $new->userOrder != $existing->userOrder ||
196
            $new->organizationOrder != $existing->organizationOrder ||
197
            $new->state != $existing->state ||
198
            $new->getTypes()->isMutated();
199
    }
200
201
    /**
202
     * @param UserAssociation $from
203
     * @param UserAssociation $to
204
     *
205
     * @return UserAssociation
206
     */
207
    private function sync(UserAssociation $to, UserAssociation $from): UserAssociation
208
    {
209
        $to->userOrder = $from->userOrder;
210
        $to->organizationOrder = $from->organizationOrder;
211
        $to->state = $from->state;
212
213
        if ($from->getTypes()->isMutated()) {
214
            $to->getTypes()->clear()->add(
215
                $from->getTypes()->getCollection()
216
            );
217
        }
218
219
        $to->ignoreSortOrder();
0 ignored issues
show
Documentation Bug introduced by
The method ignoreSortOrder does not exist on object<flipbox\organizat...ecords\UserAssociation>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
220
221
        return $to;
222
    }
223
224
    /*******************************************
225
     * COLLECTION UTILS
226
     *******************************************/
227
228
    /**
229
     * Position the relationship based on the sort order
230
     *
231
     * @inheritDoc
232
     */
233
    protected function insertCollection(Collection $collection, UserAssociation $association)
234
    {
235
        if ($association->organizationOrder > 0) {
236
            $collection->splice($association->organizationOrder - 1, 0, [$association]);
237
            return;
238
        }
239
240
        $collection->push($association);
241
    }
242
243
    /**
244
     * @inheritDoc
245
     */
246
    protected function updateCollection(Collection $collection, UserAssociation $association)
247
    {
248
        if (null !== ($key = $this->findKey($association))) {
249
            $collection->offsetUnset($key);
250
        }
251
252
        $this->insertCollection($collection, $association);
253
    }
254
255
256
    /*******************************************
257
     * UTILS
258
     *******************************************/
259
260
    /**
261
     * @param UserAssociation|Organization|int|array|null $object
262
     * @return int|null
263
     */
264
    protected function findKey($object = null)
265
    {
266
        if ($object instanceof UserAssociation) {
267
            return $this->findRelationshipKey($object->getOrganizationId());
268
        }
269
270
        if (null === ($element = $this->resolveObject($object))) {
271
            return null;
272
        }
273
274
        return $this->findRelationshipKey($element->getId());
275
    }
276
277
    /**
278
     * @param $identifier
279
     * @return int|string|null
280
     */
281
    private function findRelationshipKey($identifier)
282
    {
283
        /** @var UserAssociation $association */
284
        foreach ($this->getRelationships()->all() as $key => $association) {
285
            if ($association->getOrganizationId() == $identifier) {
286
                return $key;
287
            }
288
        }
289
290
        return null;
291
    }
292
293
    /**
294
     * @param UserAssociation|Organization|int|array $organization
295
     * @return Organization|null
296
     */
297
    protected function resolveObjectInternal($organization)
298
    {
299
        if ($organization instanceof UserAssociation) {
300
            return $organization->getOrganization();
301
        }
302
303
        if ($organization instanceof Organization) {
304
            return $organization;
305
        }
306
307
        return Organization::findOne($organization);
308
    }
309
}
310