Subversion-Projekte lars-tiefland.laravel_shop

Revision

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

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
namespace Illuminate\Database\Eloquent\Relations;
4
 
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Database\Eloquent\Collection;
7
use Illuminate\Database\Eloquent\Model;
8
use Illuminate\Database\Eloquent\Relations\Concerns\InteractsWithDictionary;
9
 
10
abstract class HasOneOrMany extends Relation
11
{
12
    use InteractsWithDictionary;
13
 
14
    /**
15
     * The foreign key of the parent model.
16
     *
17
     * @var string
18
     */
19
    protected $foreignKey;
20
 
21
    /**
22
     * The local key of the parent model.
23
     *
24
     * @var string
25
     */
26
    protected $localKey;
27
 
28
    /**
29
     * Create a new has one or many relationship instance.
30
     *
31
     * @param  \Illuminate\Database\Eloquent\Builder  $query
32
     * @param  \Illuminate\Database\Eloquent\Model  $parent
33
     * @param  string  $foreignKey
34
     * @param  string  $localKey
35
     * @return void
36
     */
37
    public function __construct(Builder $query, Model $parent, $foreignKey, $localKey)
38
    {
39
        $this->localKey = $localKey;
40
        $this->foreignKey = $foreignKey;
41
 
42
        parent::__construct($query, $parent);
43
    }
44
 
45
    /**
46
     * Create and return an un-saved instance of the related model.
47
     *
48
     * @param  array  $attributes
49
     * @return \Illuminate\Database\Eloquent\Model
50
     */
51
    public function make(array $attributes = [])
52
    {
53
        return tap($this->related->newInstance($attributes), function ($instance) {
54
            $this->setForeignAttributesForCreate($instance);
55
        });
56
    }
57
 
58
    /**
59
     * Create and return an un-saved instance of the related models.
60
     *
61
     * @param  iterable  $records
62
     * @return \Illuminate\Database\Eloquent\Collection
63
     */
64
    public function makeMany($records)
65
    {
66
        $instances = $this->related->newCollection();
67
 
68
        foreach ($records as $record) {
69
            $instances->push($this->make($record));
70
        }
71
 
72
        return $instances;
73
    }
74
 
75
    /**
76
     * Set the base constraints on the relation query.
77
     *
78
     * @return void
79
     */
80
    public function addConstraints()
81
    {
82
        if (static::$constraints) {
83
            $query = $this->getRelationQuery();
84
 
85
            $query->where($this->foreignKey, '=', $this->getParentKey());
86
 
87
            $query->whereNotNull($this->foreignKey);
88
        }
89
    }
90
 
91
    /**
92
     * Set the constraints for an eager load of the relation.
93
     *
94
     * @param  array  $models
95
     * @return void
96
     */
97
    public function addEagerConstraints(array $models)
98
    {
99
        $whereIn = $this->whereInMethod($this->parent, $this->localKey);
100
 
101
        $this->getRelationQuery()->{$whereIn}(
102
            $this->foreignKey, $this->getKeys($models, $this->localKey)
103
        );
104
    }
105
 
106
    /**
107
     * Match the eagerly loaded results to their single parents.
108
     *
109
     * @param  array  $models
110
     * @param  \Illuminate\Database\Eloquent\Collection  $results
111
     * @param  string  $relation
112
     * @return array
113
     */
114
    public function matchOne(array $models, Collection $results, $relation)
115
    {
116
        return $this->matchOneOrMany($models, $results, $relation, 'one');
117
    }
118
 
119
    /**
120
     * Match the eagerly loaded results to their many parents.
121
     *
122
     * @param  array  $models
123
     * @param  \Illuminate\Database\Eloquent\Collection  $results
124
     * @param  string  $relation
125
     * @return array
126
     */
127
    public function matchMany(array $models, Collection $results, $relation)
128
    {
129
        return $this->matchOneOrMany($models, $results, $relation, 'many');
130
    }
131
 
132
    /**
133
     * Match the eagerly loaded results to their many parents.
134
     *
135
     * @param  array  $models
136
     * @param  \Illuminate\Database\Eloquent\Collection  $results
137
     * @param  string  $relation
138
     * @param  string  $type
139
     * @return array
140
     */
141
    protected function matchOneOrMany(array $models, Collection $results, $relation, $type)
142
    {
143
        $dictionary = $this->buildDictionary($results);
144
 
145
        // Once we have the dictionary we can simply spin through the parent models to
146
        // link them up with their children using the keyed dictionary to make the
147
        // matching very convenient and easy work. Then we'll just return them.
148
        foreach ($models as $model) {
149
            if (isset($dictionary[$key = $this->getDictionaryKey($model->getAttribute($this->localKey))])) {
150
                $model->setRelation(
151
                    $relation, $this->getRelationValue($dictionary, $key, $type)
152
                );
153
            }
154
        }
155
 
156
        return $models;
157
    }
158
 
159
    /**
160
     * Get the value of a relationship by one or many type.
161
     *
162
     * @param  array  $dictionary
163
     * @param  string  $key
164
     * @param  string  $type
165
     * @return mixed
166
     */
167
    protected function getRelationValue(array $dictionary, $key, $type)
168
    {
169
        $value = $dictionary[$key];
170
 
171
        return $type === 'one' ? reset($value) : $this->related->newCollection($value);
172
    }
173
 
174
    /**
175
     * Build model dictionary keyed by the relation's foreign key.
176
     *
177
     * @param  \Illuminate\Database\Eloquent\Collection  $results
178
     * @return array
179
     */
180
    protected function buildDictionary(Collection $results)
181
    {
182
        $foreign = $this->getForeignKeyName();
183
 
184
        return $results->mapToDictionary(function ($result) use ($foreign) {
185
            return [$this->getDictionaryKey($result->{$foreign}) => $result];
186
        })->all();
187
    }
188
 
189
    /**
190
     * Find a model by its primary key or return a new instance of the related model.
191
     *
192
     * @param  mixed  $id
193
     * @param  array  $columns
194
     * @return \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Model
195
     */
196
    public function findOrNew($id, $columns = ['*'])
197
    {
198
        if (is_null($instance = $this->find($id, $columns))) {
199
            $instance = $this->related->newInstance();
200
 
201
            $this->setForeignAttributesForCreate($instance);
202
        }
203
 
204
        return $instance;
205
    }
206
 
207
    /**
208
     * Get the first related model record matching the attributes or instantiate it.
209
     *
210
     * @param  array  $attributes
211
     * @param  array  $values
212
     * @return \Illuminate\Database\Eloquent\Model
213
     */
214
    public function firstOrNew(array $attributes = [], array $values = [])
215
    {
216
        if (is_null($instance = $this->where($attributes)->first())) {
217
            $instance = $this->related->newInstance(array_merge($attributes, $values));
218
 
219
            $this->setForeignAttributesForCreate($instance);
220
        }
221
 
222
        return $instance;
223
    }
224
 
225
    /**
226
     * Get the first related record matching the attributes or create it.
227
     *
228
     * @param  array  $attributes
229
     * @param  array  $values
230
     * @return \Illuminate\Database\Eloquent\Model
231
     */
232
    public function firstOrCreate(array $attributes = [], array $values = [])
233
    {
234
        if (is_null($instance = $this->where($attributes)->first())) {
235
            $instance = $this->create(array_merge($attributes, $values));
236
        }
237
 
238
        return $instance;
239
    }
240
 
241
    /**
242
     * Create or update a related record matching the attributes, and fill it with values.
243
     *
244
     * @param  array  $attributes
245
     * @param  array  $values
246
     * @return \Illuminate\Database\Eloquent\Model
247
     */
248
    public function updateOrCreate(array $attributes, array $values = [])
249
    {
250
        return tap($this->firstOrNew($attributes), function ($instance) use ($values) {
251
            $instance->fill($values);
252
 
253
            $instance->save();
254
        });
255
    }
256
 
257
    /**
258
     * Attach a model instance to the parent model.
259
     *
260
     * @param  \Illuminate\Database\Eloquent\Model  $model
261
     * @return \Illuminate\Database\Eloquent\Model|false
262
     */
263
    public function save(Model $model)
264
    {
265
        $this->setForeignAttributesForCreate($model);
266
 
267
        return $model->save() ? $model : false;
268
    }
269
 
270
    /**
271
     * Attach a model instance without raising any events to the parent model.
272
     *
273
     * @param  \Illuminate\Database\Eloquent\Model  $model
274
     * @return \Illuminate\Database\Eloquent\Model|false
275
     */
276
    public function saveQuietly(Model $model)
277
    {
278
        return Model::withoutEvents(function () use ($model) {
279
            return $this->save($model);
280
        });
281
    }
282
 
283
    /**
284
     * Attach a collection of models to the parent instance.
285
     *
286
     * @param  iterable  $models
287
     * @return iterable
288
     */
289
    public function saveMany($models)
290
    {
291
        foreach ($models as $model) {
292
            $this->save($model);
293
        }
294
 
295
        return $models;
296
    }
297
 
298
    /**
299
     * Attach a collection of models to the parent instance without raising any events to the parent model.
300
     *
301
     * @param  iterable  $models
302
     * @return iterable
303
     */
304
    public function saveManyQuietly($models)
305
    {
306
        return Model::withoutEvents(function () use ($models) {
307
            return $this->saveMany($models);
308
        });
309
    }
310
 
311
    /**
312
     * Create a new instance of the related model.
313
     *
314
     * @param  array  $attributes
315
     * @return \Illuminate\Database\Eloquent\Model
316
     */
317
    public function create(array $attributes = [])
318
    {
319
        return tap($this->related->newInstance($attributes), function ($instance) {
320
            $this->setForeignAttributesForCreate($instance);
321
 
322
            $instance->save();
323
        });
324
    }
325
 
326
    /**
399 lars 327
     * Create a new instance of the related model without raising any events to the parent model.
328
     *
329
     * @param  array  $attributes
330
     * @return \Illuminate\Database\Eloquent\Model
331
     */
332
    public function createQuietly(array $attributes = [])
333
    {
334
        return Model::withoutEvents(fn () => $this->create($attributes));
335
    }
336
 
337
    /**
148 lars 338
     * Create a new instance of the related model. Allow mass-assignment.
339
     *
340
     * @param  array  $attributes
341
     * @return \Illuminate\Database\Eloquent\Model
342
     */
343
    public function forceCreate(array $attributes = [])
344
    {
345
        $attributes[$this->getForeignKeyName()] = $this->getParentKey();
346
 
347
        return $this->related->forceCreate($attributes);
348
    }
349
 
350
    /**
351
     * Create a Collection of new instances of the related model.
352
     *
353
     * @param  iterable  $records
354
     * @return \Illuminate\Database\Eloquent\Collection
355
     */
356
    public function createMany(iterable $records)
357
    {
358
        $instances = $this->related->newCollection();
359
 
360
        foreach ($records as $record) {
361
            $instances->push($this->create($record));
362
        }
363
 
364
        return $instances;
365
    }
366
 
367
    /**
368
     * Set the foreign ID for creating a related model.
369
     *
370
     * @param  \Illuminate\Database\Eloquent\Model  $model
371
     * @return void
372
     */
373
    protected function setForeignAttributesForCreate(Model $model)
374
    {
375
        $model->setAttribute($this->getForeignKeyName(), $this->getParentKey());
376
    }
377
 
378
    /**
379
     * Add the constraints for a relationship query.
380
     *
381
     * @param  \Illuminate\Database\Eloquent\Builder  $query
382
     * @param  \Illuminate\Database\Eloquent\Builder  $parentQuery
383
     * @param  array|mixed  $columns
384
     * @return \Illuminate\Database\Eloquent\Builder
385
     */
386
    public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
387
    {
388
        if ($query->getQuery()->from == $parentQuery->getQuery()->from) {
389
            return $this->getRelationExistenceQueryForSelfRelation($query, $parentQuery, $columns);
390
        }
391
 
392
        return parent::getRelationExistenceQuery($query, $parentQuery, $columns);
393
    }
394
 
395
    /**
396
     * Add the constraints for a relationship query on the same table.
397
     *
398
     * @param  \Illuminate\Database\Eloquent\Builder  $query
399
     * @param  \Illuminate\Database\Eloquent\Builder  $parentQuery
400
     * @param  array|mixed  $columns
401
     * @return \Illuminate\Database\Eloquent\Builder
402
     */
403
    public function getRelationExistenceQueryForSelfRelation(Builder $query, Builder $parentQuery, $columns = ['*'])
404
    {
405
        $query->from($query->getModel()->getTable().' as '.$hash = $this->getRelationCountHash());
406
 
407
        $query->getModel()->setTable($hash);
408
 
409
        return $query->select($columns)->whereColumn(
410
            $this->getQualifiedParentKeyName(), '=', $hash.'.'.$this->getForeignKeyName()
411
        );
412
    }
413
 
414
    /**
415
     * Get the key for comparing against the parent key in "has" query.
416
     *
417
     * @return string
418
     */
419
    public function getExistenceCompareKey()
420
    {
421
        return $this->getQualifiedForeignKeyName();
422
    }
423
 
424
    /**
425
     * Get the key value of the parent's local key.
426
     *
427
     * @return mixed
428
     */
429
    public function getParentKey()
430
    {
431
        return $this->parent->getAttribute($this->localKey);
432
    }
433
 
434
    /**
435
     * Get the fully qualified parent key name.
436
     *
437
     * @return string
438
     */
439
    public function getQualifiedParentKeyName()
440
    {
441
        return $this->parent->qualifyColumn($this->localKey);
442
    }
443
 
444
    /**
445
     * Get the plain foreign key.
446
     *
447
     * @return string
448
     */
449
    public function getForeignKeyName()
450
    {
451
        $segments = explode('.', $this->getQualifiedForeignKeyName());
452
 
453
        return end($segments);
454
    }
455
 
456
    /**
457
     * Get the foreign key for the relationship.
458
     *
459
     * @return string
460
     */
461
    public function getQualifiedForeignKeyName()
462
    {
463
        return $this->foreignKey;
464
    }
465
 
466
    /**
467
     * Get the local key for the relationship.
468
     *
469
     * @return string
470
     */
471
    public function getLocalKeyName()
472
    {
473
        return $this->localKey;
474
    }
475
}