Completed
Push — master ( 8967da...2226ff )
by Jared
02:10
created

WhereStatement::getConditions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
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
        }
170
171
        // handle array values, i.e. for IN conditions
172
        if (is_array($cond[2])) {
173
            foreach ($cond[2] as &$value) {
174
                $value = $this->parameterize($value);
175
            }
176
            $cond[2] = '('.implode(',', $cond[2]).')';
177
178
        // otherwise parameterize the value
179
        } else {
180
            $cond[2] = $this->parameterize($cond[2]);
181
        }
182
183
        return implode(' ', $cond);
184
    }
185
186
    public function build()
187
    {
188
        // reset the parameterized values
189
        $this->values = [];
190
191
        // build clause from conditions
192
        $clauses = [];
193
        foreach ($this->conditions as $condition) {
194
            $clauses[] = $this->buildClause($condition);
195
        }
196
197
        // remove empty values
198
        $clauses = array_filter($clauses);
199
200
        if (count($clauses) == 0) {
201
            return '';
202
        }
203
204
        $sql = (!$this->having) ? 'WHERE ' : 'HAVING ';
205
206
        return $sql.implode(' AND ', $clauses);
207
    }
208
}
209