Completed
Push — master ( 7eba35...c19f38 )
by Jared
02:07
created

WhereStatement::addBetweenCondition()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 6
rs 9.4285
cc 1
eloc 3
nc 1
nop 3
1
<?php
2
3
/**
4
 * @author Jared King <[email protected]>
5
 *
6
 * @link http://jaredtking.com
7
 *
8
 * @copyright 2015 Jared King
9
 * @license MIT
10
 */
11
namespace JAQB\Statement;
12
13
class WhereStatement extends Statement
14
{
15
    /**
16
     * @var bool
17
     */
18
    protected $having;
19
20
    /**
21
     * @var array
22
     */
23
    protected $conditions = [];
24
25
    /**
26
     * @param bool $having when true, statement becomes a having statement
27
     */
28
    public function __construct($having = false)
29
    {
30
        $this->having = $having;
31
    }
32
33
    /**
34
     * Tells whether this statement is a HAVING statement.
35
     *
36
     * @return bool true: is HAVING, false: is WHERE
37
     */
38
    public function isHaving()
39
    {
40
        return $this->having;
41
    }
42
43
    /**
44
     * Adds a condition to the statement.
45
     *
46
     * Accepts the following forms:
47
     * 1. Equality comparison:
48
     *      addCondition('username', 'john')
49
     * 2. Comparison with custom operator:
50
     *      addCondition('balance', 100, '>')
51
     * 3. IN statement:
52
     *      addCondition('group', ['admin', 'owner'])
53
     * 4. SQL fragment:
54
     *      addCondition('name LIKE "%john%"')
55
     * 5. List of conditions to add:
56
     *      addCondition([['balance', 100, '>'],
57
     *                    ['user_id', 5]])
58
     * 6. Map of equality comparisons:
59
     *      addCondition(['username' => 'john',
60
     *                    'user_id' => 5])
61
     * 7. List of SQL fragments:
62
     *      addCondition(['first_name LIKE "%john%"',
63
     *                    'last_name LIKE "%doe%"'])
64
     *
65
     * @param array|string $field
66
     * @param string|bool  $value    condition value (optional)
67
     * @param string       $operator operator (optional)
68
     *
69
     * @return self
70
     */
71
    public function addCondition($field, $value = false, $operator = '=')
72
    {
73
        if (is_array($field) && !$value) {
74
            foreach ($field as $key => $value) {
75
                // handles #5
76
                if (is_array($value)) {
77
                    call_user_func_array([$this, 'addCondition'], $value);
78
                // handles #6
79
                } elseif (!is_numeric($key)) {
80
                    $this->addCondition($key, $value);
81
                // handles #7
82
                } else {
83
                    $this->addCondition($value);
84
                }
85
            }
86
        } else {
87
            // handles #4
88
            $condition = [$field];
89
90
            if (func_num_args() >= 2) {
91
                // handles #3
92
                if (is_array($value)) {
93
                    $operator = 'IN';
94
                }
95
96
                // handles #1 and #2
97
                $condition[] = $operator;
98
                $condition[] = $value;
99
            }
100
101
            $this->conditions[] = $condition;
102
        }
103
104
        return $this;
105
    }
106
107
    /**
108
     * Adds a between condition to the query.
109
     *
110
     * @param string $field
111
     * @param mixed  $a     first between value
112
     * @param mixed  $b     second between value
113
     *
114
     * @return self
115
     */
116
    public function addBetweenCondition($field, $a, $b)
117
    {
118
        $this->conditions[] = [$field, 'BETWEEN', $a, $b];
119
120
        return $this;
121
    }
122
123
    /**
124
     * Gets the conditions for this statement.
125
     *
126
     * @return array
127
     */
128
    public function getConditions()
129
    {
130
        return $this->conditions;
131
    }
132
133
    /**
134
     * Builds a parameterized and escaped SQL fragment
135
     * for a condition that uses our own internal
136
     * representation.
137
     *
138
     * A condition is represented by an array, and can be
139
     * have one of the following forms:
140
     * i)   ['SQL fragment']
141
     * ii)  ['identifier', '=', 'value']
142
     * iii) ['identifier', 'BETWEEN', 'value', 'value']
143
     *
144
     * @param array $cond
145
     *
146
     * @return string generated SQL fragment
147
     */
148
    protected function buildClause(array $cond)
149
    {
150
        // handle SQL fragments
151
        if (count($cond) == 1) {
152
            return $cond[0];
153
        }
154
155
        // escape the identifier
156
        $cond[0] = $this->escapeIdentifier($cond[0]);
157
        if (empty($cond[0])) {
158
            return '';
159
        }
160
161
        // handle between conditions
162
        if ($cond[1] === 'BETWEEN') {
163
            return $cond[0].' BETWEEN '.$this->parameterize($cond[2]).' AND '.$this->parameterize($cond[3]);
164
        }
165
166
        // handle NULL values
167
        if ($cond[1] === '=' && $cond[2] === null) {
168
            return $cond[0].' IS NULL';
169
        } elseif ($cond[1] === '<>' && $cond[2] === null) {
170
            return $cond[0].' IS NOT NULL';
171
        }
172
173
        // handle array values, i.e. for IN conditions
174
        if (is_array($cond[2])) {
175
            foreach ($cond[2] as &$value) {
176
                $value = $this->parameterize($value);
177
            }
178
            $cond[2] = '('.implode(',', $cond[2]).')';
179
180
        // otherwise parameterize the value
181
        } else {
182
            $cond[2] = $this->parameterize($cond[2]);
183
        }
184
185
        return implode(' ', $cond);
186
    }
187
188
    public function build()
189
    {
190
        // reset the parameterized values
191
        $this->values = [];
192
193
        // build clause from conditions
194
        $clauses = [];
195
        foreach ($this->conditions as $condition) {
196
            $clauses[] = $this->buildClause($condition);
197
        }
198
199
        // remove empty values
200
        $clauses = array_filter($clauses);
201
202
        if (count($clauses) == 0) {
203
            return '';
204
        }
205
206
        $sql = (!$this->having) ? 'WHERE ' : 'HAVING ';
207
208
        return $sql.implode(' AND ', $clauses);
209
    }
210
}
211