Test Failed
Push — master ( b17eba...69b436 )
by Kirill
06:37
created

Query::with()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 21
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 8.7624
c 0
b 0
f 0
cc 6
eloc 12
nc 5
nop 1
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())
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 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