Total Complexity | 20 |
Total Lines | 262 |
Duplicated Lines | 0 % |
Coverage | 100% |
Changes | 1 | ||
Bugs | 0 | Features | 0 |
1 | <?php |
||
25 | trait ConditionTrait |
||
26 | { |
||
27 | use SubQueryTrait; |
||
28 | use WrapValueTrait; |
||
29 | use QuoteNameTrait; |
||
30 | |||
31 | /** |
||
32 | * Build a condition into a string |
||
33 | * |
||
34 | * Conditions are recursively parsed. |
||
35 | * |
||
36 | * See where() in conditions trait for options which can be passed here. |
||
37 | * |
||
38 | * @param array|StructuredQuery $condition Condition to be built. |
||
39 | * @param MySqlQueryBuilderState $state Query builder state. |
||
40 | * @return string |
||
41 | * @throws Exception |
||
42 | * @see \ArekX\PQL\Sql\Query\Traits\ConditionTrait::where() |
||
43 | */ |
||
44 | 34 | protected function buildCondition(StructuredQuery|array $condition, MySqlQueryBuilderState $state): string |
|
45 | { |
||
46 | 34 | static $map = null; |
|
47 | |||
48 | 34 | if ($map === null) { |
|
49 | 34 | $map = [ |
|
50 | 34 | 'all' => fn($condition, $state) => $this->buildAssociativeCondition(' AND ', $condition, $state), |
|
51 | 34 | 'any' => fn($condition, $state) => $this->buildAssociativeCondition(' OR ', $condition, $state), |
|
52 | 34 | 'and' => fn($condition, $state) => $this->buildConjunctionCondition(' AND ', $condition, $state), |
|
53 | 34 | 'or' => fn($condition, $state) => $this->buildConjunctionCondition(' OR ', $condition, $state), |
|
54 | 34 | 'not' => fn($condition, $state) => $this->buildUnaryCondition('NOT', $condition, $state), |
|
55 | 34 | 'exists' => fn($condition, $state) => $this->buildUnaryCondition('EXISTS', $condition, $state), |
|
56 | 34 | 'in' => fn($condition, $state) => $this->buildInCondition($condition, $state), |
|
57 | 34 | 'between' => fn($condition, $state) => $this->buildBetweenCondition($condition, $state), |
|
58 | 34 | 'like' => fn($condition, $state) => $this->buildBinaryCondition(' LIKE ', $condition, $state), |
|
59 | 34 | '=' => fn($condition, $state) => $this->buildBinaryCondition(' = ', $condition, $state), |
|
60 | 34 | '>' => fn($condition, $state) => $this->buildBinaryCondition(' > ', $condition, $state), |
|
61 | 34 | '>=' => fn($condition, $state) => $this->buildBinaryCondition(' >= ', $condition, $state), |
|
62 | 34 | '<' => fn($condition, $state) => $this->buildBinaryCondition(' < ', $condition, $state), |
|
63 | 34 | '<=' => fn($condition, $state) => $this->buildBinaryCondition(' <= ', $condition, $state), |
|
64 | 34 | '<>' => fn($condition, $state) => $this->buildBinaryCondition(' <> ', $condition, $state), |
|
65 | 34 | '!=' => fn($condition, $state) => $this->buildBinaryCondition(' <> ', $condition, $state), |
|
66 | 34 | 'column' => fn($condition) => $this->buildColumnCondition($condition), |
|
67 | 34 | 'value' => fn($condition, $state) => $this->buildValueCondition($condition, $state), |
|
68 | 34 | ]; |
|
69 | } |
||
70 | |||
71 | 34 | if ($condition instanceof StructuredQuery) { |
|
1 ignored issue
–
show
|
|||
72 | 18 | return $this->buildSubQuery($condition, $state); |
|
73 | } |
||
74 | |||
75 | 32 | if (empty($map[$condition[0]])) { |
|
76 | 1 | throw new UnexpectedValueException('Unknown condition: ' . var_export($condition[0], true)); |
|
77 | } |
||
78 | |||
79 | 31 | return $map[$condition[0]]($condition, $state); |
|
80 | } |
||
81 | |||
82 | /** |
||
83 | * Builder for associative condition. |
||
84 | * |
||
85 | * Builds: |
||
86 | * ``` |
||
87 | * ['all', ['key' => 'value']] |
||
88 | * ['any', ['key' => 'value']] |
||
89 | * ``` |
||
90 | * |
||
91 | * @param string $glue Glue to be used between checks (AND/OR) |
||
92 | * @param array $condition Condition to be parsed |
||
93 | * @param MySqlQueryBuilderState $state Query builder state |
||
94 | * @return string |
||
95 | */ |
||
96 | 12 | protected function buildAssociativeCondition(string $glue, array $condition, MySqlQueryBuilderState $state): string |
|
97 | { |
||
98 | 12 | $result = []; |
|
99 | 12 | foreach ($condition[1] as $key => $value) { |
|
100 | 12 | $leftSide = $this->quoteName($key); |
|
101 | |||
102 | 12 | if ($value instanceof StructuredQuery) { |
|
103 | 2 | $result[] = $leftSide . ' IN ' . $this->buildSubQuery($value, $state); |
|
104 | 2 | continue; |
|
105 | } |
||
106 | |||
107 | 12 | if (is_array($value)) { |
|
108 | 2 | $result[] = $leftSide . ' IN (' . $this->buildWrapValue($value, $state) . ')'; |
|
109 | 2 | continue; |
|
110 | } |
||
111 | |||
112 | 12 | $result[] = $leftSide . ' = ' . $this->buildWrapValue($value, $state); |
|
113 | } |
||
114 | |||
115 | 12 | return implode($glue, $result); |
|
116 | } |
||
117 | |||
118 | /** |
||
119 | * Builder for conjunction conditions |
||
120 | * |
||
121 | * Builds: |
||
122 | * ``` |
||
123 | * ['and', expression1, expression2, ..., expressionN] |
||
124 | * ['or', expression1, expression2, ..., expressionN] |
||
125 | * ``` |
||
126 | * |
||
127 | * @param string $glue Glue to be used between checks (AND/OR) |
||
128 | * @param array $condition Condition to be parsed |
||
129 | * @param MySqlQueryBuilderState $state Query builder state |
||
130 | * @return string |
||
131 | * @throws Exception |
||
132 | */ |
||
133 | 2 | protected function buildConjunctionCondition(string $glue, array $condition, MySqlQueryBuilderState $state): string |
|
134 | { |
||
135 | 2 | $result = []; |
|
136 | |||
137 | 2 | $max = count($condition); |
|
138 | 2 | for ($i = 1; $i < $max; $i++) { |
|
139 | 2 | $result[] = '(' . $this->buildCondition($condition[$i], $state) . ')'; |
|
140 | } |
||
141 | |||
142 | 2 | return implode($glue, $result); |
|
143 | } |
||
144 | |||
145 | /** |
||
146 | * Builder for unary conditions |
||
147 | * |
||
148 | * Builds: |
||
149 | * ``` |
||
150 | * ['not', expression] |
||
151 | * ['exists', expression] |
||
152 | * ``` |
||
153 | * |
||
154 | * @param string $op Resulting operation (NOT/EXISTS) |
||
155 | * @param array $condition Condition to be parsed |
||
156 | * @param MySqlQueryBuilderState $state Query builder state |
||
157 | * @return string |
||
158 | * @throws Exception |
||
159 | */ |
||
160 | 2 | protected function buildUnaryCondition(string $op, array $condition, MySqlQueryBuilderState $state): string |
|
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 | * @throws Exception |
||
181 | */ |
||
182 | 1 | protected function buildInCondition(array $condition, MySqlQueryBuilderState $state): string |
|
183 | { |
||
184 | 1 | $left = $this->buildCondition($condition[1] ?? null, $state); |
|
1 ignored issue
–
show
|
|||
185 | |||
186 | 1 | $right = $condition[2] ?? null; |
|
187 | 1 | if ($right instanceof StructuredQuery) { |
|
188 | 1 | return $left . ' IN ' . $this->buildSubQuery($right, $state); |
|
189 | } |
||
190 | |||
191 | 1 | return $left . ' IN (' . $this->buildCondition($right, $state) . ')'; |
|
192 | } |
||
193 | |||
194 | /** |
||
195 | * Builder for BETWEEN condition |
||
196 | * |
||
197 | * Builds: |
||
198 | * ``` |
||
199 | * ['between', ofExpression, fromExpression, toExpression] |
||
200 | * ``` |
||
201 | * |
||
202 | * @param array $condition Condition to be parsed |
||
203 | * @param MySqlQueryBuilderState $state Query builder state |
||
204 | * @return string |
||
205 | * @throws Exception |
||
206 | */ |
||
207 | 1 | protected function buildBetweenCondition(array $condition, MySqlQueryBuilderState $state): string |
|
208 | { |
||
209 | 1 | $of = $this->buildCondition($condition[1] ?? null, $state); |
|
1 ignored issue
–
show
|
|||
210 | 1 | $from = $this->buildCondition($condition[2] ?? null, $state); |
|
211 | 1 | $to = $this->buildCondition($condition[3] ?? null, $state); |
|
212 | |||
213 | 1 | return $of . ' BETWEEN ' . $from . ' AND ' . $to; |
|
214 | } |
||
215 | |||
216 | /** |
||
217 | * Builder for binary conditions |
||
218 | * |
||
219 | * Builds: |
||
220 | * ``` |
||
221 | * ['=', leftExpression, rightExpression] |
||
222 | * ['<', leftExpression, rightExpression] |
||
223 | * ['>', leftExpression, rightExpression] |
||
224 | * ['<=', leftExpression, rightExpression] |
||
225 | * ['>=', leftExpression, rightExpression] |
||
226 | * ['!=', leftExpression, rightExpression] |
||
227 | * ['<>', leftExpression, rightExpression] |
||
228 | * ``` |
||
229 | * |
||
230 | * @param string $operation Operation to be used |
||
231 | * @param array $condition Condition to be parsed |
||
232 | * @param MySqlQueryBuilderState $state Query builder state |
||
233 | * @return string |
||
234 | * @throws Exception |
||
235 | */ |
||
236 | 9 | protected function buildBinaryCondition(string $operation, array $condition, MySqlQueryBuilderState $state): string |
|
237 | { |
||
238 | 9 | $left = $this->buildCondition($condition[1] ?? null, $state); |
|
1 ignored issue
–
show
|
|||
239 | 9 | $right = $this->buildCondition($condition[2] ?? null, $state); |
|
240 | |||
241 | 9 | return $left . $operation . $right; |
|
242 | } |
||
243 | |||
244 | /** |
||
245 | * Builder for column |
||
246 | * |
||
247 | * Builds: |
||
248 | * ``` |
||
249 | * ['column', string] |
||
250 | * ``` |
||
251 | * |
||
252 | * @param array $condition Condition to be parsed |
||
253 | * @return string |
||
254 | */ |
||
255 | 14 | protected function buildColumnCondition(array $condition): string |
|
256 | { |
||
257 | 14 | $column = $condition[1] ?? null; |
|
258 | |||
259 | 14 | if (empty($column)) { |
|
260 | 1 | throw new UnexpectedValueException('Column name must be set.'); |
|
261 | 13 | } elseif (!is_string($column)) { |
|
262 | 1 | throw new UnexpectedValueException('Column name must be a string.'); |
|
263 | } |
||
264 | |||
265 | 12 | return $this->quoteName($column); |
|
266 | } |
||
267 | |||
268 | |||
269 | /** |
||
270 | * Builder for value (user input) |
||
271 | * |
||
272 | * Builds: |
||
273 | * ``` |
||
274 | * ['value', anyValue, type] |
||
275 | * ``` |
||
276 | * |
||
277 | * @param array $condition Condition to be parsed |
||
278 | * @param MySqlQueryBuilderState $state Query builder state |
||
279 | * @return string |
||
280 | */ |
||
281 | 16 | protected function buildValueCondition(array $condition, MySqlQueryBuilderState $state): string |
|
287 | 16 | ); |
|
288 | } |
||
289 | } |
||
290 |