Passed
Push — develop ( 30cf64...589229 )
by Guillaume
06:18 queued 04:10
created

HasRelationships   F

Complexity

Total Complexity 87

Size/Duplication

Total Lines 846
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 152
dl 0
loc 846
rs 2
c 0
b 0
f 0
wmc 87

How to fix   Complexity   

Complex Class

Complex classes like HasRelationships often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use HasRelationships, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Illuminate\Database\Eloquent\Concerns;
4
5
use Closure;
6
use Illuminate\Database\Eloquent\Builder;
7
use Illuminate\Database\Eloquent\Collection;
8
use Illuminate\Database\Eloquent\Model;
9
use Illuminate\Database\Eloquent\Relations\BelongsTo;
10
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
11
use Illuminate\Database\Eloquent\Relations\HasMany;
12
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
13
use Illuminate\Database\Eloquent\Relations\HasOne;
14
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
15
use Illuminate\Database\Eloquent\Relations\MorphMany;
16
use Illuminate\Database\Eloquent\Relations\MorphOne;
17
use Illuminate\Database\Eloquent\Relations\MorphTo;
18
use Illuminate\Database\Eloquent\Relations\MorphToMany;
19
use Illuminate\Database\Eloquent\Relations\Relation;
20
use Illuminate\Support\Arr;
21
use Illuminate\Support\Str;
22
23
trait HasRelationships
24
{
25
    /**
26
     * The loaded relationships for the model.
27
     *
28
     * @var array
29
     */
30
    protected $relations = [];
31
32
    /**
33
     * The relationships that should be touched on save.
34
     *
35
     * @var array
36
     */
37
    protected $touches = [];
38
39
    /**
40
     * The many to many relationship methods.
41
     *
42
     * @var array
43
     */
44
    public static $manyMethods = [
45
        'belongsToMany', 'morphToMany', 'morphedByMany',
46
    ];
47
48
    /**
49
     * The relation resolver callbacks.
50
     *
51
     * @var array
52
     */
53
    protected static $relationResolvers = [];
54
55
    /**
56
     * Define a dynamic relation resolver.
57
     *
58
     * @param  string  $name
59
     * @param  \Closure  $callback
60
     * @return void
61
     */
62
    public static function resolveRelationUsing($name, Closure $callback)
63
    {
64
        static::$relationResolvers = array_replace_recursive(
65
            static::$relationResolvers,
66
            [static::class => [$name => $callback]]
67
        );
68
    }
69
70
    /**
71
     * Define a one-to-one relationship.
72
     *
73
     * @param  string  $related
74
     * @param  string|null  $foreignKey
75
     * @param  string|null  $localKey
76
     * @return \Illuminate\Database\Eloquent\Relations\HasOne
77
     */
78
    public function hasOne($related, $foreignKey = null, $localKey = null)
79
    {
80
        $instance = $this->newRelatedInstance($related);
81
82
        $foreignKey = $foreignKey ?: $this->getForeignKey();
83
84
        $localKey = $localKey ?: $this->getKeyName();
85
86
        return $this->newHasOne($instance->newQuery(), $this, $instance->getTable().'.'.$foreignKey, $localKey);
87
    }
88
89
    /**
90
     * Instantiate a new HasOne relationship.
91
     *
92
     * @param  \Illuminate\Database\Eloquent\Builder  $query
93
     * @param  \Illuminate\Database\Eloquent\Model  $parent
94
     * @param  string  $foreignKey
95
     * @param  string  $localKey
96
     * @return \Illuminate\Database\Eloquent\Relations\HasOne
97
     */
98
    protected function newHasOne(Builder $query, Model $parent, $foreignKey, $localKey)
99
    {
100
        return new HasOne($query, $parent, $foreignKey, $localKey);
101
    }
102
103
    /**
104
     * Define a has-one-through relationship.
105
     *
106
     * @param  string  $related
107
     * @param  string  $through
108
     * @param  string|null  $firstKey
109
     * @param  string|null  $secondKey
110
     * @param  string|null  $localKey
111
     * @param  string|null  $secondLocalKey
112
     * @return \Illuminate\Database\Eloquent\Relations\HasOneThrough
113
     */
114
    public function hasOneThrough($related, $through, $firstKey = null, $secondKey = null, $localKey = null, $secondLocalKey = null)
115
    {
116
        $through = new $through;
117
118
        $firstKey = $firstKey ?: $this->getForeignKey();
119
120
        $secondKey = $secondKey ?: $through->getForeignKey();
121
122
        return $this->newHasOneThrough(
123
            $this->newRelatedInstance($related)->newQuery(), $this, $through,
124
            $firstKey, $secondKey, $localKey ?: $this->getKeyName(),
125
            $secondLocalKey ?: $through->getKeyName()
126
        );
127
    }
128
129
    /**
130
     * Instantiate a new HasOneThrough relationship.
131
     *
132
     * @param  \Illuminate\Database\Eloquent\Builder  $query
133
     * @param  \Illuminate\Database\Eloquent\Model  $farParent
134
     * @param  \Illuminate\Database\Eloquent\Model  $throughParent
135
     * @param  string  $firstKey
136
     * @param  string  $secondKey
137
     * @param  string  $localKey
138
     * @param  string  $secondLocalKey
139
     * @return \Illuminate\Database\Eloquent\Relations\HasOneThrough
140
     */
141
    protected function newHasOneThrough(Builder $query, Model $farParent, Model $throughParent, $firstKey, $secondKey, $localKey, $secondLocalKey)
142
    {
143
        return new HasOneThrough($query, $farParent, $throughParent, $firstKey, $secondKey, $localKey, $secondLocalKey);
144
    }
145
146
    /**
147
     * Define a polymorphic one-to-one relationship.
148
     *
149
     * @param  string  $related
150
     * @param  string  $name
151
     * @param  string|null  $type
152
     * @param  string|null  $id
153
     * @param  string|null  $localKey
154
     * @return \Illuminate\Database\Eloquent\Relations\MorphOne
155
     */
156
    public function morphOne($related, $name, $type = null, $id = null, $localKey = null)
157
    {
158
        $instance = $this->newRelatedInstance($related);
159
160
        [$type, $id] = $this->getMorphs($name, $type, $id);
161
162
        $table = $instance->getTable();
163
164
        $localKey = $localKey ?: $this->getKeyName();
165
166
        return $this->newMorphOne($instance->newQuery(), $this, $table.'.'.$type, $table.'.'.$id, $localKey);
167
    }
168
169
    /**
170
     * Instantiate a new MorphOne relationship.
171
     *
172
     * @param  \Illuminate\Database\Eloquent\Builder  $query
173
     * @param  \Illuminate\Database\Eloquent\Model  $parent
174
     * @param  string  $type
175
     * @param  string  $id
176
     * @param  string  $localKey
177
     * @return \Illuminate\Database\Eloquent\Relations\MorphOne
178
     */
179
    protected function newMorphOne(Builder $query, Model $parent, $type, $id, $localKey)
180
    {
181
        return new MorphOne($query, $parent, $type, $id, $localKey);
182
    }
183
184
    /**
185
     * Define an inverse one-to-one or many relationship.
186
     *
187
     * @param  string  $related
188
     * @param  string|null  $foreignKey
189
     * @param  string|null  $ownerKey
190
     * @param  string|null  $relation
191
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
192
     */
193
    public function belongsTo($related, $foreignKey = null, $ownerKey = null, $relation = null)
194
    {
195
        // If no relation name was given, we will use this debug backtrace to extract
196
        // the calling method's name and use that as the relationship name as most
197
        // of the time this will be what we desire to use for the relationships.
198
        if (is_null($relation)) {
199
            $relation = $this->guessBelongsToRelation();
200
        }
201
202
        $instance = $this->newRelatedInstance($related);
203
204
        // If no foreign key was supplied, we can use a backtrace to guess the proper
205
        // foreign key name by using the name of the relationship function, which
206
        // when combined with an "_id" should conventionally match the columns.
207
        if (is_null($foreignKey)) {
208
            $foreignKey = Str::snake($relation).'_'.$instance->getKeyName();
209
        }
210
211
        // Once we have the foreign key names, we'll just create a new Eloquent query
212
        // for the related models and returns the relationship instance which will
213
        // actually be responsible for retrieving and hydrating every relations.
214
        $ownerKey = $ownerKey ?: $instance->getKeyName();
215
216
        return $this->newBelongsTo(
217
            $instance->newQuery(), $this, $foreignKey, $ownerKey, $relation
218
        );
219
    }
220
221
    /**
222
     * Instantiate a new BelongsTo relationship.
223
     *
224
     * @param  \Illuminate\Database\Eloquent\Builder  $query
225
     * @param  \Illuminate\Database\Eloquent\Model  $child
226
     * @param  string  $foreignKey
227
     * @param  string  $ownerKey
228
     * @param  string  $relation
229
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
230
     */
231
    protected function newBelongsTo(Builder $query, Model $child, $foreignKey, $ownerKey, $relation)
232
    {
233
        return new BelongsTo($query, $child, $foreignKey, $ownerKey, $relation);
234
    }
235
236
    /**
237
     * Define a polymorphic, inverse one-to-one or many relationship.
238
     *
239
     * @param  string|null  $name
240
     * @param  string|null  $type
241
     * @param  string|null  $id
242
     * @param  string|null  $ownerKey
243
     * @return \Illuminate\Database\Eloquent\Relations\MorphTo
244
     */
245
    public function morphTo($name = null, $type = null, $id = null, $ownerKey = null)
246
    {
247
        // If no name is provided, we will use the backtrace to get the function name
248
        // since that is most likely the name of the polymorphic interface. We can
249
        // use that to get both the class and foreign key that will be utilized.
250
        $name = $name ?: $this->guessBelongsToRelation();
251
252
        [$type, $id] = $this->getMorphs(
253
            Str::snake($name), $type, $id
254
        );
255
256
        // If the type value is null it is probably safe to assume we're eager loading
257
        // the relationship. In this case we'll just pass in a dummy query where we
258
        // need to remove any eager loads that may already be defined on a model.
259
        return empty($class = $this->{$type})
260
                    ? $this->morphEagerTo($name, $type, $id, $ownerKey)
261
                    : $this->morphInstanceTo($class, $name, $type, $id, $ownerKey);
262
    }
263
264
    /**
265
     * Define a polymorphic, inverse one-to-one or many relationship.
266
     *
267
     * @param  string  $name
268
     * @param  string  $type
269
     * @param  string  $id
270
     * @param  string  $ownerKey
271
     * @return \Illuminate\Database\Eloquent\Relations\MorphTo
272
     */
273
    protected function morphEagerTo($name, $type, $id, $ownerKey)
274
    {
275
        return $this->newMorphTo(
276
            $this->newQuery()->setEagerLoads([]), $this, $id, $ownerKey, $type, $name
277
        );
278
    }
279
280
    /**
281
     * Define a polymorphic, inverse one-to-one or many relationship.
282
     *
283
     * @param  string  $target
284
     * @param  string  $name
285
     * @param  string  $type
286
     * @param  string  $id
287
     * @param  string  $ownerKey
288
     * @return \Illuminate\Database\Eloquent\Relations\MorphTo
289
     */
290
    protected function morphInstanceTo($target, $name, $type, $id, $ownerKey)
291
    {
292
        $instance = $this->newRelatedInstance(
293
            static::getActualClassNameForMorph($target)
294
        );
295
296
        return $this->newMorphTo(
297
            $instance->newQuery(), $this, $id, $ownerKey ?? $instance->getKeyName(), $type, $name
298
        );
299
    }
300
301
    /**
302
     * Instantiate a new MorphTo relationship.
303
     *
304
     * @param  \Illuminate\Database\Eloquent\Builder  $query
305
     * @param  \Illuminate\Database\Eloquent\Model  $parent
306
     * @param  string  $foreignKey
307
     * @param  string  $ownerKey
308
     * @param  string  $type
309
     * @param  string  $relation
310
     * @return \Illuminate\Database\Eloquent\Relations\MorphTo
311
     */
312
    protected function newMorphTo(Builder $query, Model $parent, $foreignKey, $ownerKey, $type, $relation)
313
    {
314
        return new MorphTo($query, $parent, $foreignKey, $ownerKey, $type, $relation);
315
    }
316
317
    /**
318
     * Retrieve the actual class name for a given morph class.
319
     *
320
     * @param  string  $class
321
     * @return string
322
     */
323
    public static function getActualClassNameForMorph($class)
324
    {
325
        return Arr::get(Relation::morphMap() ?: [], $class, $class);
326
    }
327
328
    /**
329
     * Guess the "belongs to" relationship name.
330
     *
331
     * @return string
332
     */
333
    protected function guessBelongsToRelation()
334
    {
335
        [$one, $two, $caller] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
336
337
        return $caller['function'];
338
    }
339
340
    /**
341
     * Define a one-to-many relationship.
342
     *
343
     * @param  string  $related
344
     * @param  string|null  $foreignKey
345
     * @param  string|null  $localKey
346
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
347
     */
348
    public function hasMany($related, $foreignKey = null, $localKey = null)
349
    {
350
        $instance = $this->newRelatedInstance($related);
351
352
        $foreignKey = $foreignKey ?: $this->getForeignKey();
353
354
        $localKey = $localKey ?: $this->getKeyName();
355
356
        return $this->newHasMany(
357
            $instance->newQuery(), $this, $instance->getTable().'.'.$foreignKey, $localKey
358
        );
359
    }
360
361
    /**
362
     * Instantiate a new HasMany relationship.
363
     *
364
     * @param  \Illuminate\Database\Eloquent\Builder  $query
365
     * @param  \Illuminate\Database\Eloquent\Model  $parent
366
     * @param  string  $foreignKey
367
     * @param  string  $localKey
368
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
369
     */
370
    protected function newHasMany(Builder $query, Model $parent, $foreignKey, $localKey)
371
    {
372
        return new HasMany($query, $parent, $foreignKey, $localKey);
373
    }
374
375
    /**
376
     * Define a has-many-through relationship.
377
     *
378
     * @param  string  $related
379
     * @param  string  $through
380
     * @param  string|null  $firstKey
381
     * @param  string|null  $secondKey
382
     * @param  string|null  $localKey
383
     * @param  string|null  $secondLocalKey
384
     * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
385
     */
386
    public function hasManyThrough($related, $through, $firstKey = null, $secondKey = null, $localKey = null, $secondLocalKey = null)
387
    {
388
        $through = new $through;
389
390
        $firstKey = $firstKey ?: $this->getForeignKey();
391
392
        $secondKey = $secondKey ?: $through->getForeignKey();
393
394
        return $this->newHasManyThrough(
395
            $this->newRelatedInstance($related)->newQuery(),
396
            $this,
397
            $through,
398
            $firstKey,
399
            $secondKey,
400
            $localKey ?: $this->getKeyName(),
401
            $secondLocalKey ?: $through->getKeyName()
402
        );
403
    }
404
405
    /**
406
     * Instantiate a new HasManyThrough relationship.
407
     *
408
     * @param  \Illuminate\Database\Eloquent\Builder  $query
409
     * @param  \Illuminate\Database\Eloquent\Model  $farParent
410
     * @param  \Illuminate\Database\Eloquent\Model  $throughParent
411
     * @param  string  $firstKey
412
     * @param  string  $secondKey
413
     * @param  string  $localKey
414
     * @param  string  $secondLocalKey
415
     * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
416
     */
417
    protected function newHasManyThrough(Builder $query, Model $farParent, Model $throughParent, $firstKey, $secondKey, $localKey, $secondLocalKey)
418
    {
419
        return new HasManyThrough($query, $farParent, $throughParent, $firstKey, $secondKey, $localKey, $secondLocalKey);
420
    }
421
422
    /**
423
     * Define a polymorphic one-to-many relationship.
424
     *
425
     * @param  string  $related
426
     * @param  string  $name
427
     * @param  string|null  $type
428
     * @param  string|null  $id
429
     * @param  string|null  $localKey
430
     * @return \Illuminate\Database\Eloquent\Relations\MorphMany
431
     */
432
    public function morphMany($related, $name, $type = null, $id = null, $localKey = null)
433
    {
434
        $instance = $this->newRelatedInstance($related);
435
436
        // Here we will gather up the morph type and ID for the relationship so that we
437
        // can properly query the intermediate table of a relation. Finally, we will
438
        // get the table and create the relationship instances for the developers.
439
        [$type, $id] = $this->getMorphs($name, $type, $id);
440
441
        $table = $instance->getTable();
442
443
        $localKey = $localKey ?: $this->getKeyName();
444
445
        return $this->newMorphMany($instance->newQuery(), $this, $table.'.'.$type, $table.'.'.$id, $localKey);
446
    }
447
448
    /**
449
     * Instantiate a new MorphMany relationship.
450
     *
451
     * @param  \Illuminate\Database\Eloquent\Builder  $query
452
     * @param  \Illuminate\Database\Eloquent\Model  $parent
453
     * @param  string  $type
454
     * @param  string  $id
455
     * @param  string  $localKey
456
     * @return \Illuminate\Database\Eloquent\Relations\MorphMany
457
     */
458
    protected function newMorphMany(Builder $query, Model $parent, $type, $id, $localKey)
459
    {
460
        return new MorphMany($query, $parent, $type, $id, $localKey);
461
    }
462
463
    /**
464
     * Define a many-to-many relationship.
465
     *
466
     * @param  string  $related
467
     * @param  string|null  $table
468
     * @param  string|null  $foreignPivotKey
469
     * @param  string|null  $relatedPivotKey
470
     * @param  string|null  $parentKey
471
     * @param  string|null  $relatedKey
472
     * @param  string|null  $relation
473
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
474
     */
475
    public function belongsToMany($related, $table = null, $foreignPivotKey = null, $relatedPivotKey = null,
476
                                  $parentKey = null, $relatedKey = null, $relation = null)
477
    {
478
        // If no relationship name was passed, we will pull backtraces to get the
479
        // name of the calling function. We will use that function name as the
480
        // title of this relation since that is a great convention to apply.
481
        if (is_null($relation)) {
482
            $relation = $this->guessBelongsToManyRelation();
483
        }
484
485
        // First, we'll need to determine the foreign key and "other key" for the
486
        // relationship. Once we have determined the keys we'll make the query
487
        // instances as well as the relationship instances we need for this.
488
        $instance = $this->newRelatedInstance($related);
489
490
        $foreignPivotKey = $foreignPivotKey ?: $this->getForeignKey();
491
492
        $relatedPivotKey = $relatedPivotKey ?: $instance->getForeignKey();
493
494
        // If no table name was provided, we can guess it by concatenating the two
495
        // models using underscores in alphabetical order. The two model names
496
        // are transformed to snake case from their default CamelCase also.
497
        if (is_null($table)) {
498
            $table = $this->joiningTable($related, $instance);
499
        }
500
501
        return $this->newBelongsToMany(
502
            $instance->newQuery(), $this, $table, $foreignPivotKey,
503
            $relatedPivotKey, $parentKey ?: $this->getKeyName(),
504
            $relatedKey ?: $instance->getKeyName(), $relation
505
        );
506
    }
507
508
    /**
509
     * Instantiate a new BelongsToMany relationship.
510
     *
511
     * @param  \Illuminate\Database\Eloquent\Builder  $query
512
     * @param  \Illuminate\Database\Eloquent\Model  $parent
513
     * @param  string  $table
514
     * @param  string  $foreignPivotKey
515
     * @param  string  $relatedPivotKey
516
     * @param  string  $parentKey
517
     * @param  string  $relatedKey
518
     * @param  string|null  $relationName
519
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
520
     */
521
    protected function newBelongsToMany(Builder $query, Model $parent, $table, $foreignPivotKey, $relatedPivotKey,
522
                                        $parentKey, $relatedKey, $relationName = null)
523
    {
524
        return new BelongsToMany($query, $parent, $table, $foreignPivotKey, $relatedPivotKey, $parentKey, $relatedKey, $relationName);
525
    }
526
527
    /**
528
     * Define a polymorphic many-to-many relationship.
529
     *
530
     * @param  string  $related
531
     * @param  string  $name
532
     * @param  string|null  $table
533
     * @param  string|null  $foreignPivotKey
534
     * @param  string|null  $relatedPivotKey
535
     * @param  string|null  $parentKey
536
     * @param  string|null  $relatedKey
537
     * @param  bool  $inverse
538
     * @return \Illuminate\Database\Eloquent\Relations\MorphToMany
539
     */
540
    public function morphToMany($related, $name, $table = null, $foreignPivotKey = null,
541
                                $relatedPivotKey = null, $parentKey = null,
542
                                $relatedKey = null, $inverse = false)
543
    {
544
        $caller = $this->guessBelongsToManyRelation();
545
546
        // First, we will need to determine the foreign key and "other key" for the
547
        // relationship. Once we have determined the keys we will make the query
548
        // instances, as well as the relationship instances we need for these.
549
        $instance = $this->newRelatedInstance($related);
550
551
        $foreignPivotKey = $foreignPivotKey ?: $name.'_id';
552
553
        $relatedPivotKey = $relatedPivotKey ?: $instance->getForeignKey();
554
555
        // Now we're ready to create a new query builder for this related model and
556
        // the relationship instances for this relation. This relations will set
557
        // appropriate query constraints then entirely manages the hydrations.
558
        if (! $table) {
559
            $words = preg_split('/(_)/u', $name, -1, PREG_SPLIT_DELIM_CAPTURE);
560
561
            $lastWord = array_pop($words);
562
563
            $table = implode('', $words).Str::plural($lastWord);
564
        }
565
566
        return $this->newMorphToMany(
567
            $instance->newQuery(), $this, $name, $table,
568
            $foreignPivotKey, $relatedPivotKey, $parentKey ?: $this->getKeyName(),
569
            $relatedKey ?: $instance->getKeyName(), $caller, $inverse
570
        );
571
    }
572
573
    /**
574
     * Instantiate a new MorphToMany relationship.
575
     *
576
     * @param  \Illuminate\Database\Eloquent\Builder  $query
577
     * @param  \Illuminate\Database\Eloquent\Model  $parent
578
     * @param  string  $name
579
     * @param  string  $table
580
     * @param  string  $foreignPivotKey
581
     * @param  string  $relatedPivotKey
582
     * @param  string  $parentKey
583
     * @param  string  $relatedKey
584
     * @param  string|null  $relationName
585
     * @param  bool  $inverse
586
     * @return \Illuminate\Database\Eloquent\Relations\MorphToMany
587
     */
588
    protected function newMorphToMany(Builder $query, Model $parent, $name, $table, $foreignPivotKey,
589
                                      $relatedPivotKey, $parentKey, $relatedKey,
590
                                      $relationName = null, $inverse = false)
591
    {
592
        return new MorphToMany($query, $parent, $name, $table, $foreignPivotKey, $relatedPivotKey, $parentKey, $relatedKey,
593
            $relationName, $inverse);
594
    }
595
596
    /**
597
     * Define a polymorphic, inverse many-to-many relationship.
598
     *
599
     * @param  string  $related
600
     * @param  string  $name
601
     * @param  string|null  $table
602
     * @param  string|null  $foreignPivotKey
603
     * @param  string|null  $relatedPivotKey
604
     * @param  string|null  $parentKey
605
     * @param  string|null  $relatedKey
606
     * @return \Illuminate\Database\Eloquent\Relations\MorphToMany
607
     */
608
    public function morphedByMany($related, $name, $table = null, $foreignPivotKey = null,
609
                                  $relatedPivotKey = null, $parentKey = null, $relatedKey = null)
610
    {
611
        $foreignPivotKey = $foreignPivotKey ?: $this->getForeignKey();
612
613
        // For the inverse of the polymorphic many-to-many relations, we will change
614
        // the way we determine the foreign and other keys, as it is the opposite
615
        // of the morph-to-many method since we're figuring out these inverses.
616
        $relatedPivotKey = $relatedPivotKey ?: $name.'_id';
617
618
        return $this->morphToMany(
619
            $related, $name, $table, $foreignPivotKey,
620
            $relatedPivotKey, $parentKey, $relatedKey, true
621
        );
622
    }
623
624
    /**
625
     * Get the relationship name of the belongsToMany relationship.
626
     *
627
     * @return string|null
628
     */
629
    protected function guessBelongsToManyRelation()
630
    {
631
        $caller = Arr::first(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), function ($trace) {
632
            return ! in_array(
633
                $trace['function'],
634
                array_merge(static::$manyMethods, ['guessBelongsToManyRelation'])
635
            );
636
        });
637
638
        return ! is_null($caller) ? $caller['function'] : null;
639
    }
