Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
namespace Illuminate\Database\Eloquent\Relations;
4
 
5
use Closure;
6
use Illuminate\Contracts\Database\Eloquent\Builder as BuilderContract;
7
use Illuminate\Database\Eloquent\Builder;
8
use Illuminate\Database\Eloquent\Collection;
9
use Illuminate\Database\Eloquent\Model;
10
use Illuminate\Database\Eloquent\ModelNotFoundException;
11
use Illuminate\Database\MultipleRecordsFoundException;
12
use Illuminate\Database\Query\Expression;
13
use Illuminate\Support\Arr;
14
use Illuminate\Support\Traits\ForwardsCalls;
15
use Illuminate\Support\Traits\Macroable;
16
 
17
abstract class Relation implements BuilderContract
18
{
19
    use ForwardsCalls, Macroable {
20
        Macroable::__call as macroCall;
21
    }
22
 
23
    /**
24
     * The Eloquent query builder instance.
25
     *
26
     * @var \Illuminate\Database\Eloquent\Builder
27
     */
28
    protected $query;
29
 
30
    /**
31
     * The parent model instance.
32
     *
33
     * @var \Illuminate\Database\Eloquent\Model
34
     */
35
    protected $parent;
36
 
37
    /**
38
     * The related model instance.
39
     *
40
     * @var \Illuminate\Database\Eloquent\Model
41
     */
42
    protected $related;
43
 
44
    /**
45
     * Indicates if the relation is adding constraints.
46
     *
47
     * @var bool
48
     */
49
    protected static $constraints = true;
50
 
51
    /**
52
     * An array to map class names to their morph names in the database.
53
     *
54
     * @var array
55
     */
56
    public static $morphMap = [];
57
 
58
    /**
59
     * Prevents morph relationships without a morph map.
60
     *
61
     * @var bool
62
     */
63
    protected static $requireMorphMap = false;
64
 
65
    /**
66
     * The count of self joins.
67
     *
68
     * @var int
69
     */
70
    protected static $selfJoinCount = 0;
71
 
72
    /**
73
     * Create a new relation instance.
74
     *
75
     * @param  \Illuminate\Database\Eloquent\Builder  $query
76
     * @param  \Illuminate\Database\Eloquent\Model  $parent
77
     * @return void
78
     */
79
    public function __construct(Builder $query, Model $parent)
80
    {
81
        $this->query = $query;
82
        $this->parent = $parent;
83
        $this->related = $query->getModel();
84
 
85
        $this->addConstraints();
86
    }
87
 
88
    /**
89
     * Run a callback with constraints disabled on the relation.
90
     *
91
     * @param  \Closure  $callback
92
     * @return mixed
93
     */
94
    public static function noConstraints(Closure $callback)
95
    {
96
        $previous = static::$constraints;
97
 
98
        static::$constraints = false;
99
 
100
        // When resetting the relation where clause, we want to shift the first element
101
        // off of the bindings, leaving only the constraints that the developers put
102
        // as "extra" on the relationships, and not original relation constraints.
103
        try {
104
            return $callback();
105
        } finally {
106
            static::$constraints = $previous;
107
        }
108
    }
109
 
110
    /**
111
     * Set the base constraints on the relation query.
112
     *
113
     * @return void
114
     */
115
    abstract public function addConstraints();
116
 
117
    /**
118
     * Set the constraints for an eager load of the relation.
119
     *
120
     * @param  array  $models
121
     * @return void
122
     */
123
    abstract public function addEagerConstraints(array $models);
124
 
125
    /**
126
     * Initialize the relation on a set of models.
127
     *
128
     * @param  array  $models
129
     * @param  string  $relation
130
     * @return array
131
     */
132
    abstract public function initRelation(array $models, $relation);
133
 
134
    /**
135
     * Match the eagerly loaded results to their parents.
136
     *
137
     * @param  array  $models
138
     * @param  \Illuminate\Database\Eloquent\Collection  $results
139
     * @param  string  $relation
140
     * @return array
141
     */
142
    abstract public function match(array $models, Collection $results, $relation);
143
 
144
    /**
145
     * Get the results of the relationship.
146
     *
147
     * @return mixed
148
     */
149
    abstract public function getResults();
150
 
151
    /**
152
     * Get the relationship for eager loading.
153
     *
154
     * @return \Illuminate\Database\Eloquent\Collection
155
     */
156
    public function getEager()
157
    {
158
        return $this->get();
159
    }
160
 
161
    /**
162
     * Execute the query and get the first result if it's the sole matching record.
163
     *
164
     * @param  array|string  $columns
165
     * @return \Illuminate\Database\Eloquent\Model
166
     *
167
     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>
168
     * @throws \Illuminate\Database\MultipleRecordsFoundException
169
     */
170
    public function sole($columns = ['*'])
171
    {
172
        $result = $this->take(2)->get($columns);
173
 
174
        $count = $result->count();
175
 
176
        if ($count === 0) {
177
            throw (new ModelNotFoundException)->setModel(get_class($this->related));
178
        }
179
 
180
        if ($count > 1) {
181
            throw new MultipleRecordsFoundException($count);
182
        }
183
 
184
        return $result->first();
185
    }
186
 
187
    /**
188
     * Execute the query as a "select" statement.
189
     *
190
     * @param  array  $columns
191
     * @return \Illuminate\Database\Eloquent\Collection
192
     */
193
    public function get($columns = ['*'])
194
    {
195
        return $this->query->get($columns);
196
    }
197
 
198
    /**
199
     * Touch all of the related models for the relationship.
200
     *
201
     * @return void
202
     */
203
    public function touch()
204
    {
205
        $model = $this->getRelated();
206
 
207
        if (! $model::isIgnoringTouch()) {
208
            $this->rawUpdate([
209
                $model->getUpdatedAtColumn() => $model->freshTimestampString(),
210
            ]);
211
        }
212
    }
213
 
214
    /**
215
     * Run a raw update against the base query.
216
     *
217
     * @param  array  $attributes
218
     * @return int
219
     */
220
    public function rawUpdate(array $attributes = [])
221
    {
222
        return $this->query->withoutGlobalScopes()->update($attributes);
223
    }
224
 
225
    /**
226
     * Add the constraints for a relationship count query.
227
     *
228
     * @param  \Illuminate\Database\Eloquent\Builder  $query
229
     * @param  \Illuminate\Database\Eloquent\Builder  $parentQuery
230
     * @return \Illuminate\Database\Eloquent\Builder
231
     */
232
    public function getRelationExistenceCountQuery(Builder $query, Builder $parentQuery)
233
    {
234
        return $this->getRelationExistenceQuery(
235
            $query, $parentQuery, new Expression('count(*)')
236
        )->setBindings([], 'select');
237
    }
238
 
239
    /**
240
     * Add the constraints for an internal relationship existence query.
241
     *
242
     * Essentially, these queries compare on column names like whereColumn.
243
     *
244
     * @param  \Illuminate\Database\Eloquent\Builder  $query
245
     * @param  \Illuminate\Database\Eloquent\Builder  $parentQuery
246
     * @param  array|mixed  $columns
247
     * @return \Illuminate\Database\Eloquent\Builder
248
     */
249
    public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
250
    {
251
        return $query->select($columns)->whereColumn(
252
            $this->getQualifiedParentKeyName(), '=', $this->getExistenceCompareKey()
253
        );
254
    }
255
 
256
    /**
257
     * Get a relationship join table hash.
258
     *
259
     * @param  bool  $incrementJoinCount
260
     * @return string
261
     */
262
    public function getRelationCountHash($incrementJoinCount = true)
263
    {
264
        return 'laravel_reserved_'.($incrementJoinCount ? static::$selfJoinCount++ : static::$selfJoinCount);
265
    }
266
 
267
    /**
268
     * Get all of the primary keys for an array of models.
269
     *
270
     * @param  array  $models
271
     * @param  string|null  $key
272
     * @return array
273
     */
274
    protected function getKeys(array $models, $key = null)
275
    {
276
        return collect($models)->map(function ($value) use ($key) {
277
            return $key ? $value->getAttribute($key) : $value->getKey();
278
        })->values()->unique(null, true)->sort()->all();
279
    }
280
 
281
    /**
282
     * Get the query builder that will contain the relationship constraints.
283
     *
284
     * @return \Illuminate\Database\Eloquent\Builder
285
     */
286
    protected function getRelationQuery()
287
    {
288
        return $this->query;
289
    }
290
 
291
    /**
292
     * Get the underlying query for the relation.
293
     *
294
     * @return \Illuminate\Database\Eloquent\Builder
295
     */
296
    public function getQuery()
297
    {
298
        return $this->query;
299
    }
300
 
301
    /**
302
     * Get the base query builder driving the Eloquent builder.
303
     *
304
     * @return \Illuminate\Database\Query\Builder
305
     */
306
    public function getBaseQuery()
307
    {
308
        return $this->query->getQuery();
309
    }
310
 
311
    /**
312
     * Get a base query builder instance.
313
     *
314
     * @return \Illuminate\Database\Query\Builder
315
     */
316
    public function toBase()
317
    {
318
        return $this->query->toBase();
319
    }
320
 
321
    /**
322
     * Get the parent model of the relation.
323
     *
324
     * @return \Illuminate\Database\Eloquent\Model
325
     */
326
    public function getParent()
327
    {
328
        return $this->parent;
329
    }
330
 
331
    /**
332
     * Get the fully qualified parent key name.
333
     *
334
     * @return string
335
     */
336
    public function getQualifiedParentKeyName()
337
    {
338
        return $this->parent->getQualifiedKeyName();
339
    }
340
 
341
    /**
342
     * Get the related model of the relation.
343
     *
344
     * @return \Illuminate\Database\Eloquent\Model
345
     */
346
    public function getRelated()
347
    {
348
        return $this->related;
349
    }
350
 
351
    /**
352
     * Get the name of the "created at" column.
353
     *
354
     * @return string
355
     */
356
    public function createdAt()
357
    {
358
        return $this->parent->getCreatedAtColumn();
359
    }
360
 
361
    /**
362
     * Get the name of the "updated at" column.
363
     *
364
     * @return string
365
     */
366
    public function updatedAt()
367
    {
368
        return $this->parent->getUpdatedAtColumn();
369
    }
370
 
371
    /**
372
     * Get the name of the related model's "updated at" column.
373
     *
374
     * @return string
375
     */
376
    public function relatedUpdatedAt()
377
    {
378
        return $this->related->getUpdatedAtColumn();
379
    }
380
 
381
    /**
382
     * Get the name of the "where in" method for eager loading.
383
     *
384
     * @param  \Illuminate\Database\Eloquent\Model  $model
385
     * @param  string  $key
386
     * @return string
387
     */
388
    protected function whereInMethod(Model $model, $key)
389
    {
390
        return $model->getKeyName() === last(explode('.', $key))
391
                    && in_array($model->getKeyType(), ['int', 'integer'])
392
                        ? 'whereIntegerInRaw'
393
                        : 'whereIn';
394
    }
395
 
396
    /**
397
     * Prevent polymorphic relationships from being used without model mappings.
398
     *
399
     * @param  bool  $requireMorphMap
400
     * @return void
401
     */
402
    public static function requireMorphMap($requireMorphMap = true)
403
    {
404
        static::$requireMorphMap = $requireMorphMap;
405
    }
406
 
407
    /**
408
     * Determine if polymorphic relationships require explicit model mapping.
409
     *
410
     * @return bool
411
     */
412
    public static function requiresMorphMap()
413
    {
414
        return static::$requireMorphMap;
415
    }
416
 
417
    /**
418
     * Define the morph map for polymorphic relations and require all morphed models to be explicitly mapped.
419
     *
420
     * @param  array  $map
421
     * @param  bool  $merge
422
     * @return array
423
     */
424
    public static function enforceMorphMap(array $map, $merge = true)
425
    {
426
        static::requireMorphMap();
427
 
428
        return static::morphMap($map, $merge);
429
    }
430
 
431
    /**
432
     * Set or get the morph map for polymorphic relations.
433
     *
434
     * @param  array|null  $map
435
     * @param  bool  $merge
436
     * @return array
437
     */
438
    public static function morphMap(array $map = null, $merge = true)
439
    {
440
        $map = static::buildMorphMapFromModels($map);
441
 
442
        if (is_array($map)) {
443
            static::$morphMap = $merge && static::$morphMap
444
                            ? $map + static::$morphMap : $map;
445
        }
446
 
447
        return static::$morphMap;
448
    }
449
 
450
    /**
451
     * Builds a table-keyed array from model class names.
452
     *
453
     * @param  string[]|null  $models
454
     * @return array|null
455
     */
456
    protected static function buildMorphMapFromModels(array $models = null)
457
    {
458
        if (is_null($models) || Arr::isAssoc($models)) {
459
            return $models;
460
        }
461
 
462
        return array_combine(array_map(function ($model) {
463
            return (new $model)->getTable();
464
        }, $models), $models);
465
    }
466
 
467
    /**
468
     * Get the model associated with a custom polymorphic type.
469
     *
470
     * @param  string  $alias
471
     * @return string|null
472
     */
473
    public static function getMorphedModel($alias)
474
    {
475
        return static::$morphMap[$alias] ?? null;
476
    }
477
 
478
    /**
479
     * Handle dynamic method calls to the relationship.
480
     *
481
     * @param  string  $method
482
     * @param  array  $parameters
483
     * @return mixed
484
     */
485
    public function __call($method, $parameters)
486
    {
487
        if (static::hasMacro($method)) {
488
            return $this->macroCall($method, $parameters);
489
        }
490
 
491
        return $this->forwardDecoratedCallTo($this->query, $method, $parameters);
492
    }
493
 
494
    /**
495
     * Force a clone of the underlying query builder when cloning.
496
     *
497
     * @return void
498
     */
499
    public function __clone()
500
    {
501
        $this->query = clone $this->query;
502
    }
503
}