Passed
Push — master ( c80e12...1c5165 )
by Kirill
04:32
created

Query::isEmpty()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2
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 67
    public function __construct(ObjectRepository $repository = null)
58
    {
59 67
        $this->criteria = new \SplObjectStorage();
60
61 67
        if ($repository) {
62 14
            $this->from($repository);
63
        }
64 67
    }
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 33
    public function scope(...$scopes): self
73
    {
74 33
        $this->scopes = \array_merge($this->scopes, $scopes);
75
76 33
        return $this;
77
    }
78
79
    /**
80
     * Returns a list of selection criteria.
81
     *
82
     * @return \Generator|CriterionInterface[]
83
     */
84 67
    public function getCriteria(): \Generator
85
    {
86 67
        yield from $this->criteria;
87 35
    }
88
89
    /**
90
     * Creates a new query (alias to the constructor).
91
     *
92
     * @param CriterionInterface $criterion
93
     * @return Query|$this
94
     */
95 63
    public function add(CriterionInterface $criterion): self
96
    {
97 63
        $this->criteria->attach($criterion->withQuery($this));
98
99 63
        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 67
    public static function new(ObjectRepository $repository = null): Query
109
    {
110 67
        return new static($repository);
111
    }
112
113
    /**
114
     * @param string $name
115
     * @return null
116
     */
117 18
    public function __get(string $name)
118
    {
119 18
        if (\method_exists($this, $name)) {
120 18
            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 4
    public function __call(string $method, array $parameters = [])
132
    {
133 4
        foreach ($this->scopes as $scope) {
134 4
            if (\method_exists($scope, $method)) {
135
                /** @var Query $query */
136 4
                $query = \is_object($scope)
137 4
                    ? clone $scope->$method(...$parameters)
138 4
                    : clone $scope::$method(...$parameters);
139
140 4
                foreach ($query->getCriteria() as $criterion) {
141 4
                    $this->add($criterion);
142
                }
143
144 4
                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 7
    public function getScopes(): array
157
    {
158 7
        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 5
    public function attach(Query $query): Query
169
    {
170 5
        $this->repository
171 2
            ? $query->from($this->getRepository())
0 ignored issues
show
Bug introduced by
It seems like $this->getRepository() targeting RDS\Hydrogen\Query\Repos...ovider::getRepository() can also be of type object<RDS\Hydrogen\Hydrogen>; however, RDS\Hydrogen\Query\RepositoryProvider::from() does only seem to accept object<Doctrine\Common\P...tence\ObjectRepository>, maybe add an additional type check?

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.

Loading history...
172 3
            : $query->alias = $this->getAlias();
173
174 5
        return $query;
175
    }
176
177
    /**
178
     * @param string $alias
179
     * @return Query|$this|self
180
     */
181 2
    public function withAlias(string $alias): Query
182
    {
183 2
        $this->alias = $alias;
184
185 2
        foreach ($this->criteria as $criterion) {
186
            $criterion->withAlias($alias);
187
        }
188
189 2
        return $this;
190
    }
191
192
    /**
193
     * Creates a new query using the current set of scopes.
194
     *
195
     * @return Query
196
     */
197 7
    public function create(): Query
198
    {
199 7
        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 2
    public function merge(Query $query): Query
209
    {
210 2
        foreach ($query->getCriteria() as $criterion) {
211
            $criterion->withAlias($query->getAlias());
212
            $this->add($criterion);
213
        }
214
215 2
        return $this;
216
    }
217
218
    /**
219
     * @return void
220
     */
221 4
    public function __clone()
222
    {
223 4
        $reflection = new \ReflectionClass($this);
224
225 4
        foreach ($reflection->getProperties() as $property) {
226 4
            $property->setAccessible(true);
227 4
            $value = $property->getValue($this);
228
229 4
            if (\is_object($value)) {
230 4
                $property->setValue($this, clone $value);
231
            }
232
        }
233 4
    }
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
    /**
246
     * @return bool
247
     */
248
    public function isEmpty(): bool
249
    {
250
        return $this->criteria->count() === 0;
251
    }
252
}
253