640
641
    /**
642
     * Get the joining table name for a many-to-many relation.
643
     *
644
     * @param  string  $related
645
     * @param  \Illuminate\Database\Eloquent\Model|null  $instance
646
     * @return string
647
     */
648
    public function joiningTable($related, $instance = null)
649
    {
650
        // The joining table name, by convention, is simply the snake cased models
651
        // sorted alphabetically and concatenated with an underscore, so we can
652
        // just sort the models and join them together to get the table name.
653
        $segments = [
654
            $instance ? $instance->joiningTableSegment()
655
                      : Str::snake(class_basename($related)),
656
            $this->joiningTableSegment(),
657
        ];
658
659
        // Now that we have the model names in an array we can just sort them and
660
        // use the implode function to join them together with an underscores,
661
        // which is typically used by convention within the database system.
662
        sort($segments);
663
664
        return strtolower(implode('_', $segments));
665
    }
666
667
    /**
668
     * Get this model's half of the intermediate table name for belongsToMany relationships.
669
     *
670
     * @return string
671
     */
672
    public function joiningTableSegment()
673
    {
674
        return Str::snake(class_basename($this));
675
    }
676
677
    /**
678
     * Determine if the model touches a given relation.
679
     *
680
     * @param  string  $relation
681
     * @return bool
682
     */
