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 | namespace Analogue\ORM\Relationships; |
||
4 | |||
5 | use Analogue\ORM\Mappable; |
||
6 | use Analogue\ORM\System\Query; |
||
7 | use Analogue\ORM\System\Mapper; |
||
8 | use Analogue\ORM\EntityCollection; |
||
9 | use Illuminate\Database\Query\Expression; |
||
10 | use Analogue\ORM\System\InternallyMappable; |
||
11 | use Analogue\ORM\Exceptions\EntityNotFoundException; |
||
12 | |||
13 | class BelongsToMany extends Relationship |
||
14 | { |
||
15 | /** |
||
16 | * The intermediate table for the relation. |
||
17 | * |
||
18 | * @var string |
||
19 | */ |
||
20 | protected $table; |
||
21 | |||
22 | /** |
||
23 | * The foreign key of the parent model. |
||
24 | * |
||
25 | * @var string |
||
26 | */ |
||
27 | protected $foreignKey; |
||
28 | |||
29 | /** |
||
30 | * The associated key of the relation. |
||
31 | * |
||
32 | * @var string |
||
33 | */ |
||
34 | protected $otherKey; |
||
35 | |||
36 | /** |
||
37 | * The "name" of the relationship. |
||
38 | * |
||
39 | * @var string |
||
40 | */ |
||
41 | protected $relationName; |
||
42 | |||
43 | /** |
||
44 | * The pivot table columns to retrieve. |
||
45 | * |
||
46 | * @var array |
||
47 | */ |
||
48 | protected $pivotColumns = []; |
||
49 | |||
50 | /** |
||
51 | * This relationship has pivot attributes |
||
52 | * |
||
53 | * @var boolean |
||
54 | */ |
||
55 | protected static $hasPivot = true; |
||
56 | |||
57 | /** |
||
58 | * Create a new has many relationship instance. |
||
59 | * |
||
60 | * @param Mapper $mapper |
||
61 | * @param Mappable $parent |
||
62 | * @param string $table |
||
63 | * @param string $foreignKey |
||
64 | * @param string $otherKey |
||
65 | * @param string $relationName |
||
66 | */ |
||
67 | public function __construct(Mapper $mapper, $parent, $table, $foreignKey, $otherKey, $relationName = null) |
||
68 | { |
||
69 | $this->table = $table; |
||
70 | $this->otherKey = $otherKey; |
||
71 | $this->foreignKey = $foreignKey; |
||
72 | $this->relationName = $relationName; |
||
73 | |||
74 | parent::__construct($mapper, $parent); |
||
75 | } |
||
76 | |||
77 | /** |
||
78 | * @param $related |
||
79 | * @return mixed |
||
80 | */ |
||
81 | public function attachTo($related) |
||
82 | { |
||
83 | } |
||
84 | |||
85 | /** |
||
86 | * @param $related |
||
87 | * @return mixed |
||
88 | */ |
||
89 | public function detachFrom($related) |
||
90 | { |
||
91 | $ids = $this->getIdsFromHashes([$related]); |
||
92 | |||
93 | $this->detach($ids); |
||
94 | } |
||
95 | |||
96 | /** |
||
97 | * @param $related |
||
98 | */ |
||
99 | public function detachMany($related) |
||
100 | { |
||
101 | $ids = $this->getIdsFromHashes($related); |
||
102 | |||
103 | $this->detach($ids); |
||
104 | } |
||
105 | |||
106 | /** |
||
107 | * @param array $hashes |
||
108 | * @return array |
||
109 | */ |
||
110 | protected function getIdsFromHashes(array $hashes) |
||
111 | { |
||
112 | $ids = []; |
||
113 | |||
114 | foreach ($hashes as $hash) { |
||
115 | $split = explode('.', $hash); |
||
116 | $ids[] = $split[1]; |
||
117 | } |
||
118 | return $ids; |
||
119 | } |
||
120 | |||
121 | /** |
||
122 | * Get the results of the relationship. |
||
123 | * |
||
124 | * @param $relation |
||
125 | * |
||
126 | * @return EntityCollection |
||
127 | */ |
||
128 | public function getResults($relation) |
||
129 | { |
||
130 | $results = $this->get(); |
||
131 | |||
132 | $this->cacheRelation($results, $relation); |
||
133 | |||
134 | return $results; |
||
135 | } |
||
136 | |||
137 | /** |
||
138 | * Set a where clause for a pivot table column. |
||
139 | * |
||
140 | * @param string $column |
||
141 | * @param string $operator |
||
142 | * @param mixed $value |
||
143 | * @param string $boolean |
||
144 | * @return self |
||
145 | */ |
||
146 | public function wherePivot($column, $operator = null, $value = null, $boolean = 'and') |
||
147 | { |
||
148 | return $this->where($this->table . '.' . $column, $operator, $value, $boolean); |
||
0 ignored issues
–
show
|
|||
149 | } |
||
150 | |||
151 | /** |
||
152 | * Set an or where clause for a pivot table column. |
||
153 | * |
||
154 | * @param string $column |
||
155 | * @param string $operator |
||
156 | * @param mixed $value |
||
157 | * @return self |
||
158 | */ |
||
159 | public function orWherePivot($column, $operator = null, $value = null) |
||
160 | { |
||
161 | return $this->wherePivot($column, $operator, $value, 'or'); |
||
162 | } |
||
163 | |||
164 | /** |
||
165 | * Return Pivot attributes when available on a relationship |
||
166 | * |
||
167 | * @return array |
||
168 | */ |
||
169 | public function getPivotAttributes() |
||
170 | { |
||
171 | return $this->pivotColumns; |
||
172 | } |
||
173 | |||
174 | /** |
||
175 | * Execute the query and get the first result. |
||
176 | * |
||
177 | * @param array $columns |
||
178 | * @return mixed |
||
179 | */ |
||
180 | public function first($columns = ['*']) |
||
181 | { |
||
182 | $results = $this->take(1)->get($columns); |
||
0 ignored issues
–
show
The method
take does not exist on object<Analogue\ORM\Relationships\BelongsToMany> ? Since you implemented __call , maybe consider adding a @method annotation.
If you implement This is often the case, when 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 { }
![]() |
|||
183 | |||
184 | return count($results) > 0 ? $results->first() : null; |
||
185 | } |
||
186 | |||
187 | /** |
||
188 | * Execute the query and get the first result or throw an exception. |
||
189 | * |
||
190 | * @param array $columns |
||
191 | * |
||
192 | * @throws EntityNotFoundException |
||
193 | * |
||
194 | * @return Mappable|self |
||
195 | */ |
||
196 | public function firstOrFail($columns = ['*']) |
||
197 | { |
||
198 | if (!is_null($entity = $this->first($columns))) { |
||
199 | return $entity; |
||
200 | } |
||
201 | |||
202 | throw new EntityNotFoundException; |
||
203 | } |
||
204 | |||
205 | /** |
||
206 | * Execute the query as a "select" statement. |
||
207 | * |
||
208 | * @param array $columns |
||
209 | * @return \Analogue\ORM\EntityCollection |
||
210 | */ |
||
211 | public function get($columns = ['*']) |
||
212 | { |
||
213 | // First we'll add the proper select columns onto the query so it is run with |
||
214 | // the proper columns. Then, we will get the results and hydrate out pivot |
||
215 | // models with the result of those columns as a separate model relation. |
||
216 | $columns = $this->query->getQuery()->columns ? [] : $columns; |
||
217 | |||
218 | $select = $this->getSelectColumns($columns); |
||
219 | |||
220 | $entities = $this->query->addSelect($select)->getEntities(); |
||
0 ignored issues
–
show
The method
addSelect does not exist on object<Analogue\ORM\System\Query> ? Since you implemented __call , maybe consider adding a @method annotation.
If you implement This is often the case, when 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 { }
![]() |
|||
221 | |||
222 | $this->hydratePivotRelation($entities); |
||
223 | |||
224 | // If we actually found models we will also eager load any relationships that |
||
225 | // have been specified as needing to be eager loaded. This will solve the |
||
226 | // n + 1 query problem for the developer and also increase performance. |
||
227 | if (count($entities) > 0) { |
||
228 | $entities = $this->query->eagerLoadRelations($entities); |
||
229 | } |
||
230 | |||
231 | return $this->relatedMap->newCollection($entities); |
||
232 | } |
||
233 | |||
234 | /** |
||
235 | * Hydrate the pivot table relationship on the models. |
||
236 | * |
||
237 | * @param array $entities |
||
238 | * @return void |
||
239 | */ |
||
240 | protected function hydratePivotRelation(array $entities) |
||
241 | { |
||
242 | // To hydrate the pivot relationship, we will just gather the pivot attributes |
||
243 | // and create a new Pivot model, which is basically a dynamic model that we |
||
244 | // will set the attributes, table, and connections on so it they be used. |
||
245 | |||
246 | foreach ($entities as $entity) { |
||
247 | $entityWrapper = $this->factory->make($entity); |
||
248 | |||
249 | $pivot = $this->newExistingPivot($this->cleanPivotAttributes($entityWrapper)); |
||
250 | |||
251 | $entityWrapper->setEntityAttribute('pivot', $pivot); |
||
0 ignored issues
–
show
$pivot is of type object<Analogue\ORM\Relationships\Pivot> , but the function expects a string .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
252 | } |
||
253 | } |
||
254 | |||
255 | /** |
||
256 | * Get the pivot attributes from a model. |
||
257 | * |
||
258 | * @param $entity |
||
259 | * @return array |
||
260 | */ |
||
261 | protected function cleanPivotAttributes(InternallyMappable $entity) |
||
262 | { |
||
263 | $values = []; |
||
264 | |||
265 | $attributes = $entity->getEntityAttributes(); |
||
266 | |||
267 | foreach ($attributes as $key => $value) { |
||
268 | // To get the pivots attributes we will just take any of the attributes which |
||
269 | // begin with "pivot_" and add those to this arrays, as well as unsetting |
||
270 | // them from the parent's models since they exist in a different table. |
||
271 | if (strpos($key, 'pivot_') === 0) { |
||
272 | $values[substr($key, 6)] = $value; |
||
273 | |||
274 | unset($attributes[$key]); |
||
275 | } |
||
276 | } |
||
277 | |||
278 | // Rehydrate Entity with cleaned array. |
||
279 | $entity->setEntityAttributes($attributes); |
||
280 | |||
281 | return $values; |
||
282 | } |
||
283 | |||
284 | /** |
||
285 | * Set the base constraints on the relation query. |
||
286 | * |
||
287 | * @return void |
||
288 | */ |
||
289 | public function addConstraints() |
||
290 | { |
||
291 | $this->setJoin(); |
||
292 | |||
293 | if (static::$constraints) { |
||
294 | $this->setWhere(); |
||
295 | } |
||
296 | } |
||
297 | |||
298 | /** |
||
299 | * Add the constraints for a relationship count query. |
||
300 | * |
||
301 | * @param Query $query |
||
302 | * @param Query $parent |
||
303 | * @return Query |
||
304 | */ |
||
305 | public function getRelationCountQuery(Query $query, Query $parent) |
||
306 | { |
||
307 | if ($parent->getQuery()->from == $query->getQuery()->from) { |
||
308 | return $this->getRelationCountQueryForSelfJoin($query, $parent); |
||
309 | } |
||
310 | |||
311 | $this->setJoin($query); |
||
312 | |||
313 | return parent::getRelationCountQuery($query, $parent); |
||
314 | } |
||
315 | |||
316 | /** |
||
317 | * Add the constraints for a relationship count query on the same table. |
||
318 | * |
||
319 | * @param Query $query |
||
320 | * @param Query $parent |
||
321 | * @return Query |
||
322 | */ |
||
323 | public function getRelationCountQueryForSelfJoin(Query $query, Query $parent) |
||
0 ignored issues
–
show
|
|||
324 | { |
||
325 | $query->select(new Expression('count(*)')); |
||
0 ignored issues
–
show
The method
select does not exist on object<Analogue\ORM\System\Query> ? Since you implemented __call , maybe consider adding a @method annotation.
If you implement This is often the case, when 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 { }
![]() |
|||
326 | |||
327 | $tablePrefix = $this->query->getQuery()->getConnection()->getTablePrefix(); |
||
328 | |||
329 | $query->from($this->table . ' as ' . $tablePrefix . $hash = $this->getRelationCountHash()); |
||
0 ignored issues
–
show
The method
from does not exist on object<Analogue\ORM\System\Query> ? Since you implemented __call , maybe consider adding a @method annotation.
If you implement This is often the case, when 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 { }
![]() |
|||
330 | |||
331 | $key = $this->wrap($this->getQualifiedParentKeyName()); |
||
332 | |||
333 | return $query->where($hash . '.' . $this->foreignKey, '=', new Expression($key)); |
||
334 | } |
||
335 | |||
336 | /** |
||
337 | * Get a relationship join table hash. |
||
338 | * |
||
339 | * @return string |
||
340 | */ |
||
341 | public function getRelationCountHash() |
||
342 | { |
||
343 | return 'self_' . md5(microtime(true)); |
||
344 | } |
||
345 | |||
346 | /** |
||
347 | * Set the select clause for the relation query. |
||
348 | * |
||
349 | * @param array $columns |
||
350 | * @return \Analogue\ORM\Relationships\BelongsToMany |
||
351 | */ |
||
352 | protected function getSelectColumns(array $columns = ['*']) |
||
353 | { |
||
354 | if ($columns == ['*']) { |
||
355 | $columns = [$this->relatedMap->getTable() . '.*']; |
||
356 | } |
||
357 | |||
358 | return array_merge($columns, $this->getAliasedPivotColumns()); |
||
359 | } |
||
360 | |||
361 | /** |
||
362 | * Get the pivot columns for the relation. |
||
363 | * |
||
364 | * @return array |
||
365 | */ |
||
366 | protected function getAliasedPivotColumns() |
||
367 | { |
||
368 | $defaults = [$this->foreignKey, $this->otherKey]; |
||
369 | |||
370 | // We need to alias all of the pivot columns with the "pivot_" prefix so we |
||
371 | // can easily extract them out of the models and put them into the pivot |
||
372 | // relationships when they are retrieved and hydrated into the models. |
||
373 | $columns = []; |
||
374 | |||
375 | foreach (array_merge($defaults, $this->pivotColumns) as $column) { |
||
376 | $columns[] = $this->table . '.' . $column . ' as pivot_' . $column; |
||
377 | } |
||
378 | |||
379 | return array_unique($columns); |
||
380 | } |
||
381 | |||
382 | /** |
||
383 | * Set the join clause for the relation query. |
||
384 | * |
||
385 | * @param \Analogue\ORM\Query|null |
||
386 | * @return $this |
||
387 | */ |
||
388 | protected function setJoin($query = null) |
||
389 | { |
||
390 | $query = $query ?: $this->query; |
||
391 | |||
392 | // We need to join to the intermediate table on the related model's primary |
||
393 | // key column with the intermediate table's foreign key for the related |
||
394 | // model instance. Then we can set the "where" for the parent models. |
||
395 | $baseTable = $this->relatedMap->getTable(); |
||
396 | |||
397 | $key = $baseTable . '.' . $this->relatedMap->getKeyName(); |
||
398 | |||
399 | $query->join($this->table, $key, '=', $this->getOtherKey()); |
||
400 | |||
401 | return $this; |
||
402 | } |
||
403 | |||
404 | /** |
||
405 | * Set the where clause for the relation query. |
||
406 | * |
||
407 | * @return $this |
||
408 | */ |
||
409 | protected function setWhere() |
||
410 | { |
||
411 | $foreign = $this->getForeignKey(); |
||
412 | |||
413 | $parentKey = $this->parentMap->getKeyName(); |
||
414 | |||
415 | $this->query->where($foreign, '=', $this->parent->getEntityAttribute($parentKey)); |
||
416 | |||
417 | return $this; |
||
418 | } |
||
419 | |||
420 | /** |
||
421 | * Set the constraints for an eager load of the relation. |
||
422 | * |
||
423 | * @param array $entities |
||
424 | * @return void |
||
425 | */ |
||
426 | public function addEagerConstraints(array $entities) |
||
427 | { |
||
428 | $this->query->whereIn($this->getForeignKey(), $this->getKeys($entities)); |
||
0 ignored issues
–
show
The method
whereIn does not exist on object<Analogue\ORM\System\Query> ? Since you implemented __call , maybe consider adding a @method annotation.
If you implement This is often the case, when 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 { }
![]() |
|||
429 | } |
||
430 | |||
431 | /** |
||
432 | * Initialize the relation on a set of eneities. |
||
433 | * |
||
434 | * @param array $entities |
||
435 | * @param string $relation |
||
436 | * @return array |
||
437 | */ |
||
438 | public function initRelation(array $entities, $relation) |
||
439 | { |
||
440 | foreach ($entities as $entity) { |
||
441 | $entity = $this->factory->make($entity); |
||
442 | |||
443 | $entity->setEntityAttribute($relation, $this->relatedMap->newCollection()); |
||
444 | } |
||
445 | |||
446 | return $entities; |
||
447 | } |
||
448 | |||
449 | /** |
||
450 | * Match the eagerly loaded results to their parents. |
||
451 | * |
||
452 | * @param array $entities |
||
453 | * @param EntityCollection $results |
||
454 | * @param string $relation |
||
455 | * @return array |
||
456 | */ |
||
457 | View Code Duplication | public function match(array $entities, EntityCollection $results, $relation) |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
458 | { |
||
459 | $dictionary = $this->buildDictionary($results); |
||
460 | |||
461 | $keyName = $this->relatedMap->getKeyName(); |
||
462 | |||
463 | $cache = $this->parentMapper->getEntityCache(); |
||
464 | |||
465 | // Once we have an array dictionary of child objects we can easily match the |
||
466 | // children back to their parent using the dictionary and the keys on the |
||
467 | // the parent models. Then we will return the hydrated models back out. |
||
468 | foreach ($entities as $entity) { |
||
469 | $wrapper = $this->factory->make($entity); |
||
470 | |||
471 | if (isset($dictionary[$key = $wrapper->getEntityAttribute($keyName)])) { |
||
472 | $collection = $this->relatedMap->newCollection($dictionary[$key]); |
||
473 | |||
474 | $wrapper->setEntityAttribute($relation, $collection); |
||
475 | |||
476 | $cache->cacheLoadedRelationResult($entity, $relation, $collection, $this); |
||
477 | } |
||
478 | } |
||
479 | |||
480 | return $entities; |
||
481 | } |
||
482 | |||
483 | /** |
||
484 | * Build model dictionary keyed by the relation's foreign key. |
||
485 | * |
||
486 | * @param EntityCollection $results |
||
487 | * @return array |
||
488 | */ |
||
489 | protected function buildDictionary(EntityCollection $results) |
||
490 | { |
||
491 | $foreign = $this->foreignKey; |
||
492 | |||
493 | // First we will build a dictionary of child models keyed by the foreign key |
||
494 | // of the relation so that we will easily and quickly match them to their |
||
495 | // parents without having a possibly slow inner loops for every models. |
||
496 | $dictionary = []; |
||
497 | |||
498 | foreach ($results as $entity) { |
||
499 | $wrapper = $this->factory->make($entity); |
||
500 | |||
501 | $dictionary[$wrapper->getEntityAttribute('pivot')->$foreign][] = $entity; |
||
502 | } |
||
503 | |||
504 | return $dictionary; |
||
505 | } |
||
506 | |||
507 | /** |
||
508 | * Get all of the IDs for the related models. |
||
509 | * |
||
510 | * @return array |
||
511 | */ |
||
512 | public function getRelatedIds() |
||
513 | { |
||
514 | $fullKey = $this->relatedMap->getQualifiedKeyName(); |
||
515 | |||
516 | return $this->getQuery()->select($fullKey)->lists($this->relatedMap->getKeyName()); |
||
0 ignored issues
–
show
The method
select does not exist on object<Analogue\ORM\System\Query> ? Since you implemented __call , maybe consider adding a @method annotation.
If you implement This is often the case, when 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 { }
![]() |
|||
517 | } |
||
518 | |||
519 | /** |
||
520 | * Update Pivot |
||
521 | * |
||
522 | * @param \Analogue\ORM\Entity $entity |
||
523 | * @return void |
||
524 | */ |
||
525 | public function updatePivot($entity) |
||
526 | { |
||
527 | $keyName = $this->relatedMap->getKeyName(); |
||
528 | |||
529 | $this->updateExistingPivot( |
||
530 | $entity->getEntityAttribute($keyName), |
||
531 | $entity->getEntityAttribute('pivot')->getEntityAttributes() |
||
532 | ); |
||
533 | } |
||
534 | |||
535 | /** |
||
536 | * Update Multiple pivot |
||
537 | * |
||
538 | * @param $relatedEntities |
||
539 | * @return void |
||
540 | */ |
||
541 | public function updatePivots($relatedEntities) |
||
542 | { |
||
543 | foreach ($relatedEntities as $entity) { |
||
544 | $this->updatePivot($entity); |
||
545 | } |
||
546 | } |
||
547 | |||
548 | /** |
||
549 | * Create Pivot Records |
||
550 | * |
||
551 | * @param \Analogue\ORM\Entity[] $relatedEntities |
||
552 | * @return void |
||
553 | */ |
||
554 | public function createPivots($relatedEntities) |
||
555 | { |
||
556 | $keys = []; |
||
557 | $attributes = []; |
||
558 | |||
559 | $keyName = $this->relatedMap->getKeyName(); |
||
560 | |||
561 | foreach ($relatedEntities as $entity) { |
||
562 | $keys[] = $entity->getEntityAttribute($keyName); |
||
563 | } |
||
564 | |||
565 | $records = $this->createAttachRecords($keys, $attributes); |
||
566 | |||
567 | $this->query->getQuery()->from($this->table)->insert($records); |
||
568 | } |
||
569 | |||
570 | /** |
||
571 | * Update an existing pivot record on the table. |
||
572 | * |
||
573 | * @param mixed $id |
||
574 | * @param array $attributes |
||
575 | * @throws \InvalidArgumentException |
||
576 | * @return integer |
||
577 | */ |
||
578 | public function updateExistingPivot($id, array $attributes) |
||
579 | { |
||
580 | if (in_array($this->updatedAt(), $this->pivotColumns)) { |
||
581 | $attributes = $this->setTimestampsOnAttach($attributes, true); |
||
582 | } |
||
583 | |||
584 | return $this->newPivotStatementForId($id)->update($attributes); |
||
585 | } |
||
586 | |||
587 | /** |
||
588 | * Attach a model to the parent. |
||
589 | * |
||
590 | * @param mixed $id |
||
591 | * @param array $attributes |
||
592 | * @return void |
||
593 | */ |
||
594 | public function attach($id, array $attributes = []) |
||
595 | { |
||
596 | $query = $this->newPivotStatement(); |
||
597 | |||
598 | $query->insert($this->createAttachRecords((array) $id, $attributes)); |
||
599 | } |
||
600 | |||
601 | /** |
||
602 | * @param array $entities |
||
603 | * |
||
604 | * @throws \InvalidArgumentException |
||
605 | */ |
||
606 | public function sync(array $entities) |
||
607 | { |
||
608 | $this->detachExcept($entities); |
||
609 | } |
||
610 | |||
611 | /** |
||
612 | * Detach related entities that are not in $id |
||
613 | * |
||
614 | * @param array $entities |
||
615 | * |
||
616 | * @throws \InvalidArgumentException |
||
617 | * |
||
618 | * @return void |
||
619 | */ |
||
620 | protected function detachExcept(array $entities = []) |
||
621 | { |
||
622 | $query = $this->newPivotQuery(); |
||
623 | |||
624 | if (count($entities) > 0) { |
||
625 | $keys = $this->getKeys($entities); |
||
626 | |||
627 | $query->whereNotIn($this->otherKey, $keys); |
||
628 | } |
||
629 | $parentKey = $this->parentMap->getKeyName(); |
||
630 | |||
631 | $query->where($this->foreignKey, '=', $this->parent->getEntityAttribute($parentKey)); |
||
632 | |||
633 | $query->delete(); |
||
634 | |||
635 | $query = $this->newPivotQuery(); |
||
0 ignored issues
–
show
$query is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the ![]() |
|||
636 | } |
||
637 | |||
638 | |||
639 | /** |
||
640 | * Create an array of records to insert into the pivot table. |
||
641 | * |
||
642 | * @param array $ids |
||
643 | * @param array $attributes |
||
644 | * @return array |
||
645 | */ |
||
646 | protected function createAttachRecords($ids, array $attributes) |
||
647 | { |
||
648 | $records = []; |
||
649 | |||
650 | $timed = in_array($this->createdAt(), $this->pivotColumns); |
||
651 | |||
652 | // To create the attachment records, we will simply spin through the IDs given |
||
653 | // and create a new record to insert for each ID. Each ID may actually be a |
||
654 | // key in the array, with extra attributes to be placed in other columns. |
||
655 | foreach ($ids as $key => $value) { |
||
656 | $records[] = $this->attacher($key, $value, $attributes, $timed); |
||
657 | } |
||
658 | |||
659 | return $records; |
||
660 | } |
||
661 | |||
662 | /** |
||
663 | * Create a full attachment record payload. |
||
664 | * |
||
665 | * @param int $key |
||
666 | * @param mixed $value |
||
667 | * @param array $attributes |
||
668 | * @param bool $timed |
||
669 | * @return array |
||
670 | */ |
||
671 | protected function attacher($key, $value, $attributes, $timed) |
||
672 | { |
||
673 | list($id, $extra) = $this->getAttachId($key, $value, $attributes); |
||
674 | |||
675 | // To create the attachment records, we will simply spin through the IDs given |
||
676 | // and create a new record to insert for each ID. Each ID may actually be a |
||
677 | // key in the array, with extra attributes to be placed in other columns. |
||
678 | $record = $this->createAttachRecord($id, $timed); |
||
679 | |||
680 | return array_merge($record, $extra); |
||
681 | } |
||
682 | |||
683 | /** |
||
684 | * Get the attach record ID and extra attributes. |
||
685 | * |
||
686 | * @param int $key |
||
687 | * @param mixed $value |
||
688 | * @param array $attributes |
||
689 | * @return array |
||
690 | */ |
||
691 | protected function getAttachId($key, $value, array $attributes) |
||
692 | { |
||
693 | if (is_array($value)) { |
||
694 | return [$key, array_merge($value, $attributes)]; |
||
695 | } |
||
696 | |||
697 | return [$value, $attributes]; |
||
698 | } |
||
699 | |||
700 | /** |
||
701 | * Create a new pivot attachment record. |
||
702 | * |
||
703 | * @param int $id |
||
704 | * @param bool $timed |
||
705 | * @return array |
||
706 | */ |
||
707 | protected function createAttachRecord($id, $timed) |
||
708 | { |
||
709 | $parentKey = $this->parentMap->getKeyName(); |
||
710 | |||
711 | $record = []; |
||
712 | |||
713 | $record[$this->foreignKey] = $this->parent->getEntityAttribute($parentKey); |
||
714 | |||
715 | $record[$this->otherKey] = $id; |
||
716 | |||
717 | // If the record needs to have creation and update timestamps, we will make |
||
718 | // them by calling the parent model's "freshTimestamp" method which will |
||
719 | // provide us with a fresh timestamp in this model's preferred format. |
||
720 | if ($timed) { |
||
721 | $record = $this->setTimestampsOnAttach($record); |
||
722 | } |
||
723 | |||
724 | return $record; |
||
725 | } |
||
726 | |||
727 | /** |
||
728 | * Set the creation and update timestamps on an attach record. |
||
729 | * |
||
730 | * @param array $record |
||
731 | * @param bool $exists |
||
732 | * @return array |
||
733 | */ |
||
734 | protected function setTimestampsOnAttach(array $record, $exists = false) |
||
735 | { |
||
736 | $fresh = $this->freshTimestamp(); |
||
737 | |||
738 | if (!$exists) { |
||
739 | $record[$this->createdAt()] = $fresh; |
||
740 | } |
||
741 | |||
742 | $record[$this->updatedAt()] = $fresh; |
||
743 | |||
744 | return $record; |
||
745 | } |
||
746 | |||
747 | /** |
||
748 | * @param EntityCollection $entities |
||
749 | * @return array |
||
750 | */ |
||
751 | protected function getModelKeysFromCollection(EntityCollection $entities) |
||
752 | { |
||
753 | $keyName = $this->relatedMap->getKeyName(); |
||
754 | |||
755 | return array_map(function ($m) use ($keyName) { |
||
756 | return $m->$keyName; |
||
757 | }, $entities); |
||
758 | } |
||
759 | |||
760 | /** |
||
761 | * Detach models from the relationship. |
||
762 | * |
||
763 | * @param int|array $ids |
||
764 | * @throws \InvalidArgumentException |
||
765 | * @return int |
||
766 | */ |
||
767 | public function detach($ids = []) |
||
768 | { |
||
769 | if ($ids instanceof EntityCollection) { |
||
770 | $ids = (array) $ids->modelKeys(); |
||
771 | } |
||
772 | |||
773 | $query = $this->newPivotQuery(); |
||
774 | |||
775 | // If associated IDs were passed to the method we will only delete those |
||
776 | // associations, otherwise all of the association ties will be broken. |
||
777 | // We'll return the numbers of affected rows when we do the deletes. |
||
778 | $ids = (array) $ids; |
||
779 | |||
780 | if (count($ids) > 0) { |
||
781 | $query->whereIn($this->otherKey, (array) $ids); |
||
782 | } |
||
783 | |||
784 | // Once we have all of the conditions set on the statement, we are ready |
||
785 | // to run the delete on the pivot table. Then, if the touch parameter |
||
786 | // is true, we will go ahead and touch all related models to sync. |
||
787 | return $query->delete(); |
||
788 | } |
||
789 | |||
790 | /** |
||
791 | * Create a new query builder for the pivot table. |
||
792 | * |
||
793 | * @throws \InvalidArgumentException |
||
794 | * |
||
795 | * @return \Illuminate\Database\Query\Builder |
||
796 | */ |
||
797 | protected function newPivotQuery() |
||
798 | { |
||
799 | $query = $this->newPivotStatement(); |
||
800 | |||
801 | $parentKey = $this->parentMap->getKeyName(); |
||
802 | |||
803 | return $query->where($this->foreignKey, $this->parent->getEntityAttribute($parentKey)); |
||
804 | } |
||
805 | |||
806 | /** |
||
807 | * Get a new plain query builder for the pivot table. |
||
808 | * |
||
809 | * @return \Illuminate\Database\Query\Builder |
||
810 | */ |
||
811 | public function newPivotStatement() |
||
812 | { |
||
813 | return $this->query->getQuery()->newQuery()->from($this->table); |
||
814 | } |
||
815 | |||
816 | /** |
||
817 | * Get a new pivot statement for a given "other" ID. |
||
818 | * |
||
819 | * @param mixed $id |
||
820 | * |
||
821 | * @throws \InvalidArgumentException |
||
822 | * |
||
823 | * @return \Illuminate\Database\Query\Builder |
||
824 | */ |
||
825 | public function newPivotStatementForId($id) |
||
826 | { |
||
827 | $pivot = $this->newPivotStatement(); |
||
828 | |||
829 | $parentKeyName = $this->parentMap->getKeyName(); |
||
830 | |||
831 | $key = $this->parent->getEntityAttribute($parentKeyName); |
||
832 | |||
833 | return $pivot->where($this->foreignKey, $key)->where($this->otherKey, $id); |
||
834 | } |
||
835 | |||
836 | /** |
||
837 | * Create a new pivot model instance. |
||
838 | * |
||
839 | * @param array $attributes |
||
840 | * @param bool $exists |
||
841 | * @return \Analogue\ORM\Relationships\Pivot |
||
842 | */ |
||
843 | public function newPivot(array $attributes = [], $exists = false) |
||
844 | { |
||
845 | $pivot = new Pivot($this->parent, $this->parentMap, $attributes, $this->table, $exists); |
||
846 | |||
847 | return $pivot->setPivotKeys($this->foreignKey, $this->otherKey); |
||
848 | } |
||
849 | |||
850 | /** |
||
851 | * Create a new existing pivot model instance. |
||
852 | * |
||
853 | * @param array $attributes |
||
854 | * @return \Analogue\ORM\Relationships\Pivot |
||
855 | */ |
||
856 | public function newExistingPivot(array $attributes = []) |
||
857 | { |
||
858 | return $this->newPivot($attributes, true); |
||
859 | } |
||
860 | |||
861 | /** |
||
862 | * Set the columns on the pivot table to retrieve. |
||
863 | * |
||
864 | * @param array $columns |
||
865 | * @return $this |
||
866 | */ |
||
867 | public function withPivot($columns) |
||
868 | { |
||
869 | $columns = is_array($columns) ? $columns : func_get_args(); |
||
870 | |||
871 | $this->pivotColumns = array_merge($this->pivotColumns, $columns); |
||
872 | |||
873 | return $this; |
||
874 | } |
||
875 | |||
876 | /** |
||
877 | * Specify that the pivot table has creation and update timestamps. |
||
878 | * |
||
879 | * @param mixed $createdAt |
||
880 | * @param mixed $updatedAt |
||
881 | * @return \Analogue\ORM\Relationships\BelongsToMany |
||
882 | */ |
||
883 | public function withTimestamps($createdAt = null, $updatedAt = null) |
||
884 | { |
||
885 | return $this->withPivot($createdAt ?: $this->createdAt(), $updatedAt ?: $this->updatedAt()); |
||
886 | } |
||
887 | |||
888 | /** |
||
889 | * Get the key for comparing against the parent key in "has" query. |
||
890 | * |
||
891 | * @return string |
||
892 | */ |
||
893 | public function getHasCompareKey() |
||
894 | { |
||
895 | return $this->getForeignKey(); |
||
896 | } |
||
897 | |||
898 | /** |
||
899 | * Get the fully qualified foreign key for the relation. |
||
900 | * |
||
901 | * @return string |
||
902 | */ |
||
903 | public function getForeignKey() |
||
904 | { |
||
905 | return $this->table . '.' . $this->foreignKey; |
||
906 | } |
||
907 | |||
908 | /** |
||
909 | * Get the fully qualified "other key" for the relation. |
||
910 | * |
||
911 | * @return string |
||
912 | */ |
||
913 | public function getOtherKey() |
||
914 | { |
||
915 | return $this->table . '.' . $this->otherKey; |
||
916 | } |
||
917 | |||
918 | /** |
||
919 | * Get the fully qualified parent key name. |
||
920 | * |
||
921 | * @return string |
||
922 | */ |
||
923 | protected function getQualifiedParentKeyName() |
||
924 | { |
||
925 | return $this->parentMap->getQualifiedKeyName(); |
||
926 | } |
||
927 | |||
928 | /** |
||
929 | * Get the intermediate table for the relationship. |
||
930 | * |
||
931 | * @return string |
||
932 | */ |
||
933 | public function getTable() |
||
934 | { |
||
935 | return $this->table; |
||
936 | } |
||
937 | |||
938 | /** |
||
939 | * Get the relationship name for the relationship. |
||
940 | * |
||
941 | * @return string |
||
942 | */ |
||
943 | public function getRelationName() |
||
944 | { |
||
945 | return $this->relationName; |
||
946 | } |
||
947 | } |
||
948 |
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: