Passed
Push — master ( b979f8...3829ec )
by Aleksandar
07:20 queued 11s
created

ConditionTrait::buildValueCondition()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 2
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Copyright 2021 Aleksandar Panic
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 *   http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
namespace ArekX\PQL\Drivers\Pdo\MySql\Builders\Traits;
19
20
use ArekX\PQL\Contracts\StructuredQuery;
21
use ArekX\PQL\Drivers\Pdo\MySql\MySqlQueryBuilderState;
22
23
trait ConditionTrait
24
{
25
    use SubQueryTrait;
26
    use WrapValueTrait;
27
    use QuoteNameTrait;
28
29
    /**
30
     * Build a condition into a string
31
     *
32
     * Conditions are recursively parsed.
33
     *
34
     * See where() in conditions trait for options which can be passed here.
35
     *
36
     * @see \ArekX\PQL\Sql\Query\Traits\ConditionTrait::where()
37
     * @param array|StructuredQuery $condition Condition to be built.
38
     * @param MySqlQueryBuilderState $state Query builder state.
39
     * @return string
40
     * @throws \Exception
41
     */
42 35
    protected function buildCondition($condition, MySqlQueryBuilderState $state)
43
    {
44 35
        static $map = null;
45
46 35
        if ($map === null) {
47
            $map = [
48 14
                'all' => fn($condition, $state) => $this->buildAssociativeCondition(' AND ', $condition, $state),
49 7
                'any' => fn($condition, $state) => $this->buildAssociativeCondition(' OR ', $condition, $state),
50 8
                'and' => fn($condition, $state) => $this->buildConjuctionCondition(' AND ', $condition, $state),
51 8
                'or' => fn($condition, $state) => $this->buildConjuctionCondition(' OR ', $condition, $state),
52 8
                'not' => fn($condition, $state) => $this->buildUnaryCondition('NOT', $condition, $state),
53 8
                'exists' => fn($condition, $state) => $this->buildUnaryCondition('EXISTS', $condition, $state),
54 8
                'in' => fn($condition, $state) => $this->buildInCondition($condition, $state),
55 8
                'between' => fn($condition, $state) => $this->buildBetweenCondition($condition, $state),
56 8
                'like' => fn($condition, $state) => $this->buildBinaryCondition(' LIKE ', $condition, $state),
57 10
                '=' => fn($condition, $state) => $this->buildBinaryCondition(' = ', $condition, $state),
58 8
                '>' => fn($condition, $state) => $this->buildBinaryCondition(' > ', $condition, $state),
59 8
                '>=' => fn($condition, $state) => $this->buildBinaryCondition(' >= ', $condition, $state),
60 8
                '<' => fn($condition, $state) => $this->buildBinaryCondition(' < ', $condition, $state),
61 8
                '<=' => fn($condition, $state) => $this->buildBinaryCondition(' <= ', $condition, $state),
62 8
                '<>' => fn($condition, $state) => $this->buildBinaryCondition(' <> ', $condition, $state),
63 8
                '!=' => fn($condition, $state) => $this->buildBinaryCondition(' <> ', $condition, $state),
64 21
                'column' => fn($condition) => $this->buildColumnCondition($condition),
65 22
                'value' => fn($condition, $state) => $this->buildValueCondition($condition, $state),
66
            ];
67
        }
68
69 35
        if ($condition instanceof StructuredQuery) {
70 18
            return $this->buildSubQuery($condition, $state);
71
        }
72
73 33
        if (is_array($condition)) {
74 32
            if (empty($map[$condition[0]])) {
75 1
                throw new \Exception('Unknown condition: ' . var_export($condition[0], true));
76
            }
77
78 31
            return $map[$condition[0]]($condition, $state);
79
        }
80
81 1
        throw new \Exception('Condition must be an array.');
82
    }
83
84
    /**
85
     * Builder for associative condition.
86
     *
87
     * Builds:
88
     * ```
89
     * ['all', ['key' => 'value']]
90
     * ['any', ['key' => 'value']]
91
     * ```
92
     *
93
     * @param string $glue Glue to be used between checks (AND/OR)
94
     * @param array $condition Condition to be parsed
95
     * @param MySqlQueryBuilderState $state Query builder state
96
     * @return string
97
     */
98 12
    protected function buildAssociativeCondition($glue, $condition, MySqlQueryBuilderState $state)
99
    {
100 12
        $result = [];
101 12
        foreach ($condition[1] as $key => $value) {
102 12
            $leftSide = $this->quoteName($key);
103
104 12
            if ($value instanceof StructuredQuery) {
105 2
                $result[] = $leftSide . ' IN ' . $this->buildSubQuery($value, $state);
106 2
                continue;
107
            }
108
109 12
            if (is_array($value)) {
110 2
                $result[] = $leftSide . ' IN (' . $this->buildWrapValue($value, $state) . ')';
111 2
                continue;
112
            }
113
114 12
            $result[] = $leftSide . ' = ' . $this->buildWrapValue($value, $state);
115
        }
116
117 12
        return implode($glue, $result);
118
    }
119
120
    /**
121
     * Builder for conjunction conditions
122
     *
123
     * Builds:
124
     * ```
125
     * ['and', expression1, expression2, ..., expressionN]
126
     * ['or', expression1, expression2, ..., expressionN]
127
     * ```
128
     *
129
     * @param string $glue Glue to be used between checks (AND/OR)
130
     * @param array $condition Condition to be parsed
131
     * @param MySqlQueryBuilderState $state Query builder state
132
     * @return string
133
     */
134 2
    protected function buildConjuctionCondition(string $glue, $condition, MySqlQueryBuilderState $state)
135
    {
136 2
        $result = [];
137
138 2
        $max = count($condition);
139 2
        for ($i = 1; $i < $max; $i++) {
140 2
            $result[] = '(' . $this->buildCondition($condition[$i], $state) . ')';
141
        }
142
143 2
        return implode($glue, $result);
144
    }
145
146
    /**
147
     * Builder for unary conditions
148
     *
149
     * Builds:
150
     * ```
151
     * ['not', expression]
152
     * ['exists', expression]
153
     * ```
154
     *
155
     * @param string $op Resulting operation (NOT/EXISTS)
156
     * @param array $condition Condition to be parsed
157
     * @param MySqlQueryBuilderState $state Query builder state
158
     * @return string
159
     */
160 2
    protected function buildUnaryCondition(string $op, $condition, MySqlQueryBuilderState $state)
161
    {
162 2
        if ($condition[1] instanceof StructuredQuery) {
163 2
            return $op . ' ' . $this->buildSubQuery($condition[1], $state);
164
        }
165
166 2
        return $op . ' (' . $this->buildCondition($condition[1], $state) . ')';
167
    }
168
169
    /**
170
     * Builder for IN condition
171
     *
172
     * Builds:
173
     * ```
174
     * ['in', leftExpression, rightExpression]
175
     * ```
176
     *
177
     * @param array $condition Condition to be parsed
178
     * @param MySqlQueryBuilderState $state Query builder state
179
     * @return string
180
     */
181 1
    protected function buildInCondition($condition, MySqlQueryBuilderState $state)
182
    {
183 1
        $left = $this->buildCondition($condition[1] ?? null, $state);
184
185 1
        $right = $condition[2] ?? null;
186 1
        if ($right instanceof StructuredQuery) {
187 1
            return $left . ' IN ' . $this->buildSubQuery($right, $state);
188
        }
189
190 1
        return $left . ' IN (' . $this->buildCondition($right, $state) . ')';
191
    }
192
193
    /**
194
     * Builder for BETWEEN condition
195
     *
196
     * Builds:
197
     * ```
198
     * ['between', ofExpression, fromExpression, toExpression]
199
     * ```
200
     *
201
     * @param array $condition Condition to be parsed
202
     * @param MySqlQueryBuilderState $state Query builder state
203
     * @return string
204
     */
205 1
    protected function buildBetweenCondition($condition, MySqlQueryBuilderState $state)
206
    {
207 1
        $of = $this->buildCondition($condition[1] ?? null, $state);
208 1
        $from = $this->buildCondition($condition[2] ?? null, $state);
209 1
        $to = $this->buildCondition($condition[3] ?? null, $state);
210
211 1
        return $of . ' BETWEEN ' . $from . ' AND ' . $to;
212
    }
213
214
    /**
215
     * Builder for binary conditions
216
     *
217
     * Builds:
218
     * ```
219
     * ['=', leftExpression, rightExpression]
220
     * ['<', leftExpression, rightExpression]
221
     * ['>', leftExpression, rightExpression]
222
     * ['<=', leftExpression, rightExpression]
223
     * ['>=', leftExpression, rightExpression]
224
     * ['!=', leftExpression, rightExpression]
225
     * ['<>', leftExpression, rightExpression]
226
     * ```
227
     *
228
     * @param string $operation Operation to be used
229
     * @param array $condition Condition to be parsed
230
     * @param MySqlQueryBuilderState $state Query builder state
231
     * @return string
232
     */
233 9
    protected function buildBinaryCondition(string $operation, $condition, MySqlQueryBuilderState $state)
234
    {
235 9
        $left = $this->buildCondition($condition[1] ?? null, $state);
236 9
        $right = $this->buildCondition($condition[2] ?? null, $state);
237
238 9
        return $left . $operation . $right;
239
    }
240
241
    /**
242
     * Builder for column
243
     *
244
     * Builds:
245
     * ```
246
     * ['column', string]
247
     * ```
248
     *
249
     * @param array $condition Condition to be parsed
250
     * @return string
251
     */
252 14
    protected function buildColumnCondition($condition)
253
    {
254 14
        $column = $condition[1] ?? null;
255
256 14
        if (empty($column)) {
257 1
            throw new \Exception('Column name must be set.');
258 13
        } else if (!is_string($column)) {
259 1
            throw new \Exception('Column name must be a string.');
260
        }
261
262 12
        return $this->quoteName($column);
263
    }
264
265
266
    /**
267
     * Builder for value (user input)
268
     *
269
     * Builds:
270
     * ```
271
     * ['value', anyValue, type]
272
     * ```
273
     *
274
     * @param array $condition Condition to be parsed
275
     * @param MySqlQueryBuilderState $state Query builder state
276
     * @return string
277
     */
278 16
    protected function buildValueCondition($condition, MySqlQueryBuilderState $state)
279
    {
280 16
        return $this->buildWrapValue(
281 16
            $condition[1] ?? null,
282
            $state,
283 16
            $condition[2] ?? null
284
        );
285
    }
286
}