683
    public function touches($relation)
684
    {
685
        return in_array($relation, $this->touches);
686
    }
687
688
    /**
689
     * Touch the owning relations of the model.
690
     *
691
     * @return void
692
     */
693
    public function touchOwners()
694
    {
695
        foreach ($this->touches as $relation) {
696
            $this->$relation()->touch();
697
698
            if ($this->$relation instanceof self) {
699
                $this->$relation->fireModelEvent('saved', false);
700
701
                $this->$relation->touchOwners();
702
            } elseif ($this->$relation instanceof Collection) {
703
                $this->$relation->each->touchOwners();
704
            }
705
        }
706
    }
707
708
    /**
709
     * Get the polymorphic relationship columns.
710
     *
711
     * @param  string  $name
712
     * @param  string  $type
713
     * @param  string  $id
714
     * @return array
715
     */
716
    protected function getMorphs($name, $type, $id)
717
    {
718
        return [$type ?: $name.'_type', $id ?: $name.'_id'];
719
    }
720
721
    /**
722
     * Get the class name for polymorphic relations.
723
     *
724
     * @return string
725
     */
726
    public function getMorphClass()
727
    {
728
        $morphMap = Relation::morphMap();
729
730
        if (! empty($morphMap) && in_array(static::class, $morphMap)) {
731
            return array_search(static::class, $morphMap, true);
732
        }
733
734
        return static::class;
735
    }
