This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
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\relationships; |
||
10 | |||
11 | use Craft; |
||
12 | use craft\base\ElementInterface; |
||
13 | use craft\helpers\ArrayHelper; |
||
14 | use flipbox\organizations\records\UserAssociation; |
||
15 | use Tightenco\Collect\Support\Collection; |
||
16 | use yii\base\Exception; |
||
17 | use yii\db\ActiveRecord; |
||
18 | use yii\db\QueryInterface; |
||
19 | |||
20 | /** |
||
21 | * @author Flipbox Factory <[email protected]> |
||
22 | * @since 2.0.0 |
||
23 | * |
||
24 | * @mixin RelationshipInterface |
||
25 | */ |
||
26 | trait RelationshipTrait |
||
27 | { |
||
28 | use MutatedTrait; |
||
29 | |||
30 | /** |
||
31 | * @var Collection|null |
||
32 | */ |
||
33 | protected $relations; |
||
34 | |||
35 | /** |
||
36 | * @param null $object |
||
37 | * @return int|null |
||
38 | */ |
||
39 | abstract protected function findKey($object = null); |
||
40 | |||
41 | /** |
||
42 | * Create a new association/relationship record |
||
43 | * |
||
44 | * @param $object |
||
45 | * @return ActiveRecord |
||
46 | */ |
||
47 | abstract protected function create($object): ActiveRecord; |
||
48 | |||
49 | /** |
||
50 | * Resolve the object that will be related |
||
51 | * |
||
52 | * @param $object |
||
53 | * @return mixed |
||
54 | */ |
||
55 | abstract protected function resolveObjectInternal($object); |
||
56 | |||
57 | /** |
||
58 | * A collection of existing relationships, indexed by id. We'll compare these with |
||
59 | * and new relations to determine if we need to add/remove relations |
||
60 | * |
||
61 | * @return Collection |
||
62 | */ |
||
63 | abstract protected function existingRelationships(): Collection; |
||
64 | |||
65 | /** |
||
66 | * An array of relationships to save and an array of relationships to delete. This will |
||
67 | * want to be used as such: `list($save, $delete) = $this->delta();` |
||
68 | * |
||
69 | * @return array |
||
70 | */ |
||
71 | abstract protected function delta(): array; |
||
72 | |||
73 | /** |
||
74 | * @param ActiveRecord|ElementInterface|int|string $object |
||
75 | * @return ActiveRecord |
||
76 | */ |
||
77 | public function findOrCreate($object): ActiveRecord |
||
78 | { |
||
79 | if (null === ($association = $this->findOne($object))) { |
||
80 | $association = $this->create($object); |
||
81 | } |
||
82 | |||
83 | return $association; |
||
84 | } |
||
85 | |||
86 | /** |
||
87 | * @param ActiveRecord|ElementInterface|int|string $object |
||
88 | * @return ActiveRecord |
||
89 | * @throws Exception |
||
90 | */ |
||
91 | public function findOrFail($object): ActiveRecord |
||
92 | { |
||
93 | if (null === ($association = $this->findOne($object))) { |
||
94 | throw new Exception("Association could not be found."); |
||
95 | } |
||
96 | |||
97 | return $association; |
||
98 | } |
||
99 | |||
100 | /** |
||
101 | * @param ActiveRecord|ElementInterface|int|string|null $object |
||
102 | * @return ActiveRecord|null |
||
103 | */ |
||
104 | public function findOne($object = null) |
||
105 | { |
||
106 | if (null === ($key = $this->findKey($object))) { |
||
107 | return null; |
||
108 | } |
||
109 | |||
110 | return $this->getRelationships()->get($key); |
||
111 | } |
||
112 | |||
113 | |||
114 | /************************************************************ |
||
115 | * COLLECTIONS |
||
116 | ************************************************************/ |
||
117 | |||
118 | /** |
||
119 | * @inheritDoc |
||
120 | */ |
||
121 | public function getRelationships(): Collection |
||
122 | { |
||
123 | if (null === $this->relations) { |
||
124 | $this->relations = $this->existingRelationships(); |
||
125 | } |
||
126 | |||
127 | return $this->relations; |
||
128 | } |
||
129 | |||
130 | |||
131 | /************************************************************ |
||
132 | * ADD / REMOVE |
||
133 | ************************************************************/ |
||
134 | |||
135 | /** |
||
136 | * Add one or many object relations (but do not save) |
||
137 | * |
||
138 | * @param $objects |
||
139 | * @param array $attributes |
||
140 | * @return RelationshipInterface |
||
141 | */ |
||
142 | public function add($objects, array $attributes = []): RelationshipInterface |
||
143 | { |
||
144 | foreach ($this->objectArray($objects) as $object) { |
||
145 | $this->addOne($object, $attributes); |
||
146 | } |
||
147 | |||
148 | return $this; |
||
149 | } |
||
150 | |||
151 | /** |
||
152 | * @param $object |
||
153 | * @param array $attributes |
||
154 | * @return RelationshipInterface |
||
155 | */ |
||
156 | private function addOne($object, array $attributes = []): RelationshipInterface |
||
157 | { |
||
158 | $isNew = false; |
||
159 | |||
160 | // Check if it's already linked |
||
161 | if (null === ($association = $this->findOne($object))) { |
||
162 | $association = $this->create($object); |
||
163 | $isNew = true; |
||
164 | } |
||
165 | |||
166 | // Modify? |
||
167 | if (!empty($attributes)) { |
||
168 | Craft::configure( |
||
169 | $association, |
||
170 | $attributes |
||
171 | ); |
||
172 | |||
173 | $this->mutated = true; |
||
174 | |||
175 | if (!$isNew) { |
||
176 | $this->updateCollection($this->relations, $association); |
||
177 | } |
||
178 | } |
||
179 | |||
180 | if ($isNew) { |
||
181 | $this->addToRelations($association); |
||
182 | } |
||
183 | |||
184 | return $this; |
||
185 | } |
||
186 | |||
187 | /** |
||
188 | * @param $objects |
||
189 | * @return RelationshipInterface |
||
190 | */ |
||
191 | public function remove($objects): RelationshipInterface |
||
192 | { |
||
193 | foreach ($this->objectArray($objects) as $object) { |
||
194 | if (null !== ($key = $this->findKey($object))) { |
||
195 | $this->removeFromRelations($key); |
||
196 | } |
||
197 | } |
||
198 | |||
199 | return $this; |
||
200 | } |
||
201 | |||
202 | |||
203 | /******************************************* |
||
204 | * SAVE |
||
205 | *******************************************/ |
||
206 | |||
207 | /** |
||
208 | * @return bool |
||
209 | */ |
||
210 | public function save(): bool |
||
211 | { |
||
212 | // No changes? |
||
213 | if (!$this->isMutated()) { |
||
214 | return true; |
||
215 | } |
||
216 | |||
217 | $success = true; |
||
218 | |||
219 | list($save, $delete) = $this->delta(); |
||
220 | |||
221 | foreach ($delete as $relationship) { |
||
222 | if (!$relationship->delete()) { |
||
223 | $success = false; |
||
224 | } |
||
225 | } |
||
226 | |||
227 | foreach ($save as $relationship) { |
||
228 | if (!$relationship->save()) { |
||
229 | $success = false; |
||
230 | } |
||
231 | } |
||
232 | |||
233 | $this->reset(); |
||
234 | |||
235 | return $success; |
||
236 | } |
||
237 | |||
238 | |||
239 | /************************************************************ |
||
240 | * UTILITIES |
||
241 | ************************************************************/ |
||
242 | |||
243 | /** |
||
244 | * Reset associations |
||
245 | * @return RelationshipInterface |
||
246 | */ |
||
247 | public function reset(): RelationshipInterface |
||
248 | { |
||
249 | $this->relations = null; |
||
250 | $this->mutated = false; |
||
251 | return $this; |
||
252 | } |
||
253 | |||
254 | /** |
||
255 | * Reset associations |
||
256 | * @return RelationshipInterface |
||
257 | */ |
||
258 | public function clear(): RelationshipInterface |
||
259 | { |
||
260 | $this->newRelations([]); |
||
261 | return $this; |
||
262 | } |
||
263 | |||
264 | /** |
||
265 | * @param ActiveRecord|ElementInterface|int|string $object |
||
266 | * @return bool |
||
267 | */ |
||
268 | public function exists($object): bool |
||
269 | { |
||
270 | return null !== $this->findKey($object); |
||
271 | } |
||
272 | |||
273 | |||
274 | /******************************************* |
||
275 | * COLLECTION UTILS |
||
276 | *******************************************/ |
||
277 | |||
278 | /** |
||
279 | * @param array $associations |
||
280 | * @param bool $mutated |
||
281 | * @return static |
||
282 | */ |
||
283 | protected function newRelations(array $associations, bool $mutated = true): self |
||
284 | { |
||
285 | $this->relations = $this->createRelations($associations); |
||
286 | $this->mutated = $mutated; |
||
287 | |||
288 | return $this; |
||
289 | } |
||
290 | |||
291 | /** |
||
292 | * @param $association |
||
293 | * @return RelationshipTrait |
||
294 | */ |
||
295 | protected function addToRelations($association): self |
||
296 | { |
||
297 | $this->insertCollection($this->getRelationships(), $association); |
||
298 | $this->mutated = true; |
||
299 | |||
300 | return $this; |
||
301 | } |
||
302 | |||
303 | /** |
||
304 | * @param int $key |
||
305 | * @return RelationshipTrait |
||
306 | */ |
||
307 | protected function removeFromRelations(int $key): self |
||
308 | { |
||
309 | $this->relations->forget($key); |
||
310 | $this->mutated = true; |
||
311 | |||
312 | return $this; |
||
313 | } |
||
314 | |||
315 | /** |
||
316 | * @param array $associations |
||
317 | * @return Collection |
||
318 | */ |
||
319 | protected function createRelations(array $associations = []): Collection |
||
320 | { |
||
321 | $collection = new Collection(); |
||
322 | foreach ($associations as $association) { |
||
323 | $this->insertCollection($collection, $association); |
||
324 | } |
||
325 | |||
326 | return $collection; |
||
327 | } |
||
328 | |||
329 | /** |
||
330 | * Update a relation that's already association. |
||
331 | * |
||
332 | * @param Collection $collection |
||
333 | * @param $association |
||
334 | */ |
||
335 | protected function updateCollection(Collection $collection, $association) |
||
0 ignored issues
–
show
|
|||
336 | { |
||
337 | } |
||
338 | |||
339 | /** |
||
340 | * Insert a relation that's not already associated |
||
341 | * |
||
342 | * @param Collection $collection |
||
343 | * @param $association |
||
344 | */ |
||
345 | protected function insertCollection(Collection $collection, $association) |
||
346 | { |
||
347 | $collection->push($association); |
||
348 | } |
||
349 | |||
350 | |||
351 | /******************************************* |
||
352 | * RESOLVERS |
||
353 | *******************************************/ |
||
354 | |||
355 | /** |
||
356 | * @inheritDoc |
||
357 | */ |
||
358 | protected function resolveObject($object = null) |
||
359 | { |
||
360 | if (null === $object) { |
||
361 | return null; |
||
362 | } |
||
363 | |||
364 | if (is_array($object) && |
||
365 | null !== ($id = ArrayHelper::getValue($object, 'id')) |
||
366 | ) { |
||
367 | $object = ['id' => $id]; |
||
368 | } |
||
369 | |||
370 | return $this->resolveObjectInternal($object); |
||
371 | } |
||
372 | |||
373 | /** |
||
374 | * Ensure we're working with an array of objects, not configs, etc |
||
375 | * |
||
376 | * @param array|QueryInterface|Collection|ElementInterface|UserAssociation $objects |
||
377 | * @return array |
||
378 | */ |
||
379 | protected function objectArray($objects): array |
||
380 | { |
||
381 | if ($objects instanceof QueryInterface || $objects instanceof Collection) { |
||
382 | $objects = $objects->all(); |
||
383 | } |
||
384 | |||
385 | // proper array |
||
386 | if (!is_array($objects) || ArrayHelper::isAssociative($objects)) { |
||
387 | $objects = [$objects]; |
||
388 | } |
||
389 | |||
390 | return array_filter($objects); |
||
391 | } |
||
392 | } |
||
393 |
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.