1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | namespace Umbrellio\LTree\Relations; |
||
6 | |||
7 | use Illuminate\Database\Eloquent\Builder; |
||
8 | use Illuminate\Database\Eloquent\Collection; |
||
9 | use Illuminate\Database\Eloquent\Model; |
||
10 | use Illuminate\Database\Eloquent\Relations\Relation; |
||
11 | use Illuminate\Database\Query\JoinClause; |
||
12 | use Umbrellio\LTree\Interfaces\LTreeModelInterface; |
||
13 | use Umbrellio\LTree\Traits\LTreeModelTrait; |
||
14 | |||
15 | abstract class AbstractBelongsToTree extends Relation |
||
16 | { |
||
17 | protected $throughRelationName; |
||
18 | private $foreignKey; |
||
19 | private $ownerKey; |
||
20 | |||
21 | 3 | public function __construct( |
|
22 | Builder $query, |
||
23 | Model $child, |
||
24 | string $throughRelationName, |
||
25 | string $foreignKey, |
||
26 | string $ownerKey |
||
27 | ) { |
||
28 | 3 | $this->throughRelationName = $throughRelationName; |
|
29 | 3 | $this->foreignKey = $foreignKey; |
|
30 | 3 | $this->ownerKey = $ownerKey; |
|
31 | |||
32 | 3 | parent::__construct($query, $child); |
|
33 | 3 | } |
|
34 | |||
35 | 3 | public function addConstraints(): void |
|
36 | { |
||
37 | 3 | if (static::$constraints) { |
|
38 | 3 | $relation = $this->parent->{$this->throughRelationName}; |
|
39 | |||
40 | 3 | if ($relation) { |
|
41 | 3 | $this->query = $this->getQueryForTree($relation); |
|
42 | } |
||
43 | } |
||
44 | 3 | } |
|
45 | |||
46 | 3 | public function addEagerConstraints(array $models): void |
|
47 | { |
||
48 | 3 | $key = $this->related->getTable() . '.' . $this->ownerKey; |
|
49 | |||
50 | 3 | $whereIn = $this->whereInMethod($this->related, $this->ownerKey); |
|
51 | |||
52 | 3 | $this->query->{$whereIn}($key, $this->getEagerModelKeys($models)); |
|
53 | |||
54 | $table = $this |
||
55 | 3 | ->getModel() |
|
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
56 | 3 | ->getTable(); |
|
57 | 3 | $alias = sprintf('%s_depends', $table); |
|
58 | |||
59 | 3 | $related = $this->getLTreeRelated(); |
|
60 | |||
61 | 3 | $this->query->join( |
|
62 | 3 | sprintf('%s as %s', $table, $alias), |
|
63 | 3 | function (JoinClause $query) use ($alias, $table, $related) { |
|
64 | 3 | $query->whereRaw(sprintf( |
|
65 | 3 | '%1$s.%2$s %4$s %3$s.%2$s', |
|
66 | $alias, |
||
67 | 3 | $related->getLtreePathColumn(), |
|
68 | $table, |
||
69 | 3 | $this->getOperator() |
|
70 | )); |
||
71 | 3 | } |
|
72 | ); |
||
73 | |||
74 | 3 | $this->query->orderBy($related->getLtreePathColumn()); |
|
75 | |||
76 | 3 | $this->query->selectRaw(sprintf('%s.*, %s.%s as relation_id', $alias, $table, $this->ownerKey)); |
|
77 | 3 | } |
|
78 | |||
79 | 3 | public function match(array $models, Collection $results, $relation) |
|
80 | { |
||
81 | 3 | $dictionary = []; |
|
82 | |||
83 | 3 | foreach ($results as $result) { |
|
84 | 3 | $dictionary[$result->relation_id][] = $result; |
|
85 | } |
||
86 | |||
87 | 3 | foreach ($models as $model) { |
|
88 | 3 | foreach ($dictionary as $related => $value) { |
|
89 | 3 | if ($model->getAttribute($this->foreignKey) === $related) { |
|
90 | 3 | $model->setRelation($relation, $this->related->newCollection($value)); |
|
91 | } |
||
92 | } |
||
93 | } |
||
94 | |||
95 | 3 | return $models; |
|
96 | } |
||
97 | |||
98 | 3 | public function getResults() |
|
99 | { |
||
100 | 3 | return $this->getParentKey() !== null |
|
101 | 3 | ? $this->query->get() |
|
102 | 3 | : $this->related->newCollection(); |
|
103 | } |
||
104 | |||
105 | |||
106 | /** |
||
107 | * Initialize the relation on a set of models. |
||
108 | * |
||
109 | * @param string $relation |
||
110 | * @return array |
||
111 | */ |
||
112 | 3 | public function initRelation(array $models, $relation) |
|
113 | { |
||
114 | 3 | foreach ($models as $model) { |
|
115 | 3 | $model->setRelation($relation, $this->related->newCollection()); |
|
116 | } |
||
117 | |||
118 | 3 | return $models; |
|
119 | } |
||
120 | |||
121 | /** |
||
122 | * @param Builder|LTreeModelTrait $model |
||
123 | */ |
||
124 | abstract protected function getQueryForTree($model): Builder; |
||
125 | |||
126 | abstract protected function getOperator(): string; |
||
127 | |||
128 | 3 | protected function getEagerModelKeys(array $models) |
|
129 | { |
||
130 | 3 | $keys = []; |
|
131 | |||
132 | 3 | foreach ($models as $model) { |
|
133 | 3 | if (($value = $model->{$this->foreignKey}) !== null) { |
|
134 | 3 | $keys[] = $value; |
|
135 | } |
||
136 | } |
||
137 | |||
138 | 3 | sort($keys); |
|
139 | |||
140 | 3 | return array_values(array_unique($keys)); |
|
141 | } |
||
142 | |||
143 | 3 | protected function getLTreeRelated(): LTreeModelInterface |
|
144 | { |
||
145 | return $this |
||
146 | 3 | ->parent |
|
147 | 3 | ->{$this->throughRelationName}() |
|
148 | 3 | ->related; |
|
149 | } |
||
150 | |||
151 | 3 | private function getParentKey() |
|
152 | { |
||
153 | 3 | return $this->parent->{$this->foreignKey}; |
|
154 | } |
||
155 | } |
||
156 |