736
737
    /**
738
     * Create a new model instance for a related model.
739
     *
740
     * @param  string  $class
741
     * @return mixed
742
     */
743
    protected function newRelatedInstance($class)
744
    {
745
        return tap(new $class, function ($instance) {
746
            if (! $instance->getConnectionName()) {
747
                $instance->setConnection($this->connection);
748
            }
749
        });
750
    }
751
752
    /**
753
     * Get all the loaded relations for the instance.
754
     *
755
     * @return array
756
     */
757
    public function getRelations()
758
    {
759
        return $this->relations;
760
    }
761
762
    /**
763
     * Get a specified relationship.
764
     *
765
     * @param  string  $relation
766
     * @return mixed
767
     */
768
    public function getRelation($relation)
769
    {
770
        return $this->relations[$relation];
771
    }
772
773
    /**
774
     * Determine if the given relation is loaded.
775
     *
776
     * @param  string  $key
777
     * @return bool
778
     */
779
    public function relationLoaded($key)
780
    {
781
        return array_key_exists($key, $this->relations);
782
    }
783
784
    /**
785
     * Set the given relationship on the model.
786
     *
787
     * @param  string  $relation
788
     * @param  mixed  $value
789
     * @return $this
790
     */
791
    public function setRelation($relation, $value)
