Total Complexity | 160 |
Total Lines | 1486 |
Duplicated Lines | 0 % |
Changes | 9 | ||
Bugs | 0 | Features | 2 |
Complex classes like QueryBuilderHandler often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use QueryBuilderHandler, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
25 | class QueryBuilderHandler implements HasConnection |
||
26 | { |
||
27 | /** |
||
28 | * @method add |
||
29 | */ |
||
30 | use TablePrefixer; |
||
31 | |||
32 | /** |
||
33 | * @var \Viocon\Container |
||
34 | */ |
||
35 | protected $container; |
||
36 | |||
37 | /** |
||
38 | * @var Connection |
||
39 | */ |
||
40 | protected $connection; |
||
41 | |||
42 | /** |
||
43 | * @var array<string, mixed[]|mixed> |
||
44 | */ |
||
45 | protected $statements = []; |
||
46 | |||
47 | /** |
||
48 | * @var wpdb |
||
49 | */ |
||
50 | protected $dbInstance; |
||
51 | |||
52 | /** |
||
53 | * @var string|string[]|null |
||
54 | */ |
||
55 | protected $sqlStatement = null; |
||
56 | |||
57 | /** |
||
58 | * @var string|null |
||
59 | */ |
||
60 | protected $tablePrefix = null; |
||
61 | |||
62 | /** |
||
63 | * @var WPDBAdapter |
||
64 | */ |
||
65 | protected $adapterInstance; |
||
66 | |||
67 | /** |
||
68 | * The mode to return results as. |
||
69 | * Accepts WPDB constants or class names. |
||
70 | * |
||
71 | * @var string |
||
72 | */ |
||
73 | protected $fetchMode; |
||
74 | |||
75 | /** |
||
76 | * Custom args used to construct models for hydrator |
||
77 | * |
||
78 | * @var array<int, mixed>|null |
||
79 | */ |
||
80 | protected $hydratorConstructorArgs; |
||
81 | |||
82 | /** |
||
83 | * Handler for Json Selectors |
||
84 | * |
||
85 | * @var JsonHandler |
||
86 | */ |
||
87 | protected $jsonHandler; |
||
88 | |||
89 | /** |
||
90 | * @param \Pixie\Connection|null $connection |
||
91 | * @param string $fetchMode |
||
92 | * @param mixed[] $hydratorConstructorArgs |
||
93 | * |
||
94 | * @throws Exception if no connection passed and not previously established |
||
95 | */ |
||
96 | final public function __construct( |
||
97 | Connection $connection = null, |
||
98 | string $fetchMode = \OBJECT, |
||
99 | ?array $hydratorConstructorArgs = null |
||
100 | ) { |
||
101 | if (is_null($connection)) { |
||
102 | // throws if connection not already established. |
||
103 | $connection = Connection::getStoredConnection(); |
||
104 | } |
||
105 | |||
106 | // Set all dependencies from connection. |
||
107 | $this->connection = $connection; |
||
108 | $this->container = $this->connection->getContainer(); |
||
109 | $this->dbInstance = $this->connection->getDbInstance(); |
||
110 | $this->setAdapterConfig($this->connection->getAdapterConfig()); |
||
111 | |||
112 | // Set up optional hydration details. |
||
113 | $this->setFetchMode($fetchMode); |
||
114 | $this->hydratorConstructorArgs = $hydratorConstructorArgs; |
||
115 | |||
116 | // Query builder adapter instance |
||
117 | $this->adapterInstance = $this->container->build( |
||
118 | WPDBAdapter::class, |
||
119 | [$this->connection] |
||
120 | ); |
||
121 | |||
122 | // Setup JSON Selector handler. |
||
123 | $this->jsonHandler = new JsonHandler($connection); |
||
124 | } |
||
125 | |||
126 | /** |
||
127 | * Sets the config for WPDB |
||
128 | * |
||
129 | * @param array<string, mixed> $adapterConfig |
||
130 | * |
||
131 | * @return void |
||
132 | */ |
||
133 | protected function setAdapterConfig(array $adapterConfig): void |
||
134 | { |
||
135 | if (isset($adapterConfig[Connection::PREFIX])) { |
||
136 | $this->tablePrefix = $adapterConfig[Connection::PREFIX]; |
||
137 | } |
||
138 | } |
||
139 | |||
140 | /** |
||
141 | * Fetch query results as object of specified type |
||
142 | * |
||
143 | * @param string $className |
||
144 | * @param array<int, mixed> $constructorArgs |
||
145 | * @return static |
||
146 | */ |
||
147 | public function asObject($className, $constructorArgs = array()): self |
||
148 | { |
||
149 | return $this->setFetchMode($className, $constructorArgs); |
||
150 | } |
||
151 | |||
152 | /** |
||
153 | * Set the fetch mode |
||
154 | * |
||
155 | * @param string $mode |
||
156 | * @param array<int, mixed>|null $constructorArgs |
||
157 | * |
||
158 | * @return static |
||
159 | */ |
||
160 | public function setFetchMode(string $mode, ?array $constructorArgs = null): self |
||
161 | { |
||
162 | $this->fetchMode = $mode; |
||
163 | $this->hydratorConstructorArgs = $constructorArgs; |
||
164 | |||
165 | return $this; |
||
166 | } |
||
167 | |||
168 | /** |
||
169 | * @param Connection|null $connection |
||
170 | * |
||
171 | * @return static |
||
172 | * |
||
173 | * @throws Exception |
||
174 | */ |
||
175 | public function newQuery(Connection $connection = null): self |
||
176 | { |
||
177 | if (is_null($connection)) { |
||
178 | $connection = $this->connection; |
||
179 | } |
||
180 | |||
181 | $newQuery = $this->constructCurrentBuilderClass($connection); |
||
182 | $newQuery->setFetchMode($this->getFetchMode(), $this->hydratorConstructorArgs); |
||
183 | |||
184 | return $newQuery; |
||
185 | } |
||
186 | |||
187 | /** |
||
188 | * Returns a new instance of the current, with the passed connection. |
||
189 | * |
||
190 | * @param \Pixie\Connection $connection |
||
191 | * |
||
192 | * @return static |
||
193 | */ |
||
194 | protected function constructCurrentBuilderClass(Connection $connection): self |
||
195 | { |
||
196 | return new static($connection); |
||
197 | } |
||
198 | |||
199 | /** |
||
200 | * Interpolates a query |
||
201 | * |
||
202 | * @param string $query |
||
203 | * @param array<mixed> $bindings |
||
204 | * @return string |
||
205 | */ |
||
206 | public function interpolateQuery(string $query, array $bindings = []): string |
||
207 | { |
||
208 | return $this->adapterInstance->interpolateQuery($query, $bindings); |
||
209 | } |
||
210 | |||
211 | /** |
||
212 | * @param string $sql |
||
213 | * @param array<int,mixed> $bindings |
||
214 | * |
||
215 | * @return static |
||
216 | */ |
||
217 | public function query($sql, $bindings = []): self |
||
218 | { |
||
219 | list($this->sqlStatement) = $this->statement($sql, $bindings); |
||
220 | |||
221 | return $this; |
||
222 | } |
||
223 | |||
224 | /** |
||
225 | * @param string $sql |
||
226 | * @param array<int,mixed> $bindings |
||
227 | * |
||
228 | * @return array{0:string, 1:float} |
||
229 | */ |
||
230 | public function statement(string $sql, $bindings = []): array |
||
231 | { |
||
232 | $start = microtime(true); |
||
233 | $sqlStatement = empty($bindings) ? $sql : $this->interpolateQuery($sql, $bindings); |
||
234 | |||
235 | if (!is_string($sqlStatement)) { |
||
|
|||
236 | throw new Exception('Could not interpolate query', 1); |
||
237 | } |
||
238 | |||
239 | return [$sqlStatement, microtime(true) - $start]; |
||
240 | } |
||
241 | |||
242 | /** |
||
243 | * Get all rows |
||
244 | * |
||
245 | * @return array<mixed,mixed>|null |
||
246 | * |
||
247 | * @throws Exception |
||
248 | */ |
||
249 | public function get() |
||
250 | { |
||
251 | $eventResult = $this->fireEvents('before-select'); |
||
252 | if (!is_null($eventResult)) { |
||
253 | return $eventResult; |
||
254 | } |
||
255 | $executionTime = 0; |
||
256 | if (is_null($this->sqlStatement)) { |
||
257 | $queryObject = $this->getQuery('select'); |
||
258 | $statement = $this->statement( |
||
259 | $queryObject->getSql(), |
||
260 | $queryObject->getBindings() |
||
261 | ); |
||
262 | |||
263 | $this->sqlStatement = $statement[0]; |
||
264 | $executionTime = $statement[1]; |
||
265 | } |
||
266 | |||
267 | $start = microtime(true); |
||
268 | $result = $this->dbInstance()->get_results( |
||
269 | is_array($this->sqlStatement) ? (end($this->sqlStatement) ?: '') : $this->sqlStatement, |
||
270 | // If we are using the hydrator, return as OBJECT and let the hydrator map the correct model. |
||
271 | $this->useHydrator() ? OBJECT : $this->getFetchMode() |
||
272 | ); |
||
273 | $executionTime += microtime(true) - $start; |
||
274 | $this->sqlStatement = null; |
||
275 | |||
276 | // Ensure we have an array of results. |
||
277 | if (!is_array($result) && null !== $result) { |
||
278 | $result = [$result]; |
||
279 | } |
||
280 | |||
281 | // Maybe hydrate the results. |
||
282 | if (null !== $result && $this->useHydrator()) { |
||
283 | $result = $this->getHydrator()->fromMany($result); |
||
284 | } |
||
285 | |||
286 | $this->fireEvents('after-select', $result, $executionTime); |
||
287 | |||
288 | return $result; |
||
289 | } |
||
290 | |||
291 | /** |
||
292 | * Returns a populated instance of the Hydrator. |
||
293 | * |
||
294 | * @return Hydrator |
||
295 | */ |
||
296 | protected function getHydrator(): Hydrator /* @phpstan-ignore-line */ |
||
297 | { |
||
298 | $hydrator = new Hydrator($this->getFetchMode(), $this->hydratorConstructorArgs ?? []); /* @phpstan-ignore-line */ |
||
299 | |||
300 | return $hydrator; |
||
301 | } |
||
302 | |||
303 | /** |
||
304 | * Checks if the results should be mapped via the hydrator |
||
305 | * |
||
306 | * @return bool |
||
307 | */ |
||
308 | protected function useHydrator(): bool |
||
309 | { |
||
310 | return !in_array($this->getFetchMode(), [\ARRAY_A, \ARRAY_N, \OBJECT, \OBJECT_K]); |
||
311 | } |
||
312 | |||
313 | /** |
||
314 | * Find all matching a simple where condition. |
||
315 | * |
||
316 | * Shortcut of ->where('key','=','value')->limit(1)->get(); |
||
317 | * |
||
318 | * @return \stdClass\array<mixed,mixed>|object|null Can return any object using hydrator |
||
319 | */ |
||
320 | public function first() |
||
321 | { |
||
322 | $this->limit(1); |
||
323 | $result = $this->get(); |
||
324 | |||
325 | return empty($result) ? null : $result[0]; |
||
326 | } |
||
327 | |||
328 | /** |
||
329 | * Find all matching a simple where condition. |
||
330 | * |
||
331 | * Shortcut of ->where('key','=','value')->get(); |
||
332 | * |
||
333 | * @param string $fieldName |
||
334 | * @param mixed $value |
||
335 | * |
||
336 | * @return array<mixed,mixed>|null Can return any object using hydrator |
||
337 | */ |
||
338 | public function findAll($fieldName, $value) |
||
339 | { |
||
340 | $this->where($fieldName, '=', $value); |
||
341 | |||
342 | return $this->get(); |
||
343 | } |
||
344 | |||
345 | /** |
||
346 | * @param string $fieldName |
||
347 | * @param mixed $value |
||
348 | * |
||
349 | * @return \stdClass\array<mixed,mixed>|object|null Can return any object using hydrator |
||
350 | */ |
||
351 | public function find($value, $fieldName = 'id') |
||
352 | { |
||
353 | $this->where($fieldName, '=', $value); |
||
354 | |||
355 | return $this->first(); |
||
356 | } |
||
357 | |||
358 | /** |
||
359 | * @param string $fieldName |
||
360 | * @param mixed $value |
||
361 | * |
||
362 | * @return \stdClass\array<mixed,mixed>|object Can return any object using hydrator |
||
363 | * @throws Exception If fails to find |
||
364 | */ |
||
365 | public function findOrFail($value, $fieldName = 'id') |
||
366 | { |
||
367 | $result = $this->find($value, $fieldName); |
||
368 | if (null === $result) { |
||
369 | throw new Exception("Failed to find {$fieldName}={$value}", 1); |
||
370 | } |
||
371 | return $result; |
||
372 | } |
||
373 | |||
374 | /** |
||
375 | * Used to handle all aggregation method. |
||
376 | * |
||
377 | * @see Taken from the pecee-pixie library - https://github.com/skipperbent/pecee-pixie/ |
||
378 | * |
||
379 | * @param string $type |
||
380 | * @param string $field |
||
381 | * |
||
382 | * @return float |
||
383 | */ |
||
384 | protected function aggregate(string $type, string $field = '*'): float |
||
385 | { |
||
386 | // Verify that field exists |
||
387 | if ('*' !== $field && true === isset($this->statements['selects']) && false === \in_array($field, $this->statements['selects'], true)) { |
||
388 | throw new \Exception(sprintf('Failed %s query - the column %s hasn\'t been selected in the query.', $type, $field)); |
||
389 | } |
||
390 | |||
391 | if (false === isset($this->statements['tables'])) { |
||
392 | throw new Exception('No table selected'); |
||
393 | } |
||
394 | |||
395 | $count = $this |
||
396 | ->table($this->subQuery($this, 'count')) |
||
397 | ->select([$this->raw(sprintf('%s(%s) AS field', strtoupper($type), $field))]) |
||
398 | ->first(); |
||
399 | |||
400 | return true === isset($count->field) ? (float)$count->field : 0; |
||
401 | } |
||
402 | |||
403 | /** |
||
404 | * Get count of all the rows for the current query |
||
405 | * |
||
406 | * @see Taken from the pecee-pixie library - https://github.com/skipperbent/pecee-pixie/ |
||
407 | * |
||
408 | * @param string $field |
||
409 | * |
||
410 | * @return int |
||
411 | * |
||
412 | * @throws Exception |
||
413 | */ |
||
414 | public function count(string $field = '*'): int |
||
415 | { |
||
416 | return (int)$this->aggregate('count', $field); |
||
417 | } |
||
418 | |||
419 | /** |
||
420 | * Get the sum for a field in the current query |
||
421 | * |
||
422 | * @see Taken from the pecee-pixie library - https://github.com/skipperbent/pecee-pixie/ |
||
423 | * |
||
424 | * @param string $field |
||
425 | * |
||
426 | * @return float |
||
427 | * |
||
428 | * @throws Exception |
||
429 | */ |
||
430 | public function sum(string $field): float |
||
431 | { |
||
432 | return $this->aggregate('sum', $field); |
||
433 | } |
||
434 | |||
435 | /** |
||
436 | * Get the average for a field in the current query |
||
437 | * |
||
438 | * @see Taken from the pecee-pixie library - https://github.com/skipperbent/pecee-pixie/ |
||
439 | * |
||
440 | * @param string $field |
||
441 | * |
||
442 | * @return float |
||
443 | * |
||
444 | * @throws Exception |
||
445 | */ |
||
446 | public function average(string $field): float |
||
447 | { |
||
448 | return $this->aggregate('avg', $field); |
||
449 | } |
||
450 | |||
451 | /** |
||
452 | * Get the minimum for a field in the current query |
||
453 | * |
||
454 | * @see Taken from the pecee-pixie library - https://github.com/skipperbent/pecee-pixie/ |
||
455 | * |
||
456 | * @param string $field |
||
457 | * |
||
458 | * @return float |
||
459 | * |
||
460 | * @throws Exception |
||
461 | */ |
||
462 | public function min(string $field): float |
||
463 | { |
||
464 | return $this->aggregate('min', $field); |
||
465 | } |
||
466 | |||
467 | /** |
||
468 | * Get the maximum for a field in the current query |
||
469 | * |
||
470 | * @see Taken from the pecee-pixie library - https://github.com/skipperbent/pecee-pixie/ |
||
471 | * |
||
472 | * @param string $field |
||
473 | * |
||
474 | * @return float |
||
475 | * |
||
476 | * @throws Exception |
||
477 | */ |
||
478 | public function max(string $field): float |
||
479 | { |
||
480 | return $this->aggregate('max', $field); |
||
481 | } |
||
482 | |||
483 | /** |
||
484 | * @param string $type |
||
485 | * @param bool|array<mixed, mixed> $dataToBePassed |
||
486 | * |
||
487 | * @return mixed |
||
488 | * |
||
489 | * @throws Exception |
||
490 | */ |
||
491 | public function getQuery(string $type = 'select', $dataToBePassed = []) |
||
492 | { |
||
493 | $allowedTypes = ['select', 'insert', 'insertignore', 'replace', 'delete', 'update', 'criteriaonly']; |
||
494 | if (!in_array(strtolower($type), $allowedTypes)) { |
||
495 | throw new Exception($type . ' is not a known type.', 2); |
||
496 | } |
||
497 | |||
498 | $queryArr = $this->adapterInstance->$type($this->statements, $dataToBePassed); |
||
499 | |||
500 | return $this->container->build( |
||
501 | QueryObject::class, |
||
502 | [$queryArr['sql'], $queryArr['bindings'], $this->dbInstance] |
||
503 | ); |
||
504 | } |
||
505 | |||
506 | /** |
||
507 | * @param QueryBuilderHandler $queryBuilder |
||
508 | * @param string|null $alias |
||
509 | * |
||
510 | * @return Raw |
||
511 | */ |
||
512 | public function subQuery(QueryBuilderHandler $queryBuilder, ?string $alias = null) |
||
513 | { |
||
514 | $sql = '(' . $queryBuilder->getQuery()->getRawSql() . ')'; |
||
515 | if (is_string($alias) && 0 !== mb_strlen($alias)) { |
||
516 | $sql = $sql . ' as ' . $alias; |
||
517 | } |
||
518 | |||
519 | return $queryBuilder->raw($sql); |
||
520 | } |
||
521 | |||
522 | /** |
||
523 | * Handles the various insert operations based on the type. |
||
524 | * |
||
525 | * @param array<int|string, mixed|mixed[]> $data |
||
526 | * @param string $type |
||
527 | * |
||
528 | * @return int|int[]|mixed|null can return a single row id, array of row ids, null (for failed) or any other value short circuited from event |
||
529 | */ |
||
530 | private function doInsert(array $data, string $type) |
||
531 | { |
||
532 | $eventResult = $this->fireEvents('before-insert'); |
||
533 | if (!is_null($eventResult)) { |
||
534 | return $eventResult; |
||
535 | } |
||
536 | |||
537 | // If first value is not an array () not a batch insert) |
||
538 | if (!is_array(current($data))) { |
||
539 | $queryObject = $this->getQuery($type, $data); |
||
540 | |||
541 | list($preparedQuery, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings()); |
||
542 | $this->dbInstance->get_results($preparedQuery); |
||
543 | |||
544 | // Check we have a result. |
||
545 | $return = 1 === $this->dbInstance->rows_affected ? $this->dbInstance->insert_id : null; |
||
546 | } else { |
||
547 | // Its a batch insert |
||
548 | $return = []; |
||
549 | $executionTime = 0; |
||
550 | foreach ($data as $subData) { |
||
551 | $queryObject = $this->getQuery($type, $subData); |
||
552 | |||
553 | list($preparedQuery, $time) = $this->statement($queryObject->getSql(), $queryObject->getBindings()); |
||
554 | $this->dbInstance->get_results($preparedQuery); |
||
555 | $executionTime += $time; |
||
556 | |||
557 | if (1 === $this->dbInstance->rows_affected) { |
||
558 | $return[] = $this->dbInstance->insert_id; |
||
559 | } |
||
560 | } |
||
561 | } |
||
562 | |||
563 | $this->fireEvents('after-insert', $return, $executionTime); |
||
564 | |||
565 | return $return; |
||
566 | } |
||
567 | |||
568 | /** |
||
569 | * @param array<int|string, mixed|mixed[]> $data either key=>value array for single or array of arrays for bulk |
||
570 | * |
||
571 | * @return int|int[]|mixed|null can return a single row id, array of row ids, null (for failed) or any other value short circuited from event |
||
572 | */ |
||
573 | public function insert($data) |
||
574 | { |
||
575 | return $this->doInsert($data, 'insert'); |
||
576 | } |
||
577 | |||
578 | /** |
||
579 | * |
||
580 | * @param array<int|string, mixed|mixed[]> $data either key=>value array for single or array of arrays for bulk |
||
581 | * |
||
582 | * @return int|int[]|mixed|null can return a single row id, array of row ids, null (for failed) or any other value short circuited from event |
||
583 | */ |
||
584 | public function insertIgnore($data) |
||
585 | { |
||
586 | return $this->doInsert($data, 'insertignore'); |
||
587 | } |
||
588 | |||
589 | /** |
||
590 | * |
||
591 | * @param array<int|string, mixed|mixed[]> $data either key=>value array for single or array of arrays for bulk |
||
592 | * |
||
593 | * @return int|int[]|mixed|null can return a single row id, array of row ids, null (for failed) or any other value short circuited from event |
||
594 | */ |
||
595 | public function replace($data) |
||
596 | { |
||
597 | return $this->doInsert($data, 'replace'); |
||
598 | } |
||
599 | |||
600 | /** |
||
601 | * @param array<string, mixed> $data |
||
602 | * |
||
603 | * @return int|null |
||
604 | */ |
||
605 | public function update($data) |
||
606 | { |
||
607 | $eventResult = $this->fireEvents('before-update'); |
||
608 | if (!is_null($eventResult)) { |
||
609 | return $eventResult; |
||
610 | } |
||
611 | $queryObject = $this->getQuery('update', $data); |
||
612 | list($preparedQuery, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings()); |
||
613 | |||
614 | $this->dbInstance()->get_results($preparedQuery); |
||
615 | $this->fireEvents('after-update', $queryObject, $executionTime); |
||
616 | |||
617 | return 0 !== $this->dbInstance()->rows_affected |
||
618 | ? $this->dbInstance()->rows_affected |
||
619 | : null; |
||
620 | } |
||
621 | |||
622 | /** |
||
623 | * @param array<string, mixed> $data |
||
624 | * |
||
625 | * @return int|null will return row id for insert and bool for success/fail on update |
||
626 | */ |
||
627 | public function updateOrInsert($data) |
||
628 | { |
||
629 | if ($this->first()) { |
||
630 | return $this->update($data); |
||
631 | } |
||
632 | |||
633 | return $this->insert($data); |
||
634 | } |
||
635 | |||
636 | /** |
||
637 | * @param array<string, mixed> $data |
||
638 | * |
||
639 | * @return static |
||
640 | */ |
||
641 | public function onDuplicateKeyUpdate($data) |
||
642 | { |
||
643 | $this->addStatement('onduplicate', $data); |
||
644 | |||
645 | return $this; |
||
646 | } |
||
647 | |||
648 | /** |
||
649 | * @return int number of rows effected |
||
650 | */ |
||
651 | public function delete(): int |
||
652 | { |
||
653 | $eventResult = $this->fireEvents('before-delete'); |
||
654 | if (!is_null($eventResult)) { |
||
655 | return $eventResult; |
||
656 | } |
||
657 | |||
658 | $queryObject = $this->getQuery('delete'); |
||
659 | |||
660 | list($preparedQuery, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings()); |
||
661 | $this->dbInstance()->get_results($preparedQuery); |
||
662 | $this->fireEvents('after-delete', $queryObject, $executionTime); |
||
663 | |||
664 | return $this->dbInstance()->rows_affected; |
||
665 | } |
||
666 | |||
667 | /** |
||
668 | * @param string|Raw ...$tables Single table or array of tables |
||
669 | * |
||
670 | * @return static |
||
671 | * |
||
672 | * @throws Exception |
||
673 | */ |
||
674 | public function table(...$tables) |
||
675 | { |
||
676 | $instance = $this->constructCurrentBuilderClass($this->connection); |
||
677 | $this->setFetchMode($this->getFetchMode(), $this->hydratorConstructorArgs); |
||
678 | $tables = $this->addTablePrefix($tables, false); |
||
679 | $instance->addStatement('tables', $tables); |
||
680 | |||
681 | return $instance; |
||
682 | } |
||
683 | |||
684 | /** |
||
685 | * @param string|Raw ...$tables Single table or array of tables |
||
686 | * |
||
687 | * @return static |
||
688 | */ |
||
689 | public function from(...$tables): self |
||
690 | { |
||
691 | $tables = $this->addTablePrefix($tables, false); |
||
692 | $this->addStatement('tables', $tables); |
||
693 | |||
694 | return $this; |
||
695 | } |
||
696 | |||
697 | /** |
||
698 | * @param string|string[]|Raw[]|array<string, string> $fields |
||
699 | * |
||
700 | * @return static |
||
701 | */ |
||
702 | public function select($fields): self |
||
703 | { |
||
704 | if (!is_array($fields)) { |
||
705 | $fields = func_get_args(); |
||
706 | } |
||
707 | |||
708 | foreach ($fields as $field => $alias) { |
||
709 | // If we have a JSON expression |
||
710 | if ($this->jsonHandler->isJsonSelector($field)) { |
||
711 | $field = $this->jsonHandler->extractAndUnquoteFromJsonSelector($field); |
||
712 | } |
||
713 | |||
714 | // If no alias passed, but field is for JSON. thrown an exception. |
||
715 | if (is_numeric($field) && is_string($alias) && $this->jsonHandler->isJsonSelector($alias)) { |
||
716 | throw new Exception("An alias must be used if you wish to select from JSON Object", 1); |
||
717 | } |
||
718 | |||
719 | // Treat each array as a single table, to retain order added |
||
720 | $field = is_numeric($field) |
||
721 | ? $field = $alias // If single colum |
||
722 | : $field = [$field => $alias]; // Has alias |
||
723 | |||
724 | $field = $this->addTablePrefix($field); |
||
725 | $this->addStatement('selects', $field); |
||
726 | } |
||
727 | |||
728 | return $this; |
||
729 | } |
||
730 | |||
731 | /** |
||
732 | * @param string|string[]|Raw[]|array<string, string> $fields |
||
733 | * |
||
734 | * @return static |
||
735 | */ |
||
736 | public function selectDistinct($fields) |
||
737 | { |
||
738 | $this->select($fields); |
||
739 | $this->addStatement('distinct', true); |
||
740 | |||
741 | return $this; |
||
742 | } |
||
743 | |||
744 | /** |
||
745 | * @param string|string[] $field either the single field or an array of fields |
||
746 | * |
||
747 | * @return static |
||
748 | */ |
||
749 | public function groupBy($field): self |
||
750 | { |
||
751 | $field = $this->addTablePrefix($field); |
||
752 | $this->addStatement('groupBys', $field); |
||
753 | |||
754 | return $this; |
||
755 | } |
||
756 | |||
757 | /** |
||
758 | * @param string|array<string|int, mixed> $fields |
||
759 | * @param string $defaultDirection |
||
760 | * |
||
761 | * @return static |
||
762 | */ |
||
763 | public function orderBy($fields, string $defaultDirection = 'ASC'): self |
||
764 | { |
||
765 | if (!is_array($fields)) { |
||
766 | $fields = [$fields]; |
||
767 | } |
||
768 | |||
769 | foreach ($fields as $key => $value) { |
||
770 | $field = $key; |
||
771 | $type = $value; |
||
772 | if (is_int($key)) { |
||
773 | $field = $value; |
||
774 | $type = $defaultDirection; |
||
775 | } |
||
776 | |||
777 | if ($this->jsonHandler->isJsonSelector($field)) { |
||
778 | $field = $this->jsonHandler->extractAndUnquoteFromJsonSelector($field); |
||
779 | } |
||
780 | |||
781 | if (!$field instanceof Raw) { |
||
782 | $field = $this->addTablePrefix($field); |
||
783 | } |
||
784 | $this->statements['orderBys'][] = compact('field', 'type'); |
||
785 | } |
||
786 | |||
787 | return $this; |
||
788 | } |
||
789 | |||
790 | /** |
||
791 | * @param string|Raw $key The database column which holds the JSON value |
||
792 | * @param string|Raw|string[] $jsonKey The json key/index to search |
||
793 | * @param string $defaultDirection |
||
794 | * @return static |
||
795 | */ |
||
796 | public function orderByJson($key, $jsonKey, string $defaultDirection = 'ASC'): self |
||
797 | { |
||
798 | $key = $this->jsonHandler->jsonExpressionFactory()->extractAndUnquote($key, $jsonKey); |
||
799 | return $this->orderBy($key, $defaultDirection); |
||
800 | } |
||
801 | |||
802 | /** |
||
803 | * @param int $limit |
||
804 | * |
||
805 | * @return static |
||
806 | */ |
||
807 | public function limit(int $limit): self |
||
808 | { |
||
809 | $this->statements['limit'] = $limit; |
||
810 | |||
811 | return $this; |
||
812 | } |
||
813 | |||
814 | /** |
||
815 | * @param int $offset |
||
816 | * |
||
817 | * @return static |
||
818 | */ |
||
819 | public function offset(int $offset): self |
||
820 | { |
||
821 | $this->statements['offset'] = $offset; |
||
822 | |||
823 | return $this; |
||
824 | } |
||
825 | |||
826 | /** |
||
827 | * @param string|string[]|Raw|Raw[] $key |
||
828 | * @param string $operator |
||
829 | * @param mixed $value |
||
830 | * @param string $joiner |
||
831 | * |
||
832 | * @return static |
||
833 | */ |
||
834 | public function having($key, string $operator, $value, string $joiner = 'AND') |
||
835 | { |
||
836 | $key = $this->addTablePrefix($key); |
||
837 | $this->statements['havings'][] = compact('key', 'operator', 'value', 'joiner'); |
||
838 | |||
839 | return $this; |
||
840 | } |
||
841 | |||
842 | /** |
||
843 | * @param string|string[]|Raw|Raw[] $key |
||
844 | * @param string $operator |
||
845 | * @param mixed $value |
||
846 | * |
||
847 | * @return static |
||
848 | */ |
||
849 | public function orHaving($key, $operator, $value) |
||
850 | { |
||
851 | return $this->having($key, $operator, $value, 'OR'); |
||
852 | } |
||
853 | |||
854 | /** |
||
855 | * @param string|Raw $key |
||
856 | * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed |
||
857 | * @param mixed|null $value |
||
858 | * |
||
859 | * @return static |
||
860 | */ |
||
861 | public function where($key, $operator = null, $value = null): self |
||
862 | { |
||
863 | // If two params are given then assume operator is = |
||
864 | if (2 === func_num_args()) { |
||
865 | $value = $operator; |
||
866 | $operator = '='; |
||
867 | } |
||
868 | |||
869 | return $this->whereHandler($key, $operator, $value); |
||
870 | } |
||
871 | |||
872 | /** |
||
873 | * @param string|Raw|\Closure(QueryBuilderHandler):void $key |
||
874 | * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed |
||
875 | * @param mixed|null $value |
||
876 | * |
||
877 | * @return static |
||
878 | */ |
||
879 | public function orWhere($key, $operator = null, $value = null): self |
||
880 | { |
||
881 | // If two params are given then assume operator is = |
||
882 | if (2 === func_num_args()) { |
||
883 | $value = $operator; |
||
884 | $operator = '='; |
||
885 | } |
||
886 | |||
887 | return $this->whereHandler($key, $operator, $value, 'OR'); |
||
888 | } |
||
889 | |||
890 | /** |
||
891 | * @param string|Raw $key |
||
892 | * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed |
||
893 | * @param mixed|null $value |
||
894 | * |
||
895 | * @return static |
||
896 | */ |
||
897 | public function whereNot($key, $operator = null, $value = null): self |
||
898 | { |
||
899 | // If two params are given then assume operator is = |
||
900 | if (2 === func_num_args()) { |
||
901 | $value = $operator; |
||
902 | $operator = '='; |
||
903 | } |
||
904 | |||
905 | return $this->whereHandler($key, $operator, $value, 'AND NOT'); |
||
906 | } |
||
907 | |||
908 | /** |
||
909 | * @param string|Raw $key |
||
910 | * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed |
||
911 | * @param mixed|null $value |
||
912 | * |
||
913 | * @return static |
||
914 | */ |
||
915 | public function orWhereNot($key, $operator = null, $value = null) |
||
916 | { |
||
917 | // If two params are given then assume operator is = |
||
918 | if (2 === func_num_args()) { |
||
919 | $value = $operator; |
||
920 | $operator = '='; |
||
921 | } |
||
922 | |||
923 | return $this->whereHandler($key, $operator, $value, 'OR NOT'); |
||
924 | } |
||
925 | |||
926 | /** |
||
927 | * @param string|Raw $key |
||
928 | * @param mixed[]|string|Raw $values |
||
929 | * |
||
930 | * @return static |
||
931 | */ |
||
932 | public function whereIn($key, $values): self |
||
933 | { |
||
934 | return $this->whereHandler($key, 'IN', $values, 'AND'); |
||
935 | } |
||
936 | |||
937 | /** |
||
938 | * @param string|Raw $key |
||
939 | * @param mixed[]|string|Raw $values |
||
940 | * |
||
941 | * @return static |
||
942 | */ |
||
943 | public function whereNotIn($key, $values): self |
||
944 | { |
||
945 | return $this->whereHandler($key, 'NOT IN', $values, 'AND'); |
||
946 | } |
||
947 | |||
948 | /** |
||
949 | * @param string|Raw $key |
||
950 | * @param mixed[]|string|Raw $values |
||
951 | * |
||
952 | * @return static |
||
953 | */ |
||
954 | public function orWhereIn($key, $values): self |
||
955 | { |
||
956 | return $this->whereHandler($key, 'IN', $values, 'OR'); |
||
957 | } |
||
958 | |||
959 | /** |
||
960 | * @param string|Raw $key |
||
961 | * @param mixed[]|string|Raw $values |
||
962 | * |
||
963 | * @return static |
||
964 | */ |
||
965 | public function orWhereNotIn($key, $values): self |
||
966 | { |
||
967 | return $this->whereHandler($key, 'NOT IN', $values, 'OR'); |
||
968 | } |
||
969 | |||
970 | /** |
||
971 | * @param string|Raw $key |
||
972 | * @param mixed $valueFrom |
||
973 | * @param mixed $valueTo |
||
974 | * |
||
975 | * @return static |
||
976 | */ |
||
977 | public function whereBetween($key, $valueFrom, $valueTo): self |
||
978 | { |
||
979 | return $this->whereHandler($key, 'BETWEEN', [$valueFrom, $valueTo], 'AND'); |
||
980 | } |
||
981 | |||
982 | /** |
||
983 | * @param string|Raw $key |
||
984 | * @param mixed $valueFrom |
||
985 | * @param mixed $valueTo |
||
986 | * |
||
987 | * @return static |
||
988 | */ |
||
989 | public function orWhereBetween($key, $valueFrom, $valueTo): self |
||
990 | { |
||
991 | return $this->whereHandler($key, 'BETWEEN', [$valueFrom, $valueTo], 'OR'); |
||
992 | } |
||
993 | |||
994 | /** |
||
995 | * Handles all function call based where conditions |
||
996 | * |
||
997 | * @param string|Raw $key |
||
998 | * @param string $function |
||
999 | * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed |
||
1000 | * @param mixed|null $value |
||
1001 | * @return static |
||
1002 | */ |
||
1003 | protected function whereFunctionCallHandler($key, $function, $operator, $value): self |
||
1004 | { |
||
1005 | $key = \sprintf('%s(%s)', $function, $this->addTablePrefix($key)); |
||
1006 | return $this->where($key, $operator, $value); |
||
1007 | } |
||
1008 | |||
1009 | /** |
||
1010 | * @param string|Raw $key |
||
1011 | * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed |
||
1012 | * @param mixed|null $value |
||
1013 | * @return self |
||
1014 | */ |
||
1015 | public function whereMonth($key, $operator = null, $value = null): self |
||
1016 | { |
||
1017 | // If two params are given then assume operator is = |
||
1018 | if (2 === func_num_args()) { |
||
1019 | $value = $operator; |
||
1020 | $operator = '='; |
||
1021 | } |
||
1022 | return $this->whereFunctionCallHandler($key, 'MONTH', $operator, $value); |
||
1023 | } |
||
1024 | |||
1025 | /** |
||
1026 | * @param string|Raw $key |
||
1027 | * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed |
||
1028 | * @param mixed|null $value |
||
1029 | * @return self |
||
1030 | */ |
||
1031 | public function whereDay($key, $operator = null, $value = null): self |
||
1032 | { |
||
1033 | // If two params are given then assume operator is = |
||
1034 | if (2 === func_num_args()) { |
||
1035 | $value = $operator; |
||
1036 | $operator = '='; |
||
1037 | } |
||
1038 | return $this->whereFunctionCallHandler($key, 'DAY', $operator, $value); |
||
1039 | } |
||
1040 | |||
1041 | /** |
||
1042 | * @param string|Raw $key |
||
1043 | * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed |
||
1044 | * @param mixed|null $value |
||
1045 | * @return self |
||
1046 | */ |
||
1047 | public function whereYear($key, $operator = null, $value = null): self |
||
1048 | { |
||
1049 | // If two params are given then assume operator is = |
||
1050 | if (2 === func_num_args()) { |
||
1051 | $value = $operator; |
||
1052 | $operator = '='; |
||
1053 | } |
||
1054 | return $this->whereFunctionCallHandler($key, 'YEAR', $operator, $value); |
||
1055 | } |
||
1056 | |||
1057 | /** |
||
1058 | * @param string|Raw $key |
||
1059 | * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed |
||
1060 | * @param mixed|null $value |
||
1061 | * @return self |
||
1062 | */ |
||
1063 | public function whereDate($key, $operator = null, $value = null): self |
||
1064 | { |
||
1065 | // If two params are given then assume operator is = |
||
1066 | if (2 === func_num_args()) { |
||
1067 | $value = $operator; |
||
1068 | $operator = '='; |
||
1069 | } |
||
1070 | return $this->whereFunctionCallHandler($key, 'DATE', $operator, $value); |
||
1071 | } |
||
1072 | |||
1073 | /** |
||
1074 | * @param string|Raw $key |
||
1075 | * |
||
1076 | * @return static |
||
1077 | */ |
||
1078 | public function whereNull($key): self |
||
1079 | { |
||
1080 | return $this->whereNullHandler($key); |
||
1081 | } |
||
1082 | |||
1083 | /** |
||
1084 | * @param string|Raw $key |
||
1085 | * |
||
1086 | * @return static |
||
1087 | */ |
||
1088 | public function whereNotNull($key): self |
||
1089 | { |
||
1090 | return $this->whereNullHandler($key, 'NOT'); |
||
1091 | } |
||
1092 | |||
1093 | /** |
||
1094 | * @param string|Raw $key |
||
1095 | * |
||
1096 | * @return static |
||
1097 | */ |
||
1098 | public function orWhereNull($key): self |
||
1099 | { |
||
1100 | return $this->whereNullHandler($key, '', 'or'); |
||
1101 | } |
||
1102 | |||
1103 | /** |
||
1104 | * @param string|Raw $key |
||
1105 | * |
||
1106 | * @return static |
||
1107 | */ |
||
1108 | public function orWhereNotNull($key): self |
||
1109 | { |
||
1110 | return $this->whereNullHandler($key, 'NOT', 'or'); |
||
1111 | } |
||
1112 | |||
1113 | /** |
||
1114 | * @param string|Raw $key |
||
1115 | * @param string $prefix |
||
1116 | * @param string $operator |
||
1117 | * |
||
1118 | * @return static |
||
1119 | */ |
||
1120 | protected function whereNullHandler($key, string $prefix = '', $operator = ''): self |
||
1121 | { |
||
1122 | $prefix = 0 === mb_strlen($prefix) ? '' : " {$prefix}"; |
||
1123 | |||
1124 | if ($key instanceof Raw) { |
||
1125 | $key = $this->adapterInstance->parseRaw($key); |
||
1126 | } |
||
1127 | |||
1128 | $key = $this->addTablePrefix($key); |
||
1129 | if ($key instanceof Closure) { |
||
1130 | throw new Exception('Key used for whereNull condition must be a string or raw exrpession.', 1); |
||
1131 | } |
||
1132 | |||
1133 | return $this->{$operator . 'Where'}($this->raw("{$key} IS{$prefix} NULL")); |
||
1134 | } |
||
1135 | |||
1136 | |||
1137 | /** |
||
1138 | * Runs a transaction |
||
1139 | * |
||
1140 | * @param \Closure(Transaction):void $callback |
||
1141 | * |
||
1142 | * @return static |
||
1143 | */ |
||
1144 | public function transaction(Closure $callback): self |
||
1167 | } |
||
1168 | } |
||
1169 | |||
1170 | /** |
||
1171 | * Handles the transaction call. |
||
1172 | * Catches any WPDB Errors (printed) |
||
1173 | * |
||
1174 | * @param Closure $callback |
||
1175 | * @param Transaction $transaction |
||
1176 | * |
||
1177 | * @return void |
||
1178 | * @throws Exception |
||
1179 | */ |
||
1180 | protected function handleTransactionCall(Closure $callback, Transaction $transaction): void |
||
1181 | { |
||
1182 | try { |
||
1183 | ob_start(); |
||
1184 | $callback($transaction); |
||
1185 | $output = ob_get_clean() ?: ''; |
||
1186 | } catch (Throwable $th) { |
||
1187 | ob_end_clean(); |
||
1188 | throw $th; |
||
1189 | } |
||
1190 | |||
1191 | // If we caught an error, throw an exception. |
||
1192 | if (0 !== mb_strlen($output)) { |
||
1193 | throw new Exception($output); |
||
1194 | } |
||
1195 | } |
||
1196 | |||
1197 | /*************************************************************************/ |
||
1198 | /*************************************************************************/ |
||
1199 | /*************************************************************************/ |
||
1200 | /** JOIN JOIN **/ |
||
1201 | /** JOIN **/ |
||
1202 | /** JOIN JOIN **/ |
||
1203 | /*************************************************************************/ |
||
1204 | /*************************************************************************/ |
||
1205 | /*************************************************************************/ |
||
1206 | |||
1207 | /** |
||
1208 | * @param string|Raw $table |
||
1209 | * @param string|Raw|Closure $key |
||
1210 | * @param string|null $operator |
||
1211 | * @param mixed $value |
||
1212 | * @param string $type |
||
1213 | * |
||
1214 | * @return static |
||
1215 | */ |
||
1216 | public function join($table, $key, ?string $operator = null, $value = null, $type = 'inner') |
||
1217 | { |
||
1218 | // Potentially cast key from JSON |
||
1219 | if ($this->jsonHandler->isJsonSelector($key)) { |
||
1220 | /** @var string $key */ |
||
1221 | $key = $this->jsonHandler->extractAndUnquoteFromJsonSelector($key); /** @phpstan-ignore-line */ |
||
1222 | } |
||
1223 | |||
1224 | // Potentially cast value from json |
||
1225 | if ($this->jsonHandler->isJsonSelector($value)) { |
||
1226 | /** @var string $value */ |
||
1227 | $value = $this->jsonHandler->extractAndUnquoteFromJsonSelector($value); |
||
1228 | } |
||
1229 | |||
1230 | if (!$key instanceof Closure) { |
||
1231 | $key = function ($joinBuilder) use ($key, $operator, $value) { |
||
1232 | $joinBuilder->on($key, $operator, $value); |
||
1233 | }; |
||
1234 | } |
||
1235 | |||
1236 | // Build a new JoinBuilder class, keep it by reference so any changes made |
||
1237 | // in the closure should reflect here |
||
1238 | $joinBuilder = $this->container->build(JoinBuilder::class, [$this->connection]); |
||
1239 | $joinBuilder = &$joinBuilder; |
||
1240 | // Call the closure with our new joinBuilder object |
||
1241 | $key($joinBuilder); |
||
1242 | $table = $this->addTablePrefix($table, false); |
||
1243 | // Get the criteria only query from the joinBuilder object |
||
1244 | $this->statements['joins'][] = compact('type', 'table', 'joinBuilder'); |
||
1245 | return $this; |
||
1246 | } |
||
1247 | |||
1248 | |||
1249 | |||
1250 | /** |
||
1251 | * @param string|Raw $table |
||
1252 | * @param string|Raw|Closure $key |
||
1253 | * @param string|null $operator |
||
1254 | * @param mixed $value |
||
1255 | * |
||
1256 | * @return static |
||
1257 | */ |
||
1258 | public function leftJoin($table, $key, $operator = null, $value = null) |
||
1259 | { |
||
1260 | return $this->join($table, $key, $operator, $value, 'left'); |
||
1261 | } |
||
1262 | |||
1263 | /** |
||
1264 | * @param string|Raw $table |
||
1265 | * @param string|Raw|Closure $key |
||
1266 | * @param string|null $operator |
||
1267 | * @param mixed $value |
||
1268 | * |
||
1269 | * @return static |
||
1270 | */ |
||
1271 | public function rightJoin($table, $key, $operator = null, $value = null) |
||
1272 | { |
||
1273 | return $this->join($table, $key, $operator, $value, 'right'); |
||
1274 | } |
||
1275 | |||
1276 | /** |
||
1277 | * @param string|Raw $table |
||
1278 | * @param string|Raw|Closure $key |
||
1279 | * @param string|null $operator |
||
1280 | * @param mixed $value |
||
1281 | * |
||
1282 | * @return static |
||
1283 | */ |
||
1284 | public function innerJoin($table, $key, $operator = null, $value = null) |
||
1285 | { |
||
1286 | return $this->join($table, $key, $operator, $value, 'inner'); |
||
1287 | } |
||
1288 | |||
1289 | /** |
||
1290 | * @param string|Raw $table |
||
1291 | * @param string|Raw|Closure $key |
||
1292 | * @param string|null $operator |
||
1293 | * @param mixed $value |
||
1294 | * |
||
1295 | * @return static |
||
1296 | */ |
||
1297 | public function crossJoin($table, $key, $operator = null, $value = null) |
||
1298 | { |
||
1299 | return $this->join($table, $key, $operator, $value, 'cross'); |
||
1300 | } |
||
1301 | |||
1302 | /** |
||
1303 | * @param string|Raw $table |
||
1304 | * @param string|Raw|Closure $key |
||
1305 | * @param string|null $operator |
||
1306 | * @param mixed $value |
||
1307 | * |
||
1308 | * @return static |
||
1309 | */ |
||
1310 | public function outerJoin($table, $key, $operator = null, $value = null) |
||
1311 | { |
||
1312 | return $this->join($table, $key, $operator, $value, 'outer'); |
||
1313 | } |
||
1314 | |||
1315 | /** |
||
1316 | * Shortcut to join 2 tables on the same key name with equals |
||
1317 | * |
||
1318 | * @param string $table |
||
1319 | * @param string $key |
||
1320 | * @param string $type |
||
1321 | * @return self |
||
1322 | * @throws Exception If base table is set as more than 1 or 0 |
||
1323 | */ |
||
1324 | public function joinUsing(string $table, string $key, string $type = 'INNER'): self |
||
1325 | { |
||
1326 | if (!array_key_exists('tables', $this->statements) || count($this->statements['tables']) !== 1) { |
||
1327 | throw new Exception("JoinUsing can only be used with a single table set as the base of the query", 1); |
||
1328 | } |
||
1329 | $baseTable = end($this->statements['tables']); |
||
1330 | |||
1331 | // Potentialy cast key from JSON |
||
1332 | if ($this->jsonHandler->isJsonSelector($key)) { |
||
1333 | $key = $this->jsonHandler->extractAndUnquoteFromJsonSelector($key); |
||
1334 | } |
||
1335 | |||
1336 | $remoteKey = $table = $this->addTablePrefix("{$table}.{$key}", true); |
||
1337 | $localKey = $table = $this->addTablePrefix("{$baseTable}.{$key}", true); |
||
1338 | return $this->join($table, $remoteKey, '=', $localKey, $type); |
||
1339 | } |
||
1340 | |||
1341 | /** |
||
1342 | * Add a raw query |
||
1343 | * |
||
1344 | * @param string|Raw $value |
||
1345 | * @param mixed|mixed[] $bindings |
||
1346 | * |
||
1347 | * @return Raw |
||
1348 | */ |
||
1349 | public function raw($value, $bindings = []): Raw |
||
1350 | { |
||
1351 | return new Raw($value, $bindings); |
||
1352 | } |
||
1353 | |||
1354 | /** |
||
1355 | * Return wpdb instance |
||
1356 | * |
||
1357 | * @return wpdb |
||
1358 | */ |
||
1359 | public function dbInstance(): wpdb |
||
1360 | { |
||
1361 | return $this->dbInstance; |
||
1362 | } |
||
1363 | |||
1364 | /** |
||
1365 | * @param Connection $connection |
||
1366 | * |
||
1367 | * @return static |
||
1368 | */ |
||
1369 | public function setConnection(Connection $connection): self |
||
1370 | { |
||
1371 | $this->connection = $connection; |
||
1372 | |||
1373 | return $this; |
||
1374 | } |
||
1375 | |||
1376 | /** |
||
1377 | * @return Connection |
||
1378 | */ |
||
1379 | public function getConnection() |
||
1382 | } |
||
1383 | |||
1384 | /** |
||
1385 | * @param string|Raw|Closure $key |
||
1386 | * @param string|null $operator |
||
1387 | * @param mixed|null $value |
||
1388 | * @param string $joiner |
||
1389 | * |
||
1390 | * @return static |
||
1391 | */ |
||
1392 | protected function whereHandler($key, $operator = null, $value = null, $joiner = 'AND') |
||
1393 | { |
||
1394 | $key = $this->addTablePrefix($key); |
||
1395 | if ($key instanceof Raw) { |
||
1396 | $key = $this->adapterInstance->parseRaw($key); |
||
1397 | } |
||
1398 | |||
1399 | if ($this->jsonHandler->isJsonSelector($key)) { |
||
1400 | $key = $this->jsonHandler->extractAndUnquoteFromJsonSelector($key); |
||
1401 | } |
||
1402 | |||
1403 | $this->statements['wheres'][] = compact('key', 'operator', 'value', 'joiner'); |
||
1404 | return $this; |
||
1405 | } |
||
1406 | |||
1407 | |||
1408 | |||
1409 | /** |
||
1410 | * @param string $key |
||
1411 | * @param mixed|mixed[]|bool $value |
||
1412 | * |
||
1413 | * @return void |
||
1414 | */ |
||
1415 | protected function addStatement($key, $value) |
||
1416 | { |
||
1417 | if (!is_array($value)) { |
||
1418 | $value = [$value]; |
||
1419 | } |
||
1420 | |||
1421 | if (!array_key_exists($key, $this->statements)) { |
||
1422 | $this->statements[$key] = $value; |
||
1423 | } else { |
||
1424 | $this->statements[$key] = array_merge($this->statements[$key], $value); |
||
1425 | } |
||
1426 | } |
||
1427 | |||
1428 | /** |
||
1429 | * @param string $event |
||
1430 | * @param string|Raw $table |
||
1431 | * |
||
1432 | * @return callable|null |
||
1433 | */ |
||
1434 | public function getEvent(string $event, $table = ':any'): ?callable |
||
1437 | } |
||
1438 | |||
1439 | /** |
||
1440 | * @param string $event |
||
1441 | * @param string|Raw $table |
||
1442 | * @param Closure $action |
||
1443 | * |
||
1444 | * @return void |
||
1445 | */ |
||
1446 | public function registerEvent($event, $table, Closure $action): void |
||
1447 | { |
||
1448 | $table = $table ?: ':any'; |
||
1449 | |||
1450 | if (':any' != $table) { |
||
1451 | $table = $this->addTablePrefix($table, false); |
||
1452 | } |
||
1453 | |||
1454 | $this->connection->getEventHandler()->registerEvent($event, $table, $action); |
||
1455 | } |
||
1456 | |||
1457 | /** |
||
1458 | * @param string $event |
||
1459 | * @param string|Raw $table |
||
1460 | * |
||
1461 | * @return void |
||
1462 | */ |
||
1463 | public function removeEvent(string $event, $table = ':any') |
||
1470 | } |
||
1471 | |||
1472 | /** |
||
1473 | * @param string $event |
||
1474 | * |
||
1475 | * @return mixed |
||
1476 | */ |
||
1477 | public function fireEvents(string $event) |
||
1478 | { |
||
1479 | $params = func_get_args(); // @todo Replace this with an easier to read alteratnive |
||
1480 | array_unshift($params, $this); |
||
1481 | |||
1482 | return call_user_func_array([$this->connection->getEventHandler(), 'fireEvents'], $params); |
||
1483 | } |
||
1484 | |||
1485 | /** |
||
1486 | * @return array<string, mixed[]> |
||
1487 | */ |
||
1488 | public function getStatements() |
||
1491 | } |
||
1492 | |||
1493 | /** |
||
1494 | * @return string will return WPDB Fetch mode |
||
1495 | */ |
||
1496 | public function getFetchMode() |
||
1497 | { |
||
1498 | return null !== $this->fetchMode |
||
1499 | ? $this->fetchMode |
||
1500 | : \OBJECT; |
||
1501 | } |
||
1502 | |||
1503 | /** |
||
1504 | * Returns an NEW instance of the JSON builder populated with the same connection and hydrator details. |
||
1505 | * |
||
1506 | * @return JsonQueryBuilder |
||
1507 | */ |
||
1508 | public function jsonBuilder(): JsonQueryBuilder |
||
1511 | } |
||
1512 | } |
||
1513 |