Completed
Push — master ( b27204...a2c64d )
by Nate
05:15 queued 02:48
created

src/behaviors/UserTypesBehavior.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace flipbox\organizations\behaviors;
4
5
use craft\elements\User;
6
use craft\helpers\ArrayHelper;
7
use craft\helpers\Json;
8
use flipbox\craft\ember\helpers\QueryHelper;
9
use flipbox\organizations\elements\Organization;
10
use flipbox\organizations\Organizations as OrganizationPlugin;
11
use flipbox\organizations\queries\UserTypeQuery;
12
use flipbox\organizations\records\UserAssociation;
13
use flipbox\organizations\records\UserType;
14
use flipbox\organizations\records\UserTypeAssociation;
15
use yii\base\Behavior;
16
use yii\db\Query;
17
18
/**
19
 * @property User $owner;
20
 */
21
class UserTypesBehavior extends Behavior
22
{
23
    /**
24
     * @var UserTypeQuery|null
25
     */
26
    private $userTypes;
27
28
    /**
29
     * @param array $criteria
30
     * @return UserTypeQuery
31
     */
32
    public function userTypeQuery($criteria = []): UserTypeQuery
33
    {
34
        $query = UserType::find()
35
            ->user($this->owner);
0 ignored issues
show
It seems like $this->owner can also be of type object<yii\base\Component>; however, flipbox\craft\ember\quer...rAttributeTrait::user() does only seem to accept string|array<integer,str...ft\elements\User>>|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
36
37
        if (!empty($criteria)) {
38
            QueryHelper::configure(
39
                $query,
40
                $criteria
41
            );
42
        }
43
44
        return $query;
45
    }
46
47
    /**
48
     * Get a query with associated user types
49
     *
50
     * @param array $criteria
51
     * @return UserTypeQuery
52
     */
53
    public function getUserTypes($criteria = []): UserTypeQuery
54
    {
55
        if (null === $this->userTypes) {
56
            $this->userTypes = $this->userTypeQuery();
57
        }
58
59
        if (!empty($criteria)) {
60
            QueryHelper::configure(
61
                $this->userTypes,
62
                $criteria
63
            );
64
        }
65
66
        return $this->userTypes;
67
    }
68
69
    /**
70
     * Set an array or query of user types to a user
71
     *
72
     * @param $userTypes
73
     * @return $this
74
     */
75
    public function setUserTypes($userTypes)
76
    {
77
        if ($userTypes instanceof UserTypeQuery) {
78
            $this->userTypes = $userTypes;
79
            return $this;
80
        }
81
82
        // Reset the query
83
        $this->userTypes = $this->userTypeQuery();
84
        $this->userTypes->setCachedResult([]);
85
        $this->addUserTypes($userTypes);
86
        return $this;
87
    }
88
89
    /**
90
     * Add an array of user types to a user.  Note: This does not save the user type associations.
91
     *
92
     * @param $types
93
     * @return $this
94
     */
95
    protected function addUserTypes(array $types)
96
    {
97
        // In case a config is directly passed
98
        if (ArrayHelper::isAssociative($types)) {
99
            $types = [$types];
100
        }
101
102
        foreach ($types as $key => $type) {
103
            if (!$type = $this->resolveUserType($type)) {
104
                OrganizationPlugin::warning(sprintf(
105
                    "Unable to resolve user type: %s",
106
                    (string)Json::encode($type)
107
                ));
108
                continue;
109
            }
110
111
            $this->addUserType($type);
112
        }
113
114
        return $this;
115
    }
116
117
    /**
118
     * Add a user type to a user.  Note: This does not save the user type associations.
119
     *
120
     * @param UserType $type
121
     * @return $this
122
     */
123
    public function addUserType(UserType $type)
124
    {
125
        // Current associated types
126
        $allTypes = $this->getUserTypes()->all();
127
        $allTypes[] = $type;
128
129
        $this->getUserTypes()->setCachedResult($allTypes);
130
131
        return $this;
132
    }
133
134
    /**
135
     * @param mixed $type
136
     * @return UserType
137
     */
138
    protected function resolveUserType($type): UserType
139
    {
140
        if (null !== ($type = UserType::findOne($type))) {
141
            return $type;
142
        }
143
144
        if (!is_array($type)) {
145
            $type = ArrayHelper::toArray($type, [], false);
146
        }
147
148
        return new UserType($type);
149
    }
150
151
    /*******************************************
152
     * ASSOCIATE and/or DISASSOCIATE
153
     *******************************************/
154
155
    /**
156
     * @param UserTypeQuery $query
157
     * @param Organization $organization
158
     * @return bool
159
     * @throws \Throwable
160
     * @throws \yii\db\StaleObjectException
161
     */
162
    public function saveUserTypes(UserTypeQuery $query, Organization $organization): bool
163
    {
164
        // Determine an association
165
        if (null === ($userAssociationId = $this->associationId($this->owner->getId(), $organization->getId()))) {
166
            $this->owner->addError('types', 'User is not associated to organization.');
167
            return false;
168
        }
169
170
        $currentAssociations = UserTypeAssociation::find()
171
            ->userId($userAssociationId ?: false)
172
            ->indexBy('typeId')
173
            ->all();
174
175
        $success = true;
176
177
        if (null === ($types = $query->getCachedResult())) {
178
            // Delete anything that's currently set
179
            foreach ($currentAssociations as $currentAssociation) {
180
                if (!$currentAssociation->delete()) {
181
                    $success = false;
182
                }
183
            }
184
185
            if (!$success) {
186
                $this->owner->addError('types', 'Unable to dissociate types.');
187
            }
188
189
            return $success;
190
        }
191
192
        $associations = [];
193
        $order = 1;
194
        foreach ($types as $type) {
195
            if (null === ($association = ArrayHelper::remove($currentAssociations, $type->getId()))) {
196
                $association = (new UserTypeAssociation())
197
                    ->setType($type);
198
                $association->userId = $userAssociationId;
199
            }
200
201
            $association->sortOrder = $order++;
202
203
            $associations[] = $association;
204
        }
205
206
        // Delete anything that has been removed
207
        foreach ($currentAssociations as $currentAssociation) {
208
            if (!$currentAssociation->delete()) {
209
                $success = false;
210
            }
211
        }
212
213
        // Save'em
214
        foreach ($associations as $association) {
215
            if (!$association->save()) {
216
                $success = false;
217
            }
218
        }
219
220
        if (!$success) {
221
            $this->owner->addError('types', 'Unable to save user types.');
222
        }
223
224
        $this->setUserTypes($query);
225
226
        return $success;
227
    }
228
229
    /**
230
     * @param UserTypeQuery $query
231
     * @param Organization $organization
232
     * @return bool
233
     * @throws \Throwable
234
     */
235
    public function associateUserTypes(UserTypeQuery $query, Organization $organization): bool
236
    {
237
        $records = $query->all();
238
239
        if (empty($records)) {
240
            return true;
241
        }
242
243
        // Determine an association
244
        if (null === ($userAssociationId = $this->associationId($this->owner->getId(), $organization->getId()))) {
245
            $this->owner->addError('types', 'User is not associated to organization.');
246
            return false;
247
        }
248
249
        $currentAssociations = UserTypeAssociation::find()
250
            ->userId($userAssociationId ?: false)
251
            ->indexBy('typeId')
252
            ->all();
253
254
        $success = true;
255
        foreach ($records as $record) {
256
            if (null === ($association = ArrayHelper::remove($currentAssociations, $record->getId()))) {
257
                $association = (new UserTypeAssociation())
258
                    ->setType($record);
259
                $association->userId = $userAssociationId;
260
            }
261
262
            if (!$association->save()) {
263
                $success = false;
264
            }
265
        }
266
267
        if (!$success) {
268
            $this->owner->addError('users', 'Unable to associate user types.');
269
        }
270
271
        $this->userTypes = null;
272
273
        return $success;
274
    }
275
276
    /**
277
     * @param UserTypeQuery $query
278
     * @param Organization $organization
279
     * @return bool
280
     * @throws \Throwable
281
     */
282
    public function dissociateUserTypes(UserTypeQuery $query, Organization $organization): bool
283
    {
284
        $records = $query->all();
285
286
        if (empty($records)) {
287
            return true;
288
        }
289
290
        // Determine an association
291
        if (null === ($userAssociationId = $this->associationId($this->owner->getId(), $organization->getId()))) {
292
            $this->owner->addError('types', 'User is not associated to organization.');
293
            return false;
294
        }
295
296
        $currentAssociations = UserTypeAssociation::find()
297
            ->userId($userAssociationId ?: false)
298
            ->indexBy('typeId')
299
            ->all();
300
301
        $success = true;
302
        foreach ($records as $record) {
303
            if (null === ($association = ArrayHelper::remove($currentAssociations, $record->getId()))) {
304
                continue;
305
            }
306
307
            if (!$association->delete()) {
308
                $success = false;
309
            }
310
        }
311
312
        if (!$success) {
313
            $this->owner->addError('users', 'Unable to dissociate user types.');
314
        }
315
316
        $this->userTypes = null;
317
318
        return $success;
319
    }
320
321
    /**
322
     * @param int $userId
323
     * @param int $organizationId
324
     * @return Query
325
     */
326
    protected function associationIdQuery(int $userId, int $organizationId): Query
327
    {
328
        return (new Query())
329
            ->select(['id'])
330
            ->from([UserAssociation::tableName()])
331
            ->where([
332
                'organizationId' => $organizationId,
333
                'userId' => $userId,
334
            ]);
335
    }
336
337
    /**
338
     * @param int $userId
339
     * @param int $organizationId
340
     * @return string|null
341
     */
342
    protected function associationId(int $userId, int $organizationId)
343
    {
344
        $id = $this->associationIdQuery($userId, $organizationId)->scalar();
345
        return is_string($id) || is_numeric($id) ? $id : null;
346
    }
347
}
348