792
    {
793
        $this->relations[$relation] = $value;
794
795
        return $this;
796
    }
797
798
    /**
799
     * Unset a loaded relationship.
800
     *
801
     * @param  string  $relation
802
     * @return $this
803
     */
804
    public function unsetRelation($relation)
805
    {
806
        unset($this->relations[$relation]);
807
808
        return $this;
809
    }
810
811
    /**
812
     * Set the entire relations array on the model.
813
     *
814
     * @param  array  $relations
815
     * @return $this
816
     */
817
    public function setRelations(array $relations)
818
    {
819
        $this->relations = $relations;
820
821
        return $this;
822
    }
823
824
    /**
825
     * Duplicate the instance and unset all the loaded relations.
826
     *
827
     * @return $this
828
     */
829
    public function withoutRelations()
830
    {
831
        $model = clone $this;
832
833
        return $model->unsetRelations();
834
    }
835
836
    /**
837
     * Unset all the loaded relations for the instance.
838
     *
839
     * @return $this
840
     */
841
    public function unsetRelations()
842
    {
843
        $this->relations = [];
844
845
        return $this;
846
    }
847
848
    /**
849
     * Get the relationships that are touched on save.
850
     *
851
     * @return array
852
     */
853
    public function getTouchedRelations()
854
    {
855
        return $this->touches;
856
    }
857
858
    /**
859
     * Set the relationships that are touched on save.
860
     *
861
     * @param  array  $touches
862
     * @return $this
863
     */
864
    public function setTouchedRelations(array $touches)
865
    {
866
        $this->touches = $touches;
867
868
        return $this;
869
    }
870
}
871