-
-
Notifications
You must be signed in to change notification settings - Fork 299
/
HasManyPhotos.php
127 lines (117 loc) · 4.34 KB
/
HasManyPhotos.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
<?php
namespace App\Relations;
use App\Actions\PhotoAuthorisationProvider;
use App\DTO\SortingCriterion;
use App\Exceptions\Internal\InvalidOrderDirectionException;
use App\Models\Extensions\BaseAlbum;
use App\Models\Extensions\FixedQueryBuilder;
use App\Models\Extensions\SortingDecorator;
use App\Models\Photo;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\Relation;
/**
* Common base class of all photo relations for albums which are not the
* direct parent of the queried photos, but include the photo due to some
* indirect condition.
*/
abstract class HasManyPhotos extends Relation
{
protected PhotoAuthorisationProvider $photoAuthorisationProvider;
public function __construct(BaseAlbum $owningAlbum)
{
// Sic! We must initialize attributes of this class before we call
// the parent constructor.
// The parent constructor calls `addConstraints` and thus our own
// attributes must be initialized by then
$this->photoAuthorisationProvider = resolve(PhotoAuthorisationProvider::class);
// This is a hack.
// The abstract class
// {@link \Illuminate\Database\Eloquent\Relations\Relation}
// stores a pointer to the parent and assumes that the parent is
// an instance of {@link Illuminate\Database\Eloquent\Model}.
// However, we cannot guarantee this, because we have smart albums
// which do not exist on the DB and therefore do not extend
// `Model`.
// Actually, it is sufficient if the owning side implements the
// method which are provided by `HasRelations`.
// Unfortunately, the constructor of `Relation` demands a true model
// and does not only ask for something which implements `HasRelations`.
// Luckily, `Relation` itself does not do anything with the passed
// model but only stores a reference in `Relation::$parent` to be
// used by child classes.
// Moreover, it is impossible to pass `null`.
// As a work-around we store the owning album in our own attribute
// `$owningAlbum` and always use that instead of `$parent`.
parent::__construct(
// Sic! We also must load the album eagerly.
// This relation is not used by albums which own the queried
// photos, but by albums which only include the photos due to some
// indirect condition.
// Hence, the actually owning albums of the photos are not
// necessarily loaded.
Photo::query()->with(['album', 'size_variants', 'size_variants.sym_links']),
$owningAlbum
);
}
protected function getRelationQuery(): FixedQueryBuilder
{
/*
* We know that the internal query is of type `FixedQueryBuilder`,
* because it was set int the constructor as `Photo::query()`.
*/
return $this->query; // @phpstan-ignore-line @noinspection PhpIncompatibleReturnTypeInspection
}
public function getParent(): BaseAlbum
{
/*
* We know that the parent is of type `BaseAlbum`,
* because it was set int the constructor as `$owningAlbum`.
*/
return $this->parent; // @phpstan-ignore-line @noinspection PhpIncompatibleReturnTypeInspection
}
/**
* Initializes the given owning models with a default value of this
* relation.
*
* In this case, the default value is an empty collection of
* {@link \App\Models\Photo}.
*
* @param array $models a list of owning models, i.e. a list of albums
* @param string $relation the name of the relation on the owning models
*
* @return array always returns $models
*/
public function initRelation(array $models, $relation): array
{
/** @var BaseAlbum $model */
foreach ($models as $model) {
$model->setRelation($relation, $this->related->newCollection());
}
return $models;
}
/**
* Returns the collection of photos for a single owning parent (aka
* "album").
*
* This method also takes care of proper sorting.
* For most columns this method performs sorting on the DB layer for
* improved performance.
* But for some columns which require "natural" and locale-dependent
* sorting, the collection is sorted after is has been fetched from
* the DB.
*
* @return Collection
*
* @throws InvalidOrderDirectionException
*/
public function getResults(): Collection
{
/** @var BaseAlbum */
$parent = $this->parent;
/** @var SortingCriterion $sorting */
$sorting = $parent->getEffectiveSorting();
return (new SortingDecorator($this->getRelationQuery()))
->orderBy('photos.' . $sorting->column, $sorting->order)
->get();
}
}