Subversion-Projekte lars-tiefland.laravel_shop

Revision

Revision 399 | 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
    /**
621 lars 368
     * Create a Collection of new instances of the related model without raising any events to the parent model.
369
     *
370
     * @param  iterable  $records
371
     * @return \Illuminate\Database\Eloquent\Collection
372
     */
373
    public function createManyQuietly(iterable $records)
374
    {
375
        return Model::withoutEvents(fn () => $this->createMany($records));
376
    }
377
 
378
    /**
148 lars 379
     * Set the foreign ID for creating a related model.
380
     *
381
     * @param  \Illuminate\Database\Eloquent\Model  $model
382
     * @return void
383
     */
384
    protected function setForeignAttributesForCreate(Model $model)
385
    {
386
        $model->setAttribute($this->getForeignKeyName(), $this->getParentKey());
387
    }
388
 
389
    /**
390
     * Add the constraints for a relationship query.
391
     *
392
     * @param  \Illuminate\Database\Eloquent\Builder  $query
393
     * @param  \Illuminate\Database\Eloquent\Builder  $parentQuery
394
     * @param  array|mixed  $columns
395
     * @return \Illuminate\Database\Eloquent\Builder
396
     */
397
    public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
398
    {
399
        if ($query->getQuery()->from == $parentQuery->getQuery()->from) {
400
            return $this->getRelationExistenceQueryForSelfRelation($query, $parentQuery, $columns);
401
        }
402
 
403
        return parent::getRelationExistenceQuery($query, $parentQuery, $columns);
404
    }
405
 
406
    /**
407
     * Add the constraints for a relationship query on the same table.
408
     *
409
     * @param  \Illuminate\Database\Eloquent\Builder  $query
410
     * @param  \Illuminate\Database\Eloquent\Builder  $parentQuery
411
     * @param  array|mixed  $columns
412
     * @return \Illuminate\Database\Eloquent\Builder
413
     */
414
    public function getRelationExistenceQueryForSelfRelation(Builder $query, Builder $parentQuery, $columns = ['*'])
415
    {
416
        $query->from($query->getModel()->getTable().' as '.$hash = $this->getRelationCountHash());
417
 
418
        $query->getModel()->setTable($hash);
419
 
420
        return $query->select($columns)->whereColumn(
421
            $this->getQualifiedParentKeyName(), '=', $hash.'.'.$this->getForeignKeyName()
422
        );
423
    }
424
 
425
    /**
426
     * Get the key for comparing against the parent key in "has" query.
427
     *
428
     * @return string
429
     */
430
    public function getExistenceCompareKey()
431
    {
432
        return $this->getQualifiedForeignKeyName();
433
    }
434
 
435
    /**
436
     * Get the key value of the parent's local key.
437
     *
438
     * @return mixed
439
     */
440
    public function getParentKey()
441
    {
442
        return $this->parent->getAttribute($this->localKey);
443
    }
444
 
445
    /**
446
     * Get the fully qualified parent key name.
447
     *
448
     * @return string
449
     */
450
    public function getQualifiedParentKeyName()
451
    {
452
        return $this->parent->qualifyColumn($this->localKey);
453
    }
454
 
455
    /**
456
     * Get the plain foreign key.
457
     *
458
     * @return string
459
     */
460
    public function getForeignKeyName()
461
    {
462
        $segments = explode('.', $this->getQualifiedForeignKeyName());
463
 
464
        return end($segments);
465
    }
466
 
467
    /**
468
     * Get the foreign key for the relationship.
469
     *
470
     * @return string
471
     */
472
    public function getQualifiedForeignKeyName()
473
    {
474
        return $this->foreignKey;
475
    }
476
 
477
    /**
478
     * Get the local key for the relationship.
479
     *
480
     * @return string
481
     */
482
    public function getLocalKeyName()
483
    {
484
        return $this->localKey;
485
    }
486
}