Subversion-Projekte lars-tiefland.laravel_shop

Revision

Revision 148 | Details | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed

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