Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 54
0.00% covered (danger)
0.00%
0 / 22
CRAP
0.00% covered (danger)
0.00%
0 / 1
common_persistence_sql_Filter
0.00% covered (danger)
0.00%
0 / 54
0.00% covered (danger)
0.00%
0 / 22
650
0.00% covered (danger)
0.00%
0 / 1
 getSortBy
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setSortBy
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getSortOrder
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setSortOrder
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getLimit
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setLimit
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getOffset
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setOffset
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getFilters
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 addFilter
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 applyFilters
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 eq
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 neq
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 lt
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 lte
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 gt
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 gte
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 like
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 notLike
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 in
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 notIn
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 assertValidOperator
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3/**
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; under version 2
7 * of the License (non-upgradable).
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 *
18 * Copyright (c) 2019 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
19 */
20
21use Doctrine\DBAL\Connection;
22use Doctrine\DBAL\Query\QueryBuilder;
23
24/**
25 * Handles the application of filters.
26 *
27 * @author Martijn Swinkels <martijn@taotesting.com>
28 */
29class common_persistence_sql_Filter
30{
31    public const OP_EQ  = '=';
32    public const OP_NEQ = '!=';
33    public const OP_LT  = '<';
34    public const OP_LTE = '<=';
35    public const OP_GT  = '>';
36    public const OP_GTE = '>=';
37    public const OP_LIKE = 'LIKE';
38    public const OP_NOT_LIKE = 'NOT LIKE';
39    public const OP_IN = 'IN';
40    public const OP_NOT_IN = 'NOT IN';
41
42    /**
43     * @var array
44     */
45    private $filters = [];
46
47    /**
48     * @var int
49     */
50    private $limit;
51
52    /**
53     * @var int
54     */
55    private $offset;
56
57    /**
58     * @var string
59     */
60    private $sortBy;
61
62    /**
63     * @var string
64     */
65    private $sortOrder;
66
67    /**
68     * @return mixed
69     */
70    public function getSortBy()
71    {
72        return $this->sortBy;
73    }
74
75    /**
76     * @param mixed $sortBy
77     * @return common_persistence_sql_Filter
78     */
79    public function setSortBy($sortBy)
80    {
81        $this->sortBy = $sortBy;
82        return $this;
83    }
84
85    /**
86     * @return string
87     */
88    public function getSortOrder()
89    {
90        return $this->sortOrder;
91    }
92
93    /**
94     * @param string $sortOrder
95     * @return common_persistence_sql_Filter
96     */
97    public function setSortOrder($sortOrder)
98    {
99        $this->sortOrder = $sortOrder;
100        return $this;
101    }
102
103    /**
104     * @return int
105     */
106    public function getLimit()
107    {
108        return $this->limit;
109    }
110
111    /**
112     * @param int $limit
113     * @return common_persistence_sql_Filter
114     */
115    public function setLimit($limit)
116    {
117        $this->limit = max(0, $limit);
118        return $this;
119    }
120
121    /**
122     * @return int
123     */
124    public function getOffset()
125    {
126        return $this->offset;
127    }
128
129    /**
130     * @param int $offset
131     * @return common_persistence_sql_Filter
132     */
133    public function setOffset($offset)
134    {
135        $this->offset = max(0, $offset);
136        return $this;
137    }
138
139    /**
140     * @return array
141     */
142    public function getFilters()
143    {
144        return $this->filters;
145    }
146
147    /**
148     * Add a filter.
149     *
150     * @param string $field
151     * @param string $operator
152     * @param mixed $value
153     * @return common_persistence_sql_Filter
154     */
155    public function addFilter($field, $operator, $value)
156    {
157        $this->assertValidOperator($operator);
158
159        $this->filters[] =  [
160            'column' => (string) $field,
161            'valuePlaceholder' => uniqid(':' . $field),
162            'operator' => $operator,
163            'value' => $value
164        ];
165
166        return $this;
167    }
168
169    /**
170     * Apply the filters
171     *
172     * @param QueryBuilder $qb
173     * @return QueryBuilder
174     */
175    public function applyFilters(QueryBuilder $qb)
176    {
177        foreach ($this->getFilters() as $filter) {
178            $type = null;
179            $placeholder = $filter['valuePlaceholder'];
180            if (is_array($filter['value'])) {
181                $type = Connection::PARAM_STR_ARRAY;
182                $filter['valuePlaceholder'] = '(' . $placeholder . ')';
183            }
184
185            $qb->andWhere($filter['column'] . ' ' . $filter['operator'] . ' ' . $filter['valuePlaceholder'])
186                ->setParameter($placeholder, $filter['value'], $type);
187        }
188
189        return $qb;
190    }
191
192    /**
193     * Add an "equals" filter
194     *
195     * @param string $field
196     * @param mixed $value
197     * @return common_persistence_sql_Filter
198     */
199    public function eq($field, $value)
200    {
201        return $this->addFilter($field, self::OP_EQ, $value);
202    }
203
204    /**
205     * Add a "not equals" filter
206     *
207     * @param string $field
208     * @param mixed $value
209     * @return common_persistence_sql_Filter
210     */
211    public function neq($field, $value)
212    {
213        return $this->addFilter($field, self::OP_NEQ, $value);
214    }
215
216    /**
217     * Add a "lower than" filter
218     *
219     * @param string $field
220     * @param mixed $value
221     * @return common_persistence_sql_Filter
222     */
223    public function lt($field, $value)
224    {
225        return $this->addFilter($field, self::OP_LT, $value);
226    }
227
228    /**
229     * Add a "lower than or equals" filter
230     *
231     * @param string $field
232     * @param mixed $value
233     * @return common_persistence_sql_Filter
234     */
235    public function lte($field, $value)
236    {
237        return $this->addFilter($field, self::OP_LTE, $value);
238    }
239
240    /**
241     * Add a "greater than" filter
242     *
243     * @param string $field
244     * @param mixed $value
245     * @return common_persistence_sql_Filter
246     */
247    public function gt($field, $value)
248    {
249        return $this->addFilter($field, self::OP_GT, $value);
250    }
251
252    /**
253     * Add a "greater than or equals" filter
254     *
255     * @param string $field
256     * @param mixed $value
257     * @return common_persistence_sql_Filter
258     */
259    public function gte($field, $value)
260    {
261        return $this->addFilter($field, self::OP_GTE, $value);
262    }
263
264    /**
265     * Add a "like" filter
266     *
267     * @param string $field
268     * @param mixed $value
269     * @return common_persistence_sql_Filter
270     */
271    public function like($field, $value)
272    {
273        return $this->addFilter($field, self::OP_LIKE, $value);
274    }
275
276    /**
277     * Add a "not like" filter
278     *
279     * @param string $field
280     * @param mixed $value
281     * @return common_persistence_sql_Filter
282     */
283    public function notLike($field, $value)
284    {
285        return $this->addFilter($field, self::OP_NOT_LIKE, $value);
286    }
287
288    /**
289     * Add an "in" filter
290     *
291     * @param string $field
292     * @param array $value
293     * @return common_persistence_sql_Filter
294     */
295    public function in($field, array $value)
296    {
297        return $this->addFilter($field, self::OP_IN, $value);
298    }
299
300    /**
301     * Add a "not in" filter
302     *
303     * @param string $field
304     * @param array $value
305     * @return common_persistence_sql_Filter
306     */
307    public function notIn($field, array $value)
308    {
309        return $this->addFilter($field, self::OP_NOT_IN, $value);
310    }
311
312    /**
313     * @param string $op
314     * @throws InvalidArgumentException
315     */
316    private function assertValidOperator($op)
317    {
318        $operators = [
319            self::OP_EQ,
320            self::OP_NEQ,
321            self::OP_LT,
322            self::OP_LTE,
323            self::OP_GT,
324            self::OP_GTE,
325            self::OP_LIKE,
326            self::OP_NOT_LIKE,
327            self::OP_IN,
328            self::OP_NOT_IN,
329        ];
330
331        if (!in_array(strtoupper($op), $operators, true)) {
332            throw new InvalidArgumentException('Operator "' . $op . '" is not a valid operator.');
333        }
334    }
335}