Subversion-Projekte lars-tiefland.laravel_shop

Revision

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