1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* This file is part of Hydrogen package. |
4
|
|
|
* |
5
|
|
|
* For the full copyright and license information, please view the LICENSE |
6
|
|
|
* file that was distributed with this source code. |
7
|
|
|
*/ |
8
|
|
|
declare(strict_types=1); |
9
|
|
|
|
10
|
|
|
namespace RDS\Hydrogen; |
11
|
|
|
|
12
|
|
|
use Doctrine\Common\Persistence\ObjectRepository; |
13
|
|
|
use Illuminate\Support\Traits\Macroable; |
14
|
|
|
use RDS\Hydrogen\Criteria\CriterionInterface; |
15
|
|
|
use RDS\Hydrogen\Query\AliasProvider; |
16
|
|
|
use RDS\Hydrogen\Query\GroupByProvider; |
17
|
|
|
use RDS\Hydrogen\Query\LimitAndOffsetProvider; |
18
|
|
|
use RDS\Hydrogen\Query\OrderProvider; |
19
|
|
|
use RDS\Hydrogen\Query\RelationProvider; |
20
|
|
|
use RDS\Hydrogen\Query\RepositoryProvider; |
21
|
|
|
use RDS\Hydrogen\Query\ExecutionsProvider; |
22
|
|
|
use RDS\Hydrogen\Query\SelectProvider; |
23
|
|
|
use RDS\Hydrogen\Query\WhereProvider; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* Class Query |
27
|
|
|
*/ |
28
|
|
|
class Query implements \IteratorAggregate |
29
|
|
|
{ |
30
|
|
|
use Macroable { |
31
|
|
|
Macroable::__call as __macroableCall; |
32
|
|
|
} |
33
|
|
|
use AliasProvider; |
34
|
|
|
use WhereProvider; |
35
|
|
|
use OrderProvider; |
36
|
|
|
use SelectProvider; |
37
|
|
|
use GroupByProvider; |
38
|
|
|
use RelationProvider; |
39
|
|
|
use RepositoryProvider; |
40
|
|
|
use ExecutionsProvider; |
41
|
|
|
use LimitAndOffsetProvider; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @var CriterionInterface[]|\SplObjectStorage |
45
|
|
|
*/ |
46
|
|
|
protected $criteria; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @var array|ObjectRepository[] |
50
|
|
|
*/ |
51
|
|
|
protected $scopes = []; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* Query constructor. |
55
|
|
|
* @param ObjectRepository|null $repository |
56
|
|
|
*/ |
57
|
35 |
|
public function __construct(ObjectRepository $repository = null) |
58
|
|
|
{ |
59
|
35 |
|
$this->criteria = new \SplObjectStorage(); |
60
|
|
|
|
61
|
35 |
|
if ($repository) { |
62
|
|
|
$this->from($repository); |
63
|
|
|
} |
64
|
35 |
|
} |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* Adds the specified set of scopes (method groups) to the query. |
68
|
|
|
* |
69
|
|
|
* @param object|string ...$scopes |
70
|
|
|
* @return Query|$this |
71
|
|
|
*/ |
72
|
3 |
|
public function scope(...$scopes): self |
73
|
|
|
{ |
74
|
3 |
|
$this->scopes = \array_merge($this->scopes, $scopes); |
75
|
|
|
|
76
|
3 |
|
return $this; |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* Returns a list of selection criteria. |
81
|
|
|
* |
82
|
|
|
* @return \Generator|CriterionInterface[] |
83
|
|
|
*/ |
84
|
35 |
|
public function getCriteria(): \Generator |
85
|
|
|
{ |
86
|
35 |
|
yield from $this->criteria; |
87
|
3 |
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* Creates a new query (alias to the constructor). |
91
|
|
|
* |
92
|
|
|
* @param CriterionInterface $criterion |
93
|
|
|
* @return Query|$this |
94
|
|
|
*/ |
95
|
35 |
|
public function add(CriterionInterface $criterion): self |
96
|
|
|
{ |
97
|
35 |
|
$this->criteria->attach($criterion->withQuery($this)); |
98
|
|
|
|
99
|
35 |
|
return $this; |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* Creates a new query (alias to the constructor). |
104
|
|
|
* |
105
|
|
|
* @param ObjectRepository|null $repository |
106
|
|
|
* @return Query |
107
|
|
|
*/ |
108
|
35 |
|
public static function new(ObjectRepository $repository = null): Query |
109
|
|
|
{ |
110
|
35 |
|
return new static($repository); |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* @param string $name |
115
|
|
|
* @return null |
116
|
|
|
*/ |
117
|
16 |
|
public function __get(string $name) |
118
|
|
|
{ |
119
|
16 |
|
if (\method_exists($this, $name)) { |
120
|
16 |
|
return $this->$name(); |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
return null; |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* @param string $method |
128
|
|
|
* @param array $parameters |
129
|
|
|
* @return mixed|$this|Query |
130
|
|
|
*/ |
131
|
|
|
public function __call(string $method, array $parameters = []) |
132
|
|
|
{ |
133
|
|
|
foreach ($this->scopes as $scope) { |
134
|
|
|
if (\method_exists($scope, $method)) { |
135
|
|
|
/** @var Query $query */ |
136
|
|
|
$query = \is_object($scope) |
137
|
|
|
? clone $scope->$method(...$parameters) |
138
|
|
|
: clone $scope::$method(...$parameters); |
139
|
|
|
|
140
|
|
|
foreach ($query->getCriteria() as $criterion) { |
141
|
|
|
$this->add($criterion); |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
return $this; |
145
|
|
|
} |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
return $this->__macroableCall($method, $parameters); |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* Returns a set of scopes for the specified query. |
153
|
|
|
* |
154
|
|
|
* @return array|ObjectRepository[] |
155
|
|
|
*/ |
156
|
3 |
|
public function getScopes(): array |
157
|
|
|
{ |
158
|
3 |
|
return $this->scopes; |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* Attaches a child query to the parent without affecting |
163
|
|
|
* the set of criteria (selections). |
164
|
|
|
* |
165
|
|
|
* @param Query $query |
166
|
|
|
* @return Query |
167
|
|
|
*/ |
168
|
3 |
|
public function attach(Query $query): Query |
169
|
|
|
{ |
170
|
3 |
|
$this->repository |
171
|
|
|
? $query->from($this->getRepository()) |
|
|
|
|
172
|
3 |
|
: $query->alias = $this->getAlias(); |
173
|
|
|
|
174
|
3 |
|
return $query; |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* @param string $alias |
179
|
|
|
* @return Query|$this|self |
180
|
|
|
*/ |
181
|
|
|
public function withAlias(string $alias): Query |
182
|
|
|
{ |
183
|
|
|
$this->alias = $alias; |
184
|
|
|
|
185
|
|
|
foreach ($this->criteria as $criterion) { |
186
|
|
|
$criterion->withAlias($alias); |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
return $this; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* Creates a new query using the current set of scopes. |
194
|
|
|
* |
195
|
|
|
* @return Query |
196
|
|
|
*/ |
197
|
3 |
|
public function create(): Query |
198
|
|
|
{ |
199
|
3 |
|
return static::new()->scope(...$this->getScopes()); |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* Copies a set of Criteria from the child query to the parent. |
204
|
|
|
* |
205
|
|
|
* @param Query $query |
206
|
|
|
* @return Query |
207
|
|
|
*/ |
208
|
|
|
public function merge(Query $query): Query |
209
|
|
|
{ |
210
|
|
|
foreach ($query->getCriteria() as $criterion) { |
211
|
|
|
$criterion->withAlias($query->getAlias()); |
212
|
|
|
$this->add($criterion); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
return $this; |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* @return void |
220
|
|
|
*/ |
221
|
|
|
public function __clone() |
222
|
|
|
{ |
223
|
|
|
$reflection = new \ReflectionClass($this); |
224
|
|
|
|
225
|
|
|
foreach ($reflection->getProperties() as $property) { |
226
|
|
|
$property->setAccessible(true); |
227
|
|
|
$value = $property->getValue($this); |
228
|
|
|
|
229
|
|
|
if (\is_object($value)) { |
230
|
|
|
$property->setValue($this, clone $value); |
231
|
|
|
} |
232
|
|
|
} |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
/** |
236
|
|
|
* @return \Generator |
237
|
|
|
*/ |
238
|
|
|
public function getIterator(): \Generator |
239
|
|
|
{ |
240
|
|
|
foreach ($this->get() as $result) { |
241
|
|
|
yield $result; |
242
|
|
|
} |
243
|
|
|
} |
244
|
|
|
} |
245
|
|
|
|
This check looks at variables that are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.