Completed
Push — master ( d685b1...8967da )
by Jared
02:15
created

WhereStatement::build()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 22
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
c 4
b 0
f 0
dl 0
loc 22
rs 8.9197
cc 4
eloc 10
nc 6
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
     * Gets the conditions for this statement.
109
     *
110
     * @return array
111
     */
112
    public function getConditions()
113
    {
114
        return $this->conditions;
115
    }
116
117
    /**
118
     * Builds a parameterized and escaped SQL fragment
119
     * for a condition that uses our own internal
120
     * representation.
121
     *
122
     * A condition is represented by an array, and can be
123
     * have one of the following forms:
124
     * i) ['SQL fragment']
125
     * ii) ['identifier', '=', 'value']
126
     *
127
     * @param array $cond
128
     *
129
     * @return string generated SQL fragment
130
     */
131
    protected function buildClause(array $cond)
132
    {
133
        // handle SQL fragments
134
        if (count($cond) == 1) {
135
            return $cond[0];
136
        }
137
138
        // escape the identifier
139
        $cond[0] = $this->escapeIdentifier($cond[0]);
140
        if (empty($cond[0])) {
141
            return '';
142
        }
143
144
        // handle NULL values
145
        if ($cond[1] === '=' && $cond[2] === null) {
146
            return $cond[0].' IS NULL';
147
        }
148
149
        // handle array values, i.e. for IN conditions
150
        if (is_array($cond[2])) {
151
            foreach ($cond[2] as &$value) {
152
                $value = $this->parameterize($value);
153
            }
154
            $cond[2] = '('.implode(',', $cond[2]).')';
155
156
        // otherwise parameterize the value
157
        } else {
158
            $cond[2] = $this->parameterize($cond[2]);
159
        }
160
161
        return implode(' ', $cond);
162
    }
163
164
    public function build()
165
    {
166
        // reset the parameterized values
167
        $this->values = [];
168
169
        // build clause from conditions
170
        $clauses = [];
171
        foreach ($this->conditions as $condition) {
172
            $clauses[] = $this->buildClause($condition);
173
        }
174
175
        // remove empty values
176
        $clauses = array_filter($clauses);
177
178
        if (count($clauses) == 0) {
179
            return '';
180
        }
181
182
        $sql = (!$this->having) ? 'WHERE ' : 'HAVING ';
183
184
        return $sql.implode(' AND ', $clauses);
185
    }
186
}
187