Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
43 / 43
100.00% covered (success)
100.00%
8 / 8
CRAP
100.00% covered (success)
100.00%
1 / 1
ConditionBuilder
100.00% covered (success)
100.00%
43 / 43
100.00% covered (success)
100.00%
8 / 8
14
100.00% covered (success)
100.00%
1 / 1
 andWith
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 orWith
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 addCondition
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
3
 conditions
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 buildConditionSQL
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 buildPartsSQL
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
5
 getConditionParameters
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasConditions
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace BO\Zmsdb\Query\Builder;
4
5/**
6 * ConditionBuilder
7 *
8 * Builds up a set of conditions for a query, either used in a WHERE or
9 * HAVING block of SQL.
10 *
11 * @package     BO\Zmsdb\Query\Builder
12 * @author      Alex Gisby<alex@solution10.com>
13 * @license     MIT
14 */
15class ConditionBuilder
16{
17    /**
18     * @var     array   "Parts" as in field, op, value
19     */
20    protected $parts = [];
21
22    /**
23     * @var     array   Params (used as a shortcut for feeding into PDO)
24     */
25    protected $params = [];
26
27    /**
28     * Adds an AND condition into the builder
29     *
30     * @param   string|\Closure     $field              Fieldname|callback for group
31     * @param   string              $operator           Operator (=, !=, <>, <= etc)
32     * @param   mixed               $value              Value to test against
33     * @return  $this
34     */
35    public function andWith($field, $operator = null, $value = null)
36    {
37        $this->addCondition('AND', $field, $operator, $value);
38        return $this;
39    }
40
41    /**
42     * Adds an OR condition into the builder
43     *
44     * @param   string|\Closure     $field              Fieldname|callback for group
45     * @param   string              $operator           Operator (=, !=, <>, <= etc)
46     * @param   mixed               $value              Value to test against
47     * @return  $this
48     */
49    public function orWith($field, $operator = null, $value = null)
50    {
51        $this->addCondition('OR', $field, $operator, $value);
52        return $this;
53    }
54
55    /**
56     * Adds the condition into the family we're building.
57     *
58     * @param   string              $join               AND or OR
59     * @param   string|\Closure     $field              Fieldname|callback for group
60     * @param   string              $operator           Operator (=, !=, <>, <= etc)
61     * @param   mixed               $value              Value to test against
62     * @return  $this               $this on set, array on get
63     */
64    protected function addCondition($join, $field, $operator, $value)
65    {
66        if ($field instanceof \Closure) {
67            // Return and merge the result of these queries
68            $subQuery = new ConditionBuilder();
69            $field($subQuery);
70            $this->parts[] = [
71                'join' => $join,
72                'sub' => $subQuery->conditions()
73            ];
74            $newParams = $subQuery->getConditionParameters();
75//            $this->params = array_merge($this->params, $subQuery->getConditionParameters());
76        } else {
77            $this->parts[] = [
78                'join' => $join,
79                'field' => $field,
80                'operator' => $operator,
81                'value' => $value
82            ];
83            $newParams = $value;
84        }
85
86        if (!is_array($newParams)) {
87            $newParams = [$newParams];
88        }
89
90        $this->params = array_merge($this->params, $newParams);
91
92        return $this;
93    }
94
95    /**
96     * Returns the conditions that have been set on this builder.
97     *
98     * @return  array
99     */
100    public function conditions()
101    {
102        return $this->parts;
103    }
104
105    /**
106     * Builds up the SQL for this condition.
107     *
108     * @param   DialectInterface    $dialect
109     * @return  string
110     */
111    public function buildConditionSQL(DialectInterface $dialect)
112    {
113        return $this->buildPartsSQL($this->parts, $dialect);
114    }
115
116    /**
117     * Builds up an array of parts into a SQL string. To be used recursively.
118     *
119     * @param   DialectInterface    $dialect
120     * @param   array               $parts
121     * @return  string
122     */
123    protected function buildPartsSQL(array $parts, DialectInterface $dialect)
124    {
125        $where = '';
126        foreach ($parts as $c) {
127            $where .= ' ' . $c['join'] . ' ';
128            if (array_key_exists('sub', $c)) {
129                $where .= '(';
130                $where .= $this->buildPartsSQL($c['sub'], $dialect);
131                $where .= ')';
132            } else {
133                $where .= $dialect->quoteField($c['field']) . ' ' . $c['operator'] . ' ';
134                if (is_array($c['value'])) {
135                    $inParts = [];
136                    for ($i = 0; $i < count($c['value']); $i++) {
137                        $inParts[] = '?';
138                    }
139                    $where .= '(' . implode(', ', $inParts) . ')';
140                } else {
141                    $where .= '?';
142                }
143            }
144        }
145        $where = trim(preg_replace('/^(AND|OR) /', '', trim($where)));
146        return $where;
147    }
148
149    /**
150     * Returns the parameters from this set of conditions ready to throw at a PDO statement
151     *
152     * @return  array
153     */
154    public function getConditionParameters()
155    {
156        return $this->params;
157    }
158
159    /**
160     * Returns whether any conditions have been added to this builder
161     *
162     * @return  bool
163     */
164    public function hasConditions()
165    {
166        return !empty($this->parts);
167    }
168}