ProxyQueryBuilder::getConditionExpr()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.1406

Importance

Changes 5
Bugs 0 Features 1
Metric Value
cc 3
eloc 7
c 5
b 0
f 1
nc 3
nop 2
dl 0
loc 14
ccs 6
cts 8
cp 0.75
crap 3.1406
rs 10
1
<?php declare(strict_types=1);
2
3
namespace Artprima\QueryFilterBundle\Query;
4
5
use Artprima\QueryFilterBundle\Exception\InvalidArgumentException;
6
use Artprima\QueryFilterBundle\Query\Condition\ConditionInterface;
7
use Doctrine\ORM\QueryBuilder;
8
use Doctrine\ORM\Query as DoctrineQuery;
9
10
/**
11
 * Class ProxyQueryBuilder
12
 *
13
 * @author Denis Voytyuk <[email protected]>
14
 */
15
class ProxyQueryBuilder
16
{
17
    /**
18
     * @var QueryBuilder
19
     */
20
    private $queryBuilder;
21
22
    /**
23
     * @var ConditionManager
24
     */
25
    private $conditionManager;
26
27 9
    public function __construct(QueryBuilder $queryBuilder, ConditionManager $conditionManager)
28
    {
29 9
        $this->queryBuilder = $queryBuilder;
30 9
        $this->conditionManager = $conditionManager;
31 9
    }
32
33
    /**
34
     * @param int $index parameter id
35
     * @param Filter $filter
36
     * @return DoctrineQuery\Expr\Comparison|DoctrineQuery\Expr\Func|string
37
     */
38 6
    private function getConditionExpr(int $index, Filter $filter)
39
    {
40 6
        if (!$this->conditionManager->offsetExists($filter->getType())) {
41
            throw new InvalidArgumentException(sprintf('Condition "%s" is not registered', $filter->getType()));
42
        }
43
44 6
        $conditionManager = $this->conditionManager[$filter->getType()];
45 6
        if (!$conditionManager instanceof ConditionInterface) {
46
            throw new InvalidArgumentException(sprintf('Condition "%s" is of invalid type "%s"', $filter->getType(), gettype($this->conditionManager[$filter->getType()])));
47
        }
48
49 6
        $expr = $conditionManager->getExpr($this->queryBuilder, $index, $filter);
50
51 6
        return $expr;
52
    }
53
54
    /**
55
     * Get connector expression based on `and`, `or` or `null`
56
     *
57
     * @param $prev
58
     * @param $connector
59
     * @param $condition
60
     * @return DoctrineQuery\Expr\Andx|DoctrineQuery\Expr\Orx
61
     * @throws InvalidArgumentException
62
     */
63 6
    private function getConnectorExpr($prev, $connector, $condition)
64
    {
65 6
        $qb = $this->queryBuilder;
66
67 6
        if ($prev === null) {
68 6
            $expr = $condition;
69 3
        } elseif ($connector === null || $connector === 'and') {
70 1
            $expr = $qb->expr()->andX($prev, $condition);
71 2
        } elseif ($connector === 'or') {
72 2
            $expr = $qb->expr()->orX($prev, $condition);
73
        } else {
74
            throw new InvalidArgumentException(sprintf('Wrong connector type: %s', $connector));
75
        }
76
77 6
        return $expr;
78
    }
79
80 8
    private function addQueryFilters(QueryBuilder $qb, array $filterBy): QueryBuilder
81
    {
82 8
        if (empty($filterBy)) {
83 2
            return $qb;
84
        }
85
86 6
        $i = 0;
87 6
        $where = null;
88 6
        $having = null;
89
90
        /** @var Filter $val */
91 6
        foreach ($filterBy as $val) {
92 6
            if (!($val instanceof Filter)) {
93
                throw new InvalidArgumentException(sprintf('Unexpected val php type ("%s")', gettype($val)));
94
            }
95
96 6
            $i++;
97
98 6
            $condition = $this->getConditionExpr($i, $val);
99
100 6
            if ($val->isHaving()) {
101 2
                $having = $this->getConnectorExpr($having, $val->getConnector() ?? 'and', $condition);
102
            } else {
103 5
                $where = $this->getConnectorExpr($where, $val->getConnector() ?? 'and', $condition);
104
            }
105
        }
106
107 6
        if ($where) {
108 5
            $qb->add('where', $where);
109
        }
110
111 6
        if ($having) {
112 2
            $qb->add('having', $having);
113
        }
114
115 6
        return $qb;
116
    }
117
118
    /**
119
     * Add filter and order by conditions to the given QueryBuilder
120
     *
121
     * Example data
122
     *
123
     * array(
124
     *  'searchBy' => array(
125
     *    'e.name' => array(
126
     *      'type' => 'like',
127
     *      'val' => 'a',
128
     *    ),
129
     *    'e.city' => array(
130
     *      'type' => 'like',
131
     *      'val' => 'd',
132
     *    ),
133
     *    'c.name' => array(
134
     *      'type' => 'like',
135
     *      'val' => 'a',
136
     *    ),
137
     *    'concat(concat(concat(concat(p.firstname, ' '), p.middlename), ' '), p.lastname)' => array(
138
     *      'having' => TRUE
139
     *      'type' => 'like'
140
     *      'val' => 'a'
141
     *    )
142
     *    'year' => array(
143
     *      'type' => 'between',
144
     *      'val' => 2015,
145
     *      'x' => 'YEAR(e.startDate)',
146
     *      'y' => 'YEAR(e.endDate)'
147
     *    ),
148
     *  ),
149
     *  'sortData' => array(
150
     *      'e.name' => 'asc'
151
     *  )
152
     * )
153
     *
154
     * @param array $filterBy
155
     * @param array $orderBy
156
     *
157
     * @return QueryBuilder
158
     */
159 8
    public function getSortedAndFilteredQueryBuilder(array $filterBy, array $orderBy): QueryBuilder
160
    {
161 8
        $qb = $this->queryBuilder;
162
163 8
        foreach ($orderBy as $field => $dir) {
164 7
            $qb->addOrderBy($field, strtoupper($dir));
165
        }
166
167 8
        return $this->addQueryFilters($qb, $filterBy);
168
    }
169
}
170