Completed
Push — master ( a07cc3...cef099 )
by Peter
07:38 queued 10s
created

EntitySpecificationRepositoryTrait   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 153
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
dl 0
loc 153
c 0
b 0
f 0
wmc 22
lcom 1
cbo 7
rs 10

8 Methods

Rating   Name   Duplication   Size   Complexity  
A match() 0 6 1
A matchSingleResult() 0 12 3
A matchOneOrNullResult() 0 8 2
A getQuery() 0 12 2
A getQueryBuilder() 0 7 2
A setAlias() 0 6 1
A getAlias() 0 4 1
B applySpecification() 0 25 10
1
<?php
2
3
namespace Happyr\DoctrineSpecification;
4
5
use Doctrine\ORM\NonUniqueResultException;
6
use Doctrine\ORM\NoResultException;
7
use Doctrine\ORM\Query;
8
use Doctrine\ORM\QueryBuilder;
9
use Happyr\DoctrineSpecification\Filter\Filter;
10
use Happyr\DoctrineSpecification\Query\QueryModifier;
11
use Happyr\DoctrineSpecification\Result\ResultModifier;
12
13
/**
14
 * This trait should be used by a class extending \Doctrine\ORM\EntityRepository.
15
 */
16
trait EntitySpecificationRepositoryTrait
17
{
18
    /**
19
     * @var string alias
20
     */
21
    private $alias = 'e';
22
23
    /**
24
     * Get results when you match with a Specification.
25
     *
26
     * @param Filter|QueryModifier $specification
27
     * @param ResultModifier       $modifier
28
     *
29
     * @return mixed[]
30
     */
31
    public function match($specification, ResultModifier $modifier = null)
32
    {
33
        $query = $this->getQuery($specification, $modifier);
34
35
        return $query->execute();
36
    }
37
38
    /**
39
     * Get single result when you match with a Specification.
40
     *
41
     * @param Filter|QueryModifier $specification
42
     * @param ResultModifier       $modifier
43
     *
44
     * @throw Exception\NonUniqueException  If more than one result is found
45
     * @throw Exception\NoResultException   If no results found
46
     *
47
     * @return mixed
48
     */
49
    public function matchSingleResult($specification, ResultModifier $modifier = null)
50
    {
51
        $query = $this->getQuery($specification, $modifier);
52
53
        try {
54
            return $query->getSingleResult();
55
        } catch (NonUniqueResultException $e) {
56
            throw new Exception\NonUniqueResultException($e->getMessage(), $e->getCode(), $e);
57
        } catch (NoResultException $e) {
58
            throw new Exception\NoResultException($e->getMessage(), $e->getCode(), $e);
59
        }
60
    }
61
62
    /**
63
     * Get single result or null when you match with a Specification.
64
     *
65
     * @param Filter|QueryModifier $specification
66
     * @param ResultModifier       $modifier
67
     *
68
     * @throw Exception\NonUniqueException  If more than one result is found
69
     *
70
     * @return mixed|null
71
     */
72
    public function matchOneOrNullResult($specification, ResultModifier $modifier = null)
73
    {
74
        try {
75
            return $this->matchSingleResult($specification, $modifier);
76
        } catch (Exception\NoResultException $e) {
77
            return;
78
        }
79
    }
80
81
    /**
82
     * Prepare a Query with a Specification.
83
     *
84
     * @param Filter|QueryModifier $specification
85
     * @param ResultModifier       $modifier
86
     *
87
     * @return Query
88
     */
89
    public function getQuery($specification, ResultModifier $modifier = null)
90
    {
91
        $qb = $this->createQueryBuilder($this->alias);
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...
92
        $this->applySpecification($qb, $specification);
93
        $query = $qb->getQuery();
94
95
        if (null !== $modifier) {
96
            $modifier->modify($query);
97
        }
98
99
        return $query;
100
    }
101
102
    /**
103
     * @param Filter|QueryModifier $specification
104
     * @param string|null          $alias
105
     *
106
     * @return QueryBuilder
107
     */
108
    public function getQueryBuilder($specification, $alias = null)
109
    {
110
        $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...
111
        $this->applySpecification($qb, $specification, $alias);
112
113
        return $qb;
114
    }
115
116
    /**
117
     * @param string $alias
118
     *
119
     * @return $this
120
     */
121
    public function setAlias($alias)
122
    {
123
        $this->alias = $alias;
124
125
        return $this;
126
    }
127
128
    /**
129
     * @return string
130
     */
131
    public function getAlias()
132
    {
133
        return $this->alias;
134
    }
135
136
    /**
137
     * @param QueryBuilder         $queryBuilder
138
     * @param Filter|QueryModifier $specification
139
     * @param string               $alias
140
     *
141
     * @throws \InvalidArgumentException
142
     */
143
    protected function applySpecification(QueryBuilder $queryBuilder, $specification = null, $alias = null)
144
    {
145
        if (null === $specification) {
146
            return;
147
        }
148
149
        if (!$specification instanceof QueryModifier && !$specification instanceof Filter) {
150
            throw new \InvalidArgumentException(sprintf(
151
                'Expected argument of type "%s" or "%s", "%s" given.',
152
                'Happyr\DoctrineSpecification\Query\QueryModifier',
153
                'Happyr\DoctrineSpecification\Filter\Filter',
154
                is_object($specification) ? get_class($specification) : gettype($specification)
155
            ));
156
        }
157
158
        if ($specification instanceof QueryModifier) {
159
            $specification->modify($queryBuilder, $alias ?: $this->getAlias());
160
        }
161
162
        if ($specification instanceof Filter
163
            && $filter = (string) $specification->getFilter($queryBuilder, $alias ?: $this->getAlias())
164
        ) {
165
            $queryBuilder->andWhere($filter);
166
        }
167
    }
168
}
169