These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
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\elements; |
||
10 | |||
11 | use Craft; |
||
12 | use craft\helpers\ArrayHelper; |
||
13 | use flipbox\craft\ember\helpers\QueryHelper; |
||
14 | use flipbox\organizations\queries\OrganizationTypeQuery; |
||
15 | use flipbox\organizations\records\OrganizationType; |
||
16 | use flipbox\organizations\records\OrganizationType as TypeModel; |
||
17 | use flipbox\organizations\records\OrganizationTypeAssociation; |
||
18 | |||
19 | /** |
||
20 | * @author Flipbox Factory <[email protected]> |
||
21 | * @since 1.0.0 |
||
22 | */ |
||
23 | trait TypesAttributeTrait |
||
24 | { |
||
25 | /** |
||
26 | * @var OrganizationTypeQuery |
||
27 | */ |
||
28 | private $types; |
||
29 | |||
30 | /** |
||
31 | * @var TypeModel|false |
||
32 | */ |
||
33 | private $activeType; |
||
34 | |||
35 | /************************************************************ |
||
36 | * REQUEST |
||
37 | ************************************************************/ |
||
38 | |||
39 | /** |
||
40 | * AssociateUserToOrganization an array of types from request input |
||
41 | * |
||
42 | * @param string $identifier |
||
43 | * @return $this |
||
44 | */ |
||
45 | public function setTypesFromRequest(string $identifier = 'types') |
||
46 | { |
||
47 | if (null !== ($types = Craft::$app->getRequest()->getBodyParam($identifier))) { |
||
48 | $this->setTypes((array) $types); |
||
49 | } |
||
50 | |||
51 | return $this; |
||
52 | } |
||
53 | |||
54 | /************************************************************ |
||
55 | * ACTIVE TYPE |
||
56 | ************************************************************/ |
||
57 | |||
58 | /** |
||
59 | * @param TypeModel|null $type |
||
60 | * @return $this |
||
61 | */ |
||
62 | public function setActiveType(TypeModel $type = null) |
||
63 | { |
||
64 | if ($type) { |
||
65 | $this->addType($type); |
||
66 | } |
||
67 | |||
68 | $this->activeType = (null === $type) ? false : $type; |
||
69 | return $this; |
||
70 | } |
||
71 | |||
72 | /** |
||
73 | * @return TypeModel|null |
||
74 | */ |
||
75 | public function getActiveType() |
||
76 | { |
||
77 | if (null === $this->activeType) { |
||
78 | if (!$activeType = $this->getPrimaryType()) { |
||
79 | $activeType = false; |
||
80 | } |
||
81 | |||
82 | $this->activeType = $activeType; |
||
83 | } |
||
84 | |||
85 | return (false === $this->activeType) ? null : $this->activeType; |
||
86 | } |
||
87 | |||
88 | /************************************************************ |
||
89 | * TYPES |
||
90 | ************************************************************/ |
||
91 | |||
92 | /** |
||
93 | * Get an array of types associated to an organization |
||
94 | * |
||
95 | * @param array $criteria |
||
96 | * @return OrganizationTypeQuery |
||
97 | */ |
||
98 | public function getTypes($criteria = []) |
||
99 | { |
||
100 | if (null === $this->types) { |
||
101 | $this->types = OrganizationType::find() |
||
102 | ->organization($this); |
||
103 | } |
||
104 | |||
105 | if (!empty($criteria)) { |
||
106 | QueryHelper::configure( |
||
107 | $this->types, |
||
108 | $criteria |
||
109 | ); |
||
110 | } |
||
111 | |||
112 | return $this->types; |
||
113 | } |
||
114 | |||
115 | /** |
||
116 | * AssociateUserToOrganization types to an organization |
||
117 | * |
||
118 | * @param $types |
||
119 | * @return $this |
||
120 | */ |
||
121 | public function setTypes($types) |
||
122 | { |
||
123 | if ($types instanceof OrganizationTypeQuery) { |
||
124 | $this->types = $types; |
||
125 | return $this; |
||
126 | } |
||
127 | |||
128 | // Reset the query |
||
129 | $this->types = OrganizationType::find() |
||
130 | ->organization($this); |
||
131 | |||
132 | // Remove all types |
||
133 | $this->types->setCachedResult([]); |
||
134 | |||
135 | if (!empty($types)) { |
||
136 | if (!is_array($types)) { |
||
137 | $types = [$types]; |
||
138 | } |
||
139 | |||
140 | $this->addTypes($types); |
||
141 | } |
||
142 | |||
143 | return $this; |
||
144 | } |
||
145 | |||
146 | /** |
||
147 | * AssociateUserToOrganization an array of types to an organization |
||
148 | * |
||
149 | * @param $types |
||
150 | * @return $this |
||
151 | */ |
||
152 | public function addTypes(array $types) |
||
153 | { |
||
154 | // In case a config is directly passed |
||
155 | if (ArrayHelper::isAssociative($types)) { |
||
156 | $types = [$types]; |
||
157 | } |
||
158 | |||
159 | foreach ($types as $key => $type) { |
||
160 | // Ensure we have a model |
||
161 | if (!$type instanceof OrganizationType) { |
||
162 | $type = $this->resolveType($type); |
||
163 | } |
||
164 | |||
165 | $this->addType($type); |
||
166 | } |
||
167 | |||
168 | return $this; |
||
169 | } |
||
170 | |||
171 | /** |
||
172 | * AssociateUserToOrganization a type to an organization |
||
173 | * |
||
174 | * @param OrganizationType $type |
||
175 | * @return $this |
||
176 | */ |
||
177 | public function addType(OrganizationType $type) |
||
178 | { |
||
179 | $currentTypes = $this->getTypes()->all(); |
||
180 | |||
181 | $indexedTypes = ArrayHelper::index( |
||
182 | $currentTypes, |
||
183 | 'handle' |
||
184 | ); |
||
185 | |||
186 | if (!array_key_exists($type->handle, $indexedTypes)) { |
||
187 | $currentTypes[] = $type; |
||
188 | $this->getTypes()->setCachedResult($currentTypes); |
||
189 | } |
||
190 | |||
191 | return $this; |
||
192 | } |
||
193 | |||
194 | /** |
||
195 | * DissociateUserFromOrganization a type from an organization |
||
196 | * |
||
197 | * @param array $types |
||
198 | * @return $this |
||
199 | */ |
||
200 | public function removeTypes(array $types) |
||
201 | { |
||
202 | // In case a config is directly passed |
||
203 | if (ArrayHelper::isAssociative($types)) { |
||
204 | $types = [$types]; |
||
205 | } |
||
206 | |||
207 | foreach ($types as $key => $type) { |
||
208 | if (!$type instanceof OrganizationType) { |
||
209 | $type = $this->resolveType($type); |
||
210 | } |
||
211 | |||
212 | $this->removeType($type); |
||
213 | } |
||
214 | |||
215 | return $this; |
||
216 | } |
||
217 | |||
218 | /** |
||
219 | * @param mixed $type |
||
220 | * @return OrganizationType |
||
221 | */ |
||
222 | protected function resolveType($type): OrganizationType |
||
223 | { |
||
224 | if (null !== ($type = OrganizationType::findOne($type))) { |
||
225 | return $type; |
||
226 | } |
||
227 | |||
228 | if (!is_array($type)) { |
||
229 | $type = ArrayHelper::toArray($type, [], false); |
||
230 | } |
||
231 | |||
232 | return new OrganizationType($type); |
||
233 | } |
||
234 | |||
235 | /** |
||
236 | * DissociateUserFromOrganization a type from an organization |
||
237 | * |
||
238 | * @param OrganizationType $type |
||
239 | * @return $this |
||
240 | */ |
||
241 | public function removeType(OrganizationType $type) |
||
242 | { |
||
243 | $indexedTypes = ArrayHelper::index( |
||
244 | $this->getTypes()->all(), |
||
245 | 'handle' |
||
246 | ); |
||
247 | |||
248 | // Does the type already exist? |
||
249 | if (array_key_exists($type->handle, $indexedTypes)) { |
||
250 | unset($indexedTypes[$type->handle]); |
||
251 | |||
252 | $this->getTypes()->setCachedResult( |
||
253 | array_values($indexedTypes) |
||
254 | ); |
||
255 | } |
||
256 | |||
257 | return $this; |
||
258 | } |
||
259 | |||
260 | /** |
||
261 | * Reset types |
||
262 | * |
||
263 | * @return $this |
||
264 | */ |
||
265 | public function resetTypes() |
||
266 | { |
||
267 | $this->types = null; |
||
268 | return $this; |
||
269 | } |
||
270 | |||
271 | /** |
||
272 | * Get an associated type by identifier (id/handle) |
||
273 | * |
||
274 | * @param $identifier |
||
275 | * @return null|TypeModel |
||
276 | */ |
||
277 | public function getType($identifier) |
||
278 | { |
||
279 | // Determine index type |
||
280 | $indexBy = (is_numeric($identifier)) ? 'id' : 'handle'; |
||
281 | |||
282 | // Find all types |
||
283 | $allTypes = ArrayHelper::index( |
||
284 | $this->getTypes()->all(), |
||
285 | $indexBy |
||
286 | ); |
||
287 | |||
288 | return array_key_exists($identifier, $allTypes) ? $allTypes[$identifier] : null; |
||
289 | } |
||
290 | |||
291 | /** |
||
292 | * Identify whether a type is associated to the element |
||
293 | * |
||
294 | * @param TypeModel|null $type |
||
295 | * @return bool |
||
296 | */ |
||
297 | public function hasType(TypeModel $type = null): bool |
||
298 | { |
||
299 | if (null === $type) { |
||
300 | return !empty($this->getTypes()); |
||
301 | } |
||
302 | |||
303 | return null !== $this->getType($type->id); |
||
304 | } |
||
305 | |||
306 | |||
307 | /************************************************************ |
||
308 | * PRIMARY TYPE |
||
309 | ************************************************************/ |
||
310 | |||
311 | /** |
||
312 | * Identify whether a primary type is set |
||
313 | * |
||
314 | * @return bool |
||
315 | */ |
||
316 | public function hasPrimaryType() |
||
317 | { |
||
318 | return $this->getTypes()->one() instanceof TypeModel; |
||
319 | } |
||
320 | |||
321 | /** |
||
322 | * Identify whether the type is primary |
||
323 | * |
||
324 | * @param $type |
||
325 | * @return bool |
||
326 | */ |
||
327 | public function isPrimaryType(TypeModel $type) |
||
328 | { |
||
329 | if ($primaryType = $this->getPrimaryType()) { |
||
330 | return $primaryType->id === $type->id; |
||
331 | } |
||
332 | |||
333 | return false; |
||
334 | } |
||
335 | |||
336 | /** |
||
337 | * @param TypeModel $type |
||
338 | * @return $this |
||
339 | */ |
||
340 | public function setPrimaryType(TypeModel $type = null) |
||
341 | { |
||
342 | if (null === $type) { |
||
343 | return $this; |
||
344 | } |
||
345 | |||
346 | return $this->setTypes( |
||
347 | array_merge( |
||
348 | [ |
||
349 | $type |
||
350 | ], |
||
351 | $this->getTypes()->all() |
||
352 | ) |
||
353 | ); |
||
354 | } |
||
355 | |||
356 | /** |
||
357 | * Get the primary type |
||
358 | * |
||
359 | * @return TypeModel|null |
||
360 | */ |
||
361 | public function getPrimaryType() |
||
362 | { |
||
363 | if (!$this->hasPrimaryType()) { |
||
364 | return null; |
||
365 | } |
||
366 | |||
367 | return $this->getTypes()->one(); |
||
368 | } |
||
369 | |||
370 | /******************************************* |
||
371 | * ASSOCIATE and/or DISASSOCIATE |
||
372 | *******************************************/ |
||
373 | |||
374 | /** |
||
375 | * @return bool |
||
376 | * @throws \Throwable |
||
377 | * @throws \flipbox\craft\ember\exceptions\RecordNotFoundException |
||
378 | * @throws \yii\db\StaleObjectException |
||
379 | */ |
||
380 | public function saveTypes(): bool |
||
381 | { |
||
382 | // No changes? |
||
383 | if (null === ($types = $this->getTypes()->getCachedResult())) { |
||
384 | return true; |
||
385 | } |
||
386 | |||
387 | $currentAssociations = OrganizationTypeAssociation::find() |
||
388 | ->organizationId($this->getId() ?: false) |
||
0 ignored issues
–
show
|
|||
389 | ->indexBy('typeId') |
||
390 | ->all(); |
||
391 | |||
392 | $success = true; |
||
393 | $associations = []; |
||
394 | $order = 1; |
||
395 | foreach ($types as $type) { |
||
396 | if (null === ($association = ArrayHelper::remove($currentAssociations, $type->getId()))) { |
||
397 | $association = (new OrganizationTypeAssociation()) |
||
398 | ->setType($type) |
||
399 | ->setOrganization($this); |
||
400 | } |
||
401 | |||
402 | $association->sortOrder = $order++; |
||
403 | |||
404 | $associations[] = $association; |
||
405 | } |
||
406 | |||
407 | // Delete those removed |
||
408 | foreach ($currentAssociations as $currentAssociation) { |
||
409 | if (!$currentAssociation->delete()) { |
||
410 | $success = false; |
||
411 | } |
||
412 | } |
||
413 | |||
414 | foreach ($associations as $association) { |
||
415 | if (!$association->save()) { |
||
416 | $success = false; |
||
417 | } |
||
418 | } |
||
419 | |||
420 | if (!$success) { |
||
421 | $this->addError('types', 'Unable to associate types.'); |
||
0 ignored issues
–
show
It seems like
addError() must be provided by classes using this trait. How about adding it as abstract method to this trait?
This check looks for methods that are used by a trait but not required by it. To illustrate, let’s look at the following code example trait Idable {
public function equalIds(Idable $other) {
return $this->getId() === $other->getId();
}
}
The trait Adding the
Loading history...
|
|||
422 | } |
||
423 | |||
424 | return $success; |
||
425 | } |
||
426 | |||
427 | /** |
||
428 | * @param OrganizationTypeQuery $query |
||
429 | * @return bool |
||
430 | * @throws \Throwable |
||
431 | */ |
||
432 | public function associateTypes(OrganizationTypeQuery $query): bool |
||
433 | { |
||
434 | $types = $query->all(); |
||
435 | |||
436 | if (empty($types)) { |
||
437 | return true; |
||
438 | } |
||
439 | |||
440 | $currentAssociations = $currentAssociations = OrganizationTypeAssociation::find() |
||
441 | ->organizationId($this->getId() ?: false) |
||
0 ignored issues
–
show
It seems like
getId() must be provided by classes using this trait. How about adding it as abstract method to this trait?
This check looks for methods that are used by a trait but not required by it. To illustrate, let’s look at the following code example trait Idable {
public function equalIds(Idable $other) {
return $this->getId() === $other->getId();
}
}
The trait Adding the
Loading history...
|
|||
442 | ->indexBy('typeId') |
||
443 | ->all(); |
||
444 | |||
445 | $success = true; |
||
446 | foreach ($types as $type) { |
||
447 | if (null === ($association = ArrayHelper::remove($currentAssociations, $type->getId()))) { |
||
448 | $association = (new OrganizationTypeAssociation()) |
||
449 | ->setType($type) |
||
450 | ->setOrganization($this); |
||
451 | } |
||
452 | |||
453 | if (!$association->save()) { |
||
454 | $success = false; |
||
455 | } |
||
456 | } |
||
457 | |||
458 | if (!$success) { |
||
459 | $this->addError('organizations', 'Unable to associate types.'); |
||
0 ignored issues
–
show
It seems like
addError() must be provided by classes using this trait. How about adding it as abstract method to this trait?
This check looks for methods that are used by a trait but not required by it. To illustrate, let’s look at the following code example trait Idable {
public function equalIds(Idable $other) {
return $this->getId() === $other->getId();
}
}
The trait Adding the
Loading history...
|
|||
460 | } |
||
461 | |||
462 | $this->resetTypes(); |
||
463 | |||
464 | return $success; |
||
465 | } |
||
466 | |||
467 | /** |
||
468 | * @param OrganizationTypeQuery $query |
||
469 | * @return bool |
||
470 | */ |
||
471 | public function dissociateTypes(OrganizationTypeQuery $query): bool |
||
472 | { |
||
473 | $types = $query->all(); |
||
474 | |||
475 | if (empty($types)) { |
||
476 | return true; |
||
477 | } |
||
478 | |||
479 | $currentAssociations = $currentAssociations = OrganizationTypeAssociation::find() |
||
480 | ->organizationId($this->getId() ?: false) |
||
0 ignored issues
–
show
It seems like
getId() must be provided by classes using this trait. How about adding it as abstract method to this trait?
This check looks for methods that are used by a trait but not required by it. To illustrate, let’s look at the following code example trait Idable {
public function equalIds(Idable $other) {
return $this->getId() === $other->getId();
}
}
The trait Adding the
Loading history...
|
|||
481 | ->indexBy('typeId') |
||
482 | ->all(); |
||
483 | |||
484 | $success = true; |
||
485 | foreach ($types as $type) { |
||
486 | if (null === ($association = ArrayHelper::remove($currentAssociations, $type->getId()))) { |
||
487 | continue; |
||
488 | } |
||
489 | |||
490 | if (!$association->delete()) { |
||
491 | $success = false; |
||
492 | } |
||
493 | } |
||
494 | |||
495 | if (!$success) { |
||
496 | $this->addError('organizations', 'Unable to dissociate types.'); |
||
0 ignored issues
–
show
It seems like
addError() must be provided by classes using this trait. How about adding it as abstract method to this trait?
This check looks for methods that are used by a trait but not required by it. To illustrate, let’s look at the following code example trait Idable {
public function equalIds(Idable $other) {
return $this->getId() === $other->getId();
}
}
The trait Adding the
Loading history...
|
|||
497 | } |
||
498 | |||
499 | $this->resetTypes(); |
||
500 | |||
501 | return $success; |
||
502 | } |
||
503 | } |
||
504 |
This check looks for methods that are used by a trait but not required by it.
To illustrate, let’s look at the following code example
The trait
Idable
provides a methodequalsId
that in turn relies on the methodgetId()
. If this method does not exist on a class mixing in this trait, the method will fail.Adding the
getId()
as an abstract method to the trait will make sure it is available.