Completed
Push — 2.0 ( 0357a9...3fe951 )
by Peter
08:22 queued 10s
created

applySpecification()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 9.0444
c 0
b 0
f 0
cc 6
nc 6
nop 3
1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * This file is part of the Happyr Doctrine Specification package.
6
 *
7
 * (c) Tobias Nyholm <[email protected]>
8
 *     Kacper Gunia <[email protected]>
9
 *     Peter Gribanov <[email protected]>
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
namespace Happyr\DoctrineSpecification;
16
17
use Doctrine\ORM\AbstractQuery;
18
use Doctrine\ORM\NonUniqueResultException;
19
use Doctrine\ORM\NoResultException;
20
use Doctrine\ORM\Query;
21
use Doctrine\ORM\QueryBuilder;
22
use Happyr\DoctrineSpecification\Filter\Filter;
23
use Happyr\DoctrineSpecification\Query\QueryModifier;
24
use Happyr\DoctrineSpecification\Result\ResultModifier;
25
26
/**
27
 * This trait should be used by a class extending \Doctrine\ORM\EntityRepository.
28
 */
29
trait EntitySpecificationRepositoryTrait
30
{
31
    /**
32
     * @var string
33
     */
34
    private $alias = 'root';
35
36
    /**
37
     * Get results when you match with a Specification.
38
     *
39
     * @param Filter|QueryModifier $specification
40
     * @param ResultModifier|null  $modifier
41
     *
42
     * @return mixed
43
     */
44
    public function match($specification, ?ResultModifier $modifier = null)
45
    {
46
        $query = $this->getQuery($specification, $modifier);
47
48
        return $query->execute();
49
    }
50
51
    /**
52
     * Get single result when you match with a Specification.
53
     *
54
     * @param Filter|QueryModifier $specification
55
     * @param ResultModifier|null  $modifier
56
     *
57
     * @throw Exception\NonUniqueException  If more than one result is found
58
     * @throw Exception\NoResultException   If no results found
59
     *
60
     * @return mixed
61
     */
62
    public function matchSingleResult($specification, ?ResultModifier $modifier = null)
63
    {
64
        $query = $this->getQuery($specification, $modifier);
65
66
        try {
67
            return $query->getSingleResult();
68
        } catch (NonUniqueResultException $e) {
69
            throw new Exception\NonUniqueResultException($e->getMessage(), $e->getCode(), $e);
70
        } catch (NoResultException $e) {
71
            throw new Exception\NoResultException($e->getMessage(), $e->getCode(), $e);
72
        }
73
    }
74
75
    /**
76
     * Get single result or null when you match with a Specification.
77
     *
78
     * @param Filter|QueryModifier $specification
79
     * @param ResultModifier|null  $modifier
80
     *
81
     * @throw Exception\NonUniqueException  If more than one result is found
82
     *
83
     * @return mixed|null
84
     */
85
    public function matchOneOrNullResult($specification, ?ResultModifier $modifier = null)
86
    {
87
        try {
88
            return $this->matchSingleResult($specification, $modifier);
89
        } catch (Exception\NoResultException $e) {
90
            return null;
91
        }
92
    }
93
94
    /**
95
     * Get single scalar result when you match with a Specification.
96
     *
97
     * @param Filter|QueryModifier $specification
98
     * @param ResultModifier|null  $modifier
99
     *
100
     * @throw Exception\NonUniqueException  If more than one result is found
101
     * @throw Exception\NoResultException   If no results found
102
     *
103
     * @return mixed
104
     */
105
    public function matchSingleScalarResult($specification, ?ResultModifier $modifier = null)
106
    {
107
        $query = $this->getQuery($specification, $modifier);
108
109
        try {
110
            return $query->getSingleScalarResult();
111
        } catch (NonUniqueResultException $e) {
112
            throw new Exception\NonUniqueResultException($e->getMessage(), $e->getCode(), $e);
113
        }
114
    }
115
116
    /**
117
     * Get scalar result when you match with a Specification.
118
     *
119
     * @param Filter|QueryModifier $specification
120
     * @param ResultModifier|null  $modifier
121
     *
122
     * @throw Exception\NonUniqueException  If more than one result is found
123
     * @throw Exception\NoResultException   If no results found
124
     *
125
     * @return mixed
126
     */
127
    public function matchScalarResult($specification, ?ResultModifier $modifier = null)
128
    {
129
        $query = $this->getQuery($specification, $modifier);
130
131
        return $query->getScalarResult();
132
    }
133
134
    /**
135
     * Prepare a Query with a Specification.
136
     *
137
     * @param Filter|QueryModifier $specification
138
     * @param ResultModifier|null  $modifier
139
     *
140
     * @return Query
141
     */
142
    public function getQuery($specification, ?ResultModifier $modifier = null): AbstractQuery
143
    {
144
        $query = $this->getQueryBuilder($specification)->getQuery();
145
146
        if (null !== $modifier) {
147
            $modifier->modify($query);
148
        }
149
150
        return $query;
151
    }
152
153
    /**
154
     * @param Filter|QueryModifier $specification
155
     * @param string|null          $alias
156
     *
157
     * @return QueryBuilder
158
     */
159
    public function getQueryBuilder($specification, ?string $alias = null): QueryBuilder
160
    {
161
        $qb = $this->createQueryBuilder($alias ?: $this->getAlias());
0 ignored issues
show
Bug introduced by
It seems like createQueryBuilder() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
162
        $this->applySpecification($qb, $specification, $alias);
163
164
        return $qb;
165
    }
166
167
    /**
168
     * Iterate results when you match with a Specification.
169
     *
170
     * @param Filter|QueryModifier $specification
171
     * @param ResultModifier|null  $modifier
172
     *
173
     * @return \Traversable<mixed>
0 ignored issues
show
Documentation introduced by
The doc-type \Traversable<mixed> could not be parsed: Expected "|" or "end of type", but got "<" at position 12. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
174
     */
175
    public function iterate($specification, ?ResultModifier $modifier = null): \Traversable
176
    {
177
        foreach ($this->getQuery($specification, $modifier)->iterate() as $row) {
178
            yield current($row);
179
        }
180
    }
181
182
    /**
183
     * @param string $alias
184
     */
185
    public function setAlias(string $alias): void
186
    {
187
        $this->alias = $alias;
188
    }
189
190
    /**
191
     * @return string
192
     */
193
    public function getAlias(): string
194
    {
195
        return $this->alias;
196
    }
197
198
    /**
199
     * @param QueryBuilder         $queryBuilder
200
     * @param Filter|QueryModifier $specification
201
     * @param string|null          $alias
202
     *
203
     * @throws \InvalidArgumentException
204
     */
205
    protected function applySpecification(
206
        QueryBuilder $queryBuilder,
207
        $specification = null,
208
        ?string $alias = null
209
    ): void {
210
        if ($specification instanceof QueryModifier) {
211
            $specification->modify($queryBuilder, $alias ?: $this->getAlias());
212
        }
213
214
        if ($specification instanceof Filter) {
215
            $filter = $specification->getFilter($queryBuilder, $alias ?: $this->getAlias());
216
            $filter = trim($filter);
217
218
            if ('' !== $filter) {
219
                $queryBuilder->andWhere($filter);
220
            }
221
        }
222
    }
223
}
224