Passed
Branch master (c8d25d)
by Glynn
03:34 queued 01:07
created
src/QueryBuilder/Raw.php 1 patch
Indentation   +52 added lines, -52 removed lines patch added patch discarded remove patch
@@ -4,62 +4,62 @@
 block discarded – undo
4 4
 
5 5
 class Raw
6 6
 {
7
-    /**
8
-     * @var string
9
-     */
10
-    protected $value;
7
+	/**
8
+	 * @var string
9
+	 */
10
+	protected $value;
11 11
 
12
-    /**
13
-     * @var mixed[]
14
-     */
15
-    protected $bindings;
12
+	/**
13
+	 * @var mixed[]
14
+	 */
15
+	protected $bindings;
16 16
 
17
-    /**
18
-     * @param string $value
19
-     * @param mixed|mixed[] $bindings
20
-     */
21
-    public function __construct($value, $bindings = [])
22
-    {
23
-        $this->value    = (string)$value;
24
-        $this->bindings = (array)$bindings;
25
-    }
17
+	/**
18
+	 * @param string $value
19
+	 * @param mixed|mixed[] $bindings
20
+	 */
21
+	public function __construct($value, $bindings = [])
22
+	{
23
+		$this->value    = (string)$value;
24
+		$this->bindings = (array)$bindings;
25
+	}
26 26
 
27
-    /**
28
-     * Create a Raw instance with no bindings
29
-     *
30
-     * @param string $value
31
-     * @return self
32
-     */
33
-    public static function val(string $value): self
34
-    {
35
-        return new self($value, []);
36
-    }
27
+	/**
28
+	 * Create a Raw instance with no bindings
29
+	 *
30
+	 * @param string $value
31
+	 * @return self
32
+	 */
33
+	public static function val(string $value): self
34
+	{
35
+		return new self($value, []);
36
+	}
37 37
 
38
-    /**
39
-     * Returns the current bindings
40
-     *
41
-     * @return mixed[]
42
-     */
43
-    public function getBindings(): array
44
-    {
45
-        return $this->bindings;
46
-    }
38
+	/**
39
+	 * Returns the current bindings
40
+	 *
41
+	 * @return mixed[]
42
+	 */
43
+	public function getBindings(): array
44
+	{
45
+		return $this->bindings;
46
+	}
47 47
 
48
-    /**
49
-     * Returns the current value held.
50
-     *
51
-     * @return string
52
-     */
53
-    public function getValue(): string
54
-    {
55
-        return (string) $this->value;
56
-    }
48
+	/**
49
+	 * Returns the current value held.
50
+	 *
51
+	 * @return string
52
+	 */
53
+	public function getValue(): string
54
+	{
55
+		return (string) $this->value;
56
+	}
57 57
 
58
-    /**
59
-     * @return string
60
-     */
61
-    public function __toString()
62
-    {
63
-        return (string)$this->value;
64
-    }
58
+	/**
59
+	 * @return string
60
+	 */
61
+	public function __toString()
62
+	{
63
+		return (string)$this->value;
64
+	}
65 65
 }
Please login to merge, or discard this patch.
src/QueryBuilder/WPDBAdapter.php 2 patches
Indentation   +711 added lines, -711 removed lines patch added patch discarded remove patch
@@ -17,715 +17,715 @@
 block discarded – undo
17 17
 
18 18
 class WPDBAdapter
19 19
 {
20
-    /**
21
-     * @var string
22
-     */
23
-    protected $sanitizer = '';
24
-
25
-    /**
26
-     * @var \Pixie\Connection
27
-     */
28
-    protected $connection;
29
-
30
-    /**
31
-     * @var \Viocon\Container
32
-     */
33
-    protected $container;
34
-
35
-    public function __construct(Connection $connection)
36
-    {
37
-        $this->connection = $connection;
38
-        $this->container  = $this->connection->getContainer();
39
-    }
40
-
41
-    /**
42
-     * Build select query string and bindings
43
-     *
44
-     * @param array<string|Closure, mixed|mixed[]> $statements
45
-     *
46
-     * @throws Exception
47
-     *
48
-     * @return array{sql:string,bindings:mixed[]}
49
-     */
50
-    public function select(array $statements): array
51
-    {
52
-        if (!array_key_exists('tables', $statements)) {
53
-            throw new Exception('No table specified.', 3);
54
-        } elseif (!array_key_exists('selects', $statements)) {
55
-            $statements['selects'][] = '*';
56
-        }
57
-
58
-        // From
59
-        $tables = $this->arrayStr($statements['tables'], ', ');
60
-        // Select
61
-        $selects = $this->arrayStr($statements['selects'], ', ');
62
-
63
-        // Wheres
64
-        list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE');
65
-
66
-        // Group bys
67
-        $groupBys = '';
68
-        if (isset($statements['groupBys']) && $groupBys = $this->arrayStr($statements['groupBys'], ', ')) {
69
-            $groupBys = 'GROUP BY ' . $groupBys;
70
-        }
71
-
72
-        // Order bys
73
-        $orderBys = '';
74
-        if (isset($statements['orderBys']) && is_array($statements['orderBys'])) {
75
-            foreach ($statements['orderBys'] as $orderBy) {
76
-                $field = $this->wrapSanitizer($orderBy['field']);
77
-                if ($field instanceof Closure) {
78
-                    continue;
79
-                }
80
-                $orderBys .= $field . ' ' . $orderBy['type'] . ', ';
81
-            }
82
-
83
-            if ($orderBys = trim($orderBys, ', ')) {
84
-                $orderBys = 'ORDER BY ' . $orderBys;
85
-            }
86
-        }
87
-
88
-        // Limit and offset
89
-        $limit  = isset($statements['limit']) ? 'LIMIT ' . (int) $statements['limit'] : '';
90
-        $offset = isset($statements['offset']) ? 'OFFSET ' . (int) $statements['offset'] : '';
91
-
92
-        // Having
93
-        list($havingCriteria, $havingBindings) = $this->buildCriteriaWithType($statements, 'havings', 'HAVING');
94
-
95
-        // Joins
96
-        $joinString = $this->buildJoin($statements);
97
-
98
-        /** @var string[] */
99
-        $sqlArray = [
100
-            'SELECT' . (isset($statements['distinct']) ? ' DISTINCT' : ''),
101
-            $selects,
102
-            'FROM',
103
-            $tables,
104
-            $joinString,
105
-            $whereCriteria,
106
-            $groupBys,
107
-            $havingCriteria,
108
-            $orderBys,
109
-            $limit,
110
-            $offset,
111
-        ];
112
-
113
-        $sql = $this->concatenateQuery($sqlArray);
114
-
115
-        $bindings = array_merge(
116
-            $whereBindings,
117
-            $havingBindings
118
-        );
119
-
120
-        return compact('sql', 'bindings');
121
-    }
122
-
123
-    /**
124
-     * Build just criteria part of the query
125
-     *
126
-     * @param array<string|Closure, mixed|mixed[]> $statements
127
-     * @param bool $bindValues
128
-     *
129
-     * @return array{sql:string[]|string, bindings:array<mixed>}
130
-     */
131
-    public function criteriaOnly(array $statements, bool $bindValues = true): array
132
-    {
133
-        $sql = $bindings = [];
134
-        if (!isset($statements['criteria'])) {
135
-            return compact('sql', 'bindings');
136
-        }
137
-
138
-        list($sql, $bindings) = $this->buildCriteria($statements['criteria'], $bindValues);
139
-
140
-        return compact('sql', 'bindings');
141
-    }
142
-
143
-    /**
144
-     * Build a generic insert/ignore/replace query
145
-     *
146
-     * @param array<string|Closure, mixed|mixed[]> $statements
147
-     * @param array<string, mixed> $data
148
-     * @param string $type
149
-     *
150
-     * @return array{sql:string, bindings:mixed[]}
151
-     *
152
-     * @throws Exception
153
-     */
154
-    private function doInsert(array $statements, array $data, string $type): array
155
-    {
156
-        if (!isset($statements['tables'])) {
157
-            throw new Exception('No table specified', 3);
158
-        }
159
-
160
-        $table = end($statements['tables']);
161
-
162
-        $bindings = $keys = $values = [];
163
-
164
-        foreach ($data as $key => $value) {
165
-            $keys[] = $key;
166
-
167
-            // Handle value as bindings
168
-            $isBindings = $value instanceof Binding;
169
-            // If this is a raw binding, extract the Raw and replace value.
170
-            if ($isBindings && $value->isRaw()) {
171
-                $value = $value->getValue();
172
-            }
173
-
174
-            if ($value instanceof Raw) {
175
-                $values[] = $this->parseRaw($value);
176
-            } elseif ($isBindings) {
177
-                $values[]   =  $value->getType();
178
-                $bindings[] = $value->getValue();
179
-            } else {
180
-                $values[]   =  $this->inferType($value);
181
-                $bindings[] = $value;
182
-            }
183
-        }
184
-
185
-        $sqlArray = [
186
-        $type . ' INTO',
187
-        $this->wrapSanitizer($table),
188
-        '(' . $this->arrayStr($keys, ',') . ')',
189
-        'VALUES',
190
-        '(' . $this->arrayStr($values, ',') . ')',
191
-        ];
192
-
193
-        if (isset($statements['onduplicate'])) {
194
-            if (count($statements['onduplicate']) < 1) {
195
-                throw new Exception('No data given.', 4);
196
-            }
197
-            list($updateStatement, $updateBindings) = $this->getUpdateStatement($statements['onduplicate']);
198
-            $sqlArray[]                             = 'ON DUPLICATE KEY UPDATE ' . $updateStatement;
199
-            $bindings                               = array_merge($bindings, $updateBindings);
200
-        }
201
-
202
-        $sql = $this->concatenateQuery($this->stringifyValues($sqlArray));
203
-
204
-        return compact('sql', 'bindings');
205
-    }
206
-
207
-    /**
208
-     * Attempts to stringify an array of values.
209
-     *
210
-     * @param array<string|int, string|Closure> $values
211
-     *
212
-     * @return string[]
213
-     */
214
-    protected function stringifyValues(array $values): array
215
-    {
216
-        return array_filter(array_map([$this, 'stringifyValue'], $values));
217
-    }
218
-
219
-    /**
220
-     * Attempts to stringify a single of values.
221
-     *
222
-     * @param string|Closure|Raw $value
223
-     *
224
-     * @return string|null
225
-     */
226
-    protected function stringifyValue($value): ?string
227
-    {
228
-        if ($value instanceof Closure) {
229
-            $value = $value();
230
-
231
-            return is_string($value) ? $value : null;
232
-        }
233
-
234
-        if ($value instanceof Raw) {
235
-            return $this->parseRaw($value);
236
-        }
237
-
238
-        return $value;
239
-    }
240
-
241
-    /**
242
-     * Build Insert query
243
-     *
244
-     * @param array<string|Closure, mixed|mixed[]> $statements
245
-     * @param array<string, mixed> $data $data
246
-     *
247
-     * @return array{sql:string, bindings:mixed[]}
248
-     *
249
-     * @throws Exception
250
-     */
251
-    public function insert($statements, array $data)
252
-    {
253
-        return $this->doInsert($statements, $data, 'INSERT');
254
-    }
255
-
256
-    /**
257
-     * Build Insert Ignore query
258
-     *
259
-     * @param array<string|Closure, mixed|mixed[]> $statements
260
-     * @param array<string, mixed> $data $data
261
-     *
262
-     * @return array{sql:string, bindings:mixed[]}
263
-     *
264
-     * @throws Exception
265
-     */
266
-    public function insertIgnore($statements, array $data)
267
-    {
268
-        return $this->doInsert($statements, $data, 'INSERT IGNORE');
269
-    }
270
-
271
-    /**
272
-     * Build Insert Ignore query
273
-     *
274
-     * @param array<string|Closure, mixed|mixed[]> $statements
275
-     * @param array<string, mixed> $data $data
276
-     *
277
-     * @return array{sql:string, bindings:mixed[]}
278
-     *
279
-     * @throws Exception
280
-     */
281
-    public function replace($statements, array $data)
282
-    {
283
-        return $this->doInsert($statements, $data, 'REPLACE');
284
-    }
285
-
286
-    /**
287
-     * Build fields assignment part of SET ... or ON DUBLICATE KEY UPDATE ... statements
288
-     *
289
-     * @param array<string, mixed> $data
290
-     *
291
-     * @return array{0:string,1:mixed[]}
292
-     */
293
-    private function getUpdateStatement(array $data): array
294
-    {
295
-        $bindings  = [];
296
-        $statement = '';
297
-
298
-        foreach ($data as $key => $value) {
299
-            $isBindings = $value instanceof Binding;
300
-            // If this is a raw binding, extract the Raw and replace value.
301
-            if ($isBindings && $value->isRaw()) {
302
-                $value = $value->getValue();
303
-            }
304
-
305
-            if ($value instanceof Raw) {
306
-                $statement .= $this->stringifyValue($this->wrapSanitizer($key)) . '=' . $value . ',';
307
-            } elseif ($isBindings) {
308
-                $statement .= $this->stringifyValue($this->wrapSanitizer($key)) . sprintf('=%s,', $value->getType());
309
-                $bindings[] = $value->getValue();
310
-            } else {
311
-                $statement .= $this->stringifyValue($this->wrapSanitizer($key)) . sprintf('=%s,', $this->inferType($value));
312
-                $bindings[] = $value;
313
-            }
314
-        }
315
-
316
-        $statement = trim($statement, ',');
317
-
318
-        return [$statement, $bindings];
319
-    }
320
-
321
-    /**
322
-     * Build update query
323
-     *
324
-     * @param array<string|Closure, mixed|mixed[]> $statements
325
-     * @param array<string, mixed> $data
326
-     *
327
-     * @return array{sql:string, bindings:mixed[]}
328
-     *
329
-     * @throws Exception
330
-     */
331
-    public function update($statements, array $data)
332
-    {
333
-        if (!isset($statements['tables'])) {
334
-            throw new Exception('No table specified', 3);
335
-        } elseif (count($data) < 1) {
336
-            throw new Exception('No data given.', 4);
337
-        }
338
-
339
-        $table = end($statements['tables']);
340
-
341
-        // Update statement
342
-        list($updateStatement, $bindings) = $this->getUpdateStatement($data);
343
-
344
-        // Wheres
345
-        list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE');
346
-
347
-        // Limit
348
-        $limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : '';
349
-
350
-        $sqlArray = [
351
-            'UPDATE',
352
-            $this->wrapSanitizer($table),
353
-            'SET ' . $updateStatement,
354
-            $whereCriteria,
355
-            $limit,
356
-        ];
357
-
358
-        $sql = $this->concatenateQuery($this->stringifyValues($sqlArray));
359
-
360
-        $bindings = array_merge($bindings, $whereBindings);
361
-
362
-        return compact('sql', 'bindings');
363
-    }
364
-
365
-    /**
366
-     * Build delete query
367
-     *
368
-     * @param array<string|Closure, mixed|mixed[]> $statements
369
-     *
370
-     * @return array{sql:string, bindings:mixed[]}
371
-     *
372
-     * @throws Exception
373
-     */
374
-    public function delete($statements)
375
-    {
376
-        if (!isset($statements['tables'])) {
377
-            throw new Exception('No table specified', 3);
378
-        }
379
-
380
-        $table = end($statements['tables']);
381
-        // Ensure table name is a string
382
-        $table = $this->stringifyValue($this->wrapSanitizer($table));
383
-        if (null === $table) {
384
-            throw new Exception('Table must be a valid string.', 5);
385
-        }
386
-
387
-        // Wheres
388
-        list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE');
389
-
390
-        // Limit
391
-        $limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : '';
392
-
393
-        $sqlArray = ['DELETE FROM', $table, $whereCriteria];
394
-        $sql      = $this->concatenateQuery($sqlArray);
395
-        $bindings = $whereBindings;
396
-
397
-        return compact('sql', 'bindings');
398
-    }
399
-
400
-    /**
401
-     * Array concatenating method, like implode.
402
-     * But it does wrap sanitizer and trims last glue
403
-     *
404
-     * @param array<string|int, string> $pieces
405
-     * @param string $glue
406
-     *
407
-     * @return string
408
-     */
409
-    protected function arrayStr(array $pieces, string $glue): string
410
-    {
411
-        $str = '';
412
-        foreach ($pieces as $key => $piece) {
413
-            if (!is_int($key)) {
414
-                $piece = $key . ' AS ' . $piece;
415
-            }
416
-
417
-            $str .= $piece . $glue;
418
-        }
419
-
420
-        return trim($str, $glue);
421
-    }
422
-
423
-    /**
424
-     * Join different part of queries with a space.
425
-     *
426
-     * @param array<string|int, string> $pieces
427
-     *
428
-     * @return string
429
-     */
430
-    protected function concatenateQuery(array $pieces): string
431
-    {
432
-        $str = '';
433
-        foreach ($pieces as $piece) {
434
-            $str = trim($str) . ' ' . trim($piece);
435
-        }
436
-
437
-        return trim($str);
438
-    }
439
-
440
-    /**
441
-     * Gets the type of a value, either from a binding or infered
442
-     *
443
-     * @param mixed $value
444
-     * @return string
445
-     */
446
-    public function getType($value): string
447
-    {
448
-        return $value instanceof Binding && $value->getType() !== null
449
-            ? $value->getType() : $this->inferType($value) ;
450
-    }
451
-
452
-    /**
453
-     * Get the value from a possible Bindings object.
454
-     *
455
-     * @param mixed $value
456
-     * @return mixed
457
-     */
458
-    public function getValue($value)
459
-    {
460
-        return $value instanceof Binding ? $value->getValue() : $value;
461
-    }
462
-
463
-    /**
464
-     * Attempts to parse a raw query, if bindings are defined then they will be bound first.
465
-     *
466
-     * @param Raw $raw
467
-     * @requires string
468
-     */
469
-    public function parseRaw(Raw $raw): string
470
-    {
471
-        $bindings = $raw->getBindings();
472
-        return 0 === count($bindings)
473
-            ? (string) $raw
474
-            : $this->interpolateQuery($raw->getValue(), $bindings);
475
-    }
476
-
477
-    /**
478
-     * Interpolates a query
479
-     *
480
-     * @param string $query
481
-     * @param array<mixed> $bindings
482
-     * @return string
483
-     */
484
-    public function interpolateQuery(string $query, array $bindings = []): string
485
-    {
486
-        if (0 === count($bindings)) {
487
-            return $query;
488
-        }
489
-
490
-
491
-        $bindings = array_map([$this, 'getValue'], $bindings);
492
-        $query = $this->connection->getDbInstance()->prepare($query, $bindings) ;
493
-        return is_string($query) ? $query : '';
494
-    }
495
-
496
-    /**
497
-     * Build generic criteria string and bindings from statements, like "a = b and c = ?"
498
-     *
499
-     * @param array<string|Closure, mixed|mixed[]> $statements
500
-     * @param bool $bindValues
501
-     *
502
-     * @return array{0:string,1:string[]}
503
-     */
504
-    protected function buildCriteria(array $statements, bool $bindValues = true): array
505
-    {
506
-        $criteria = '';
507
-        $bindings = [];
508
-        foreach ($statements as $statement) {
509
-            $key   = $statement['key'];
510
-            $value = $statement['value'];
511
-
512
-            // If the value is a Raw Binding, cast to raw
513
-            if ($value instanceof Binding && Binding::RAW === $value->getType()) {
514
-                /** @var Raw */
515
-                $value = $value->getValue();
516
-            }
517
-
518
-            if (is_null($value) && $key instanceof Closure) {
519
-                // We have a closure, a nested criteria
520
-
521
-                // Build a new NestedCriteria class, keep it by reference so any changes made
522
-                // in the closure should reflect here
523
-                $nestedCriteria = $this->container->build(NestedCriteria::class, [$this->connection]);
524
-
525
-                $nestedCriteria = &$nestedCriteria;
526
-                // Call the closure with our new nestedCriteria object
527
-                $key($nestedCriteria);
528
-                // Get the criteria only query from the nestedCriteria object
529
-                $queryObject = $nestedCriteria->getQuery('criteriaOnly', true);
530
-                // Merge the bindings we get from nestedCriteria object
531
-                $bindings = array_merge($bindings, $queryObject->getBindings());
532
-                // Append the sql we get from the nestedCriteria object
533
-                $criteria .= $statement['joiner'] . ' (' . $queryObject->getSql() . ') ';
534
-            } elseif (is_array($value)) {
535
-                // where_in or between like query
536
-                $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'];
537
-
538
-                switch ($statement['operator']) {
539
-                    case 'BETWEEN':
540
-                        $bindings = array_merge($bindings, $statement['value']);
541
-                        $criteria .= sprintf(
542
-                            ' %s AND %s ',
543
-                            $this->getType($value[0]),
544
-                            $this->getType($value[1])
545
-                        );
546
-
547
-                        // Maybe cast the values bindings.
548
-                        $value[0] = $this->getValue($value[0]);
549
-                        $value[1] = $this->getValue($value[1]);
550
-
551
-                        // Parse any raws.
552
-                        $value = array_map(function ($value) {
553
-                            return $value instanceof Raw
554
-                                ? $this->parseRaw($value)
555
-                                : $value;
556
-                        }, $value);
557
-                        break;
558
-                    default:
559
-                        $valuePlaceholder = '';
560
-                        foreach ($statement['value'] as $subValue) {
561
-                            // Get its value.
562
-                            if ($this->getValue($subValue) instanceof Raw) {
563
-                                /** @var Raw $subValue */
564
-                                $subValue = $this->getValue($subValue);
565
-                                $valuePlaceholder .= sprintf('%s, ', $this->parseRaw($subValue));
566
-                                continue;
567
-                            }
568
-
569
-
570
-                            // Add in format placeholders.
571
-                            $valuePlaceholder .= sprintf('%s, ', $this->getType($subValue)); // glynn
572
-                            $bindings[] = $this->getValue($subValue);
573
-                        }
574
-
575
-                        $valuePlaceholder = trim($valuePlaceholder, ', ');
576
-                        $criteria .= ' (' . $valuePlaceholder . ') ';
577
-                        break;
578
-                }
579
-            } elseif ($value instanceof Raw) {
580
-                $value = $this->parseRaw($value);
581
-                $criteria .= "{$statement['joiner']} {$key} {$statement['operator']} $value ";
582
-            } else {
583
-                // Usual where like criteria
584
-                if (!$bindValues) {
585
-                    // Specially for joins
586
-                    // We are not binding values, lets sanitize then
587
-                    $value = $this->stringifyValue($this->wrapSanitizer($value)) ?? '';
588
-                    $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' ' . $value . ' ';
589
-                } elseif ($statement['key'] instanceof Raw) {
590
-                    $criteria .= $statement['joiner'] . ' ' . $key . ' ';
591
-                    $bindings = array_merge($bindings, $statement['key']->getBindings());
592
-                } else {
593
-                    // For wheres
594
-                    $bindings[] = $this->getValue($value);
595
-
596
-                    $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' '
597
-                    . $this->getType($value) . ' ';
598
-                }
599
-            }
600
-        }
601
-
602
-        // Clear all white spaces, and, or from beginning and white spaces from ending
603
-        $criteria = preg_replace('/^(\s?AND ?|\s?OR ?)|\s$/i', '', $criteria);
604
-
605
-        return [$criteria ?? '', $bindings];
606
-    }
607
-
608
-    /**
609
-     * Asserts the types place holder based on its value
610
-     *
611
-     * @param mixed $value
612
-     *
613
-     * @return string
614
-     */
615
-    public function inferType($value): string
616
-    {
617
-        switch (true) {
618
-            case is_string($value):
619
-                return '%s';
620
-            case \is_int($value):
621
-            case is_bool($value):
622
-                return '%d';
623
-            case is_float($value):
624
-                return '%f';
625
-            default:
626
-                return '';
627
-        }
628
-    }
629
-
630
-    /**
631
-     * Wrap values with adapter's sanitizer like, '`'
632
-     *
633
-     * @param string|Raw|Closure $value
634
-     *
635
-     * @return string|Closure
636
-     */
637
-    public function wrapSanitizer($value)
638
-    {
639
-        // Its a raw query, just cast as string, object has __toString()
640
-        if ($value instanceof Raw) {
641
-            return $this->parseRaw($value);
642
-        } elseif ($value instanceof Closure) {
643
-            return $value;
644
-        }
645
-
646
-        // Separate our table and fields which are joined with a ".",
647
-        // like my_table.id
648
-        $valueArr = explode('.', $value, 2);
649
-
650
-        foreach ($valueArr as $key => $subValue) {
651
-            // Don't wrap if we have *, which is not a usual field
652
-            $valueArr[$key] = '*' == trim($subValue) ? $subValue : $this->sanitizer . $subValue . $this->sanitizer;
653
-        }
654
-
655
-        // Join these back with "." and return
656
-        return implode('.', $valueArr);
657
-    }
658
-
659
-    /**
660
-     * Build criteria string and binding with various types added, like WHERE and Having
661
-     *
662
-     * @param array<string|Closure, mixed|mixed[]> $statements
663
-     * @param string $key
664
-     * @param string $type
665
-     * @param bool $bindValues
666
-     *
667
-     * @return array{0:string, 1:string[]}
668
-     */
669
-    protected function buildCriteriaWithType(array $statements, string $key, string $type, bool $bindValues = true)
670
-    {
671
-        $criteria = '';
672
-        $bindings = [];
673
-
674
-        if (isset($statements[$key])) {
675
-            // Get the generic/adapter agnostic criteria string from parent
676
-            list($criteria, $bindings) = $this->buildCriteria($statements[$key], $bindValues);
677
-
678
-            if ($criteria) {
679
-                $criteria = $type . ' ' . $criteria;
680
-            }
681
-        }
682
-
683
-        // Remove any multiple whitespace.
684
-        $criteria = (string) preg_replace('!\s+!', ' ', $criteria);
685
-
686
-        return [$criteria, $bindings];
687
-    }
688
-
689
-    /**
690
-     * Build join string
691
-     *
692
-     * @param array<string|Closure, mixed|mixed[]> $statements
693
-     *
694
-     * @return string
695
-     */
696
-    protected function buildJoin(array $statements): string
697
-    {
698
-        $sql = '';
699
-
700
-        if (!array_key_exists('joins', $statements) || !is_array($statements['joins'])) {
701
-            return $sql;
702
-        }
703
-
704
-        foreach ($statements['joins'] as $joinArr) {
705
-            if (is_array($joinArr['table'])) {
706
-                $mainTable  = $this->stringifyValue($this->wrapSanitizer($joinArr['table'][0]));
707
-                $aliasTable = $this->stringifyValue($this->wrapSanitizer($joinArr['table'][1]));
708
-                $table      = $mainTable . ' AS ' . $aliasTable;
709
-            } else {
710
-                $table = $joinArr['table'] instanceof Raw
711
-                    ? $this->parseRaw($joinArr['table'])
712
-                    : $this->wrapSanitizer($joinArr['table']);
713
-            }
714
-            $joinBuilder = $joinArr['joinBuilder'];
715
-
716
-            /** @var string[] */
717
-            $sqlArr = [
718
-                $sql,
719
-                strtoupper($joinArr['type']),
720
-                'JOIN',
721
-                $table,
722
-                'ON',
723
-                $joinBuilder->getQuery('criteriaOnly', false)->getSql(),
724
-            ];
725
-
726
-            $sql = $this->concatenateQuery($sqlArr);
727
-        }
728
-
729
-        return $sql;
730
-    }
20
+	/**
21
+	 * @var string
22
+	 */
23
+	protected $sanitizer = '';
24
+
25
+	/**
26
+	 * @var \Pixie\Connection
27
+	 */
28
+	protected $connection;
29
+
30
+	/**
31
+	 * @var \Viocon\Container
32
+	 */
33
+	protected $container;
34
+
35
+	public function __construct(Connection $connection)
36
+	{
37
+		$this->connection = $connection;
38
+		$this->container  = $this->connection->getContainer();
39
+	}
40
+
41
+	/**
42
+	 * Build select query string and bindings
43
+	 *
44
+	 * @param array<string|Closure, mixed|mixed[]> $statements
45
+	 *
46
+	 * @throws Exception
47
+	 *
48
+	 * @return array{sql:string,bindings:mixed[]}
49
+	 */
50
+	public function select(array $statements): array
51
+	{
52
+		if (!array_key_exists('tables', $statements)) {
53
+			throw new Exception('No table specified.', 3);
54
+		} elseif (!array_key_exists('selects', $statements)) {
55
+			$statements['selects'][] = '*';
56
+		}
57
+
58
+		// From
59
+		$tables = $this->arrayStr($statements['tables'], ', ');
60
+		// Select
61
+		$selects = $this->arrayStr($statements['selects'], ', ');
62
+
63
+		// Wheres
64
+		list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE');
65
+
66
+		// Group bys
67
+		$groupBys = '';
68
+		if (isset($statements['groupBys']) && $groupBys = $this->arrayStr($statements['groupBys'], ', ')) {
69
+			$groupBys = 'GROUP BY ' . $groupBys;
70
+		}
71
+
72
+		// Order bys
73
+		$orderBys = '';
74
+		if (isset($statements['orderBys']) && is_array($statements['orderBys'])) {
75
+			foreach ($statements['orderBys'] as $orderBy) {
76
+				$field = $this->wrapSanitizer($orderBy['field']);
77
+				if ($field instanceof Closure) {
78
+					continue;
79
+				}
80
+				$orderBys .= $field . ' ' . $orderBy['type'] . ', ';
81
+			}
82
+
83
+			if ($orderBys = trim($orderBys, ', ')) {
84
+				$orderBys = 'ORDER BY ' . $orderBys;
85
+			}
86
+		}
87
+
88
+		// Limit and offset
89
+		$limit  = isset($statements['limit']) ? 'LIMIT ' . (int) $statements['limit'] : '';
90
+		$offset = isset($statements['offset']) ? 'OFFSET ' . (int) $statements['offset'] : '';
91
+
92
+		// Having
93
+		list($havingCriteria, $havingBindings) = $this->buildCriteriaWithType($statements, 'havings', 'HAVING');
94
+
95
+		// Joins
96
+		$joinString = $this->buildJoin($statements);
97
+
98
+		/** @var string[] */
99
+		$sqlArray = [
100
+			'SELECT' . (isset($statements['distinct']) ? ' DISTINCT' : ''),
101
+			$selects,
102
+			'FROM',
103
+			$tables,
104
+			$joinString,
105
+			$whereCriteria,
106
+			$groupBys,
107
+			$havingCriteria,
108
+			$orderBys,
109
+			$limit,
110
+			$offset,
111
+		];
112
+
113
+		$sql = $this->concatenateQuery($sqlArray);
114
+
115
+		$bindings = array_merge(
116
+			$whereBindings,
117
+			$havingBindings
118
+		);
119
+
120
+		return compact('sql', 'bindings');
121
+	}
122
+
123
+	/**
124
+	 * Build just criteria part of the query
125
+	 *
126
+	 * @param array<string|Closure, mixed|mixed[]> $statements
127
+	 * @param bool $bindValues
128
+	 *
129
+	 * @return array{sql:string[]|string, bindings:array<mixed>}
130
+	 */
131
+	public function criteriaOnly(array $statements, bool $bindValues = true): array
132
+	{
133
+		$sql = $bindings = [];
134
+		if (!isset($statements['criteria'])) {
135
+			return compact('sql', 'bindings');
136
+		}
137
+
138
+		list($sql, $bindings) = $this->buildCriteria($statements['criteria'], $bindValues);
139
+
140
+		return compact('sql', 'bindings');
141
+	}
142
+
143
+	/**
144
+	 * Build a generic insert/ignore/replace query
145
+	 *
146
+	 * @param array<string|Closure, mixed|mixed[]> $statements
147
+	 * @param array<string, mixed> $data
148
+	 * @param string $type
149
+	 *
150
+	 * @return array{sql:string, bindings:mixed[]}
151
+	 *
152
+	 * @throws Exception
153
+	 */
154
+	private function doInsert(array $statements, array $data, string $type): array
155
+	{
156
+		if (!isset($statements['tables'])) {
157
+			throw new Exception('No table specified', 3);
158
+		}
159
+
160
+		$table = end($statements['tables']);
161
+
162
+		$bindings = $keys = $values = [];
163
+
164
+		foreach ($data as $key => $value) {
165
+			$keys[] = $key;
166
+
167
+			// Handle value as bindings
168
+			$isBindings = $value instanceof Binding;
169
+			// If this is a raw binding, extract the Raw and replace value.
170
+			if ($isBindings && $value->isRaw()) {
171
+				$value = $value->getValue();
172
+			}
173
+
174
+			if ($value instanceof Raw) {
175
+				$values[] = $this->parseRaw($value);
176
+			} elseif ($isBindings) {
177
+				$values[]   =  $value->getType();
178
+				$bindings[] = $value->getValue();
179
+			} else {
180
+				$values[]   =  $this->inferType($value);
181
+				$bindings[] = $value;
182
+			}
183
+		}
184
+
185
+		$sqlArray = [
186
+		$type . ' INTO',
187
+		$this->wrapSanitizer($table),
188
+		'(' . $this->arrayStr($keys, ',') . ')',
189
+		'VALUES',
190
+		'(' . $this->arrayStr($values, ',') . ')',
191
+		];
192
+
193
+		if (isset($statements['onduplicate'])) {
194
+			if (count($statements['onduplicate']) < 1) {
195
+				throw new Exception('No data given.', 4);
196
+			}
197
+			list($updateStatement, $updateBindings) = $this->getUpdateStatement($statements['onduplicate']);
198
+			$sqlArray[]                             = 'ON DUPLICATE KEY UPDATE ' . $updateStatement;
199
+			$bindings                               = array_merge($bindings, $updateBindings);
200
+		}
201
+
202
+		$sql = $this->concatenateQuery($this->stringifyValues($sqlArray));
203
+
204
+		return compact('sql', 'bindings');
205
+	}
206
+
207
+	/**
208
+	 * Attempts to stringify an array of values.
209
+	 *
210
+	 * @param array<string|int, string|Closure> $values
211
+	 *
212
+	 * @return string[]
213
+	 */
214
+	protected function stringifyValues(array $values): array
215
+	{
216
+		return array_filter(array_map([$this, 'stringifyValue'], $values));
217
+	}
218
+
219
+	/**
220
+	 * Attempts to stringify a single of values.
221
+	 *
222
+	 * @param string|Closure|Raw $value
223
+	 *
224
+	 * @return string|null
225
+	 */
226
+	protected function stringifyValue($value): ?string
227
+	{
228
+		if ($value instanceof Closure) {
229
+			$value = $value();
230
+
231
+			return is_string($value) ? $value : null;
232
+		}
233
+
234
+		if ($value instanceof Raw) {
235
+			return $this->parseRaw($value);
236
+		}
237
+
238
+		return $value;
239
+	}
240
+
241
+	/**
242
+	 * Build Insert query
243
+	 *
244
+	 * @param array<string|Closure, mixed|mixed[]> $statements
245
+	 * @param array<string, mixed> $data $data
246
+	 *
247
+	 * @return array{sql:string, bindings:mixed[]}
248
+	 *
249
+	 * @throws Exception
250
+	 */
251
+	public function insert($statements, array $data)
252
+	{
253
+		return $this->doInsert($statements, $data, 'INSERT');
254
+	}
255
+
256
+	/**
257
+	 * Build Insert Ignore query
258
+	 *
259
+	 * @param array<string|Closure, mixed|mixed[]> $statements
260
+	 * @param array<string, mixed> $data $data
261
+	 *
262
+	 * @return array{sql:string, bindings:mixed[]}
263
+	 *
264
+	 * @throws Exception
265
+	 */
266
+	public function insertIgnore($statements, array $data)
267
+	{
268
+		return $this->doInsert($statements, $data, 'INSERT IGNORE');
269
+	}
270
+
271
+	/**
272
+	 * Build Insert Ignore query
273
+	 *
274
+	 * @param array<string|Closure, mixed|mixed[]> $statements
275
+	 * @param array<string, mixed> $data $data
276
+	 *
277
+	 * @return array{sql:string, bindings:mixed[]}
278
+	 *
279
+	 * @throws Exception
280
+	 */
281
+	public function replace($statements, array $data)
282
+	{
283
+		return $this->doInsert($statements, $data, 'REPLACE');
284
+	}
285
+
286
+	/**
287
+	 * Build fields assignment part of SET ... or ON DUBLICATE KEY UPDATE ... statements
288
+	 *
289
+	 * @param array<string, mixed> $data
290
+	 *
291
+	 * @return array{0:string,1:mixed[]}
292
+	 */
293
+	private function getUpdateStatement(array $data): array
294
+	{
295
+		$bindings  = [];
296
+		$statement = '';
297
+
298
+		foreach ($data as $key => $value) {
299
+			$isBindings = $value instanceof Binding;
300
+			// If this is a raw binding, extract the Raw and replace value.
301
+			if ($isBindings && $value->isRaw()) {
302
+				$value = $value->getValue();
303
+			}
304
+
305
+			if ($value instanceof Raw) {
306
+				$statement .= $this->stringifyValue($this->wrapSanitizer($key)) . '=' . $value . ',';
307
+			} elseif ($isBindings) {
308
+				$statement .= $this->stringifyValue($this->wrapSanitizer($key)) . sprintf('=%s,', $value->getType());
309
+				$bindings[] = $value->getValue();
310
+			} else {
311
+				$statement .= $this->stringifyValue($this->wrapSanitizer($key)) . sprintf('=%s,', $this->inferType($value));
312
+				$bindings[] = $value;
313
+			}
314
+		}
315
+
316
+		$statement = trim($statement, ',');
317
+
318
+		return [$statement, $bindings];
319
+	}
320
+
321
+	/**
322
+	 * Build update query
323
+	 *
324
+	 * @param array<string|Closure, mixed|mixed[]> $statements
325
+	 * @param array<string, mixed> $data
326
+	 *
327
+	 * @return array{sql:string, bindings:mixed[]}
328
+	 *
329
+	 * @throws Exception
330
+	 */
331
+	public function update($statements, array $data)
332
+	{
333
+		if (!isset($statements['tables'])) {
334
+			throw new Exception('No table specified', 3);
335
+		} elseif (count($data) < 1) {
336
+			throw new Exception('No data given.', 4);
337
+		}
338
+
339
+		$table = end($statements['tables']);
340
+
341
+		// Update statement
342
+		list($updateStatement, $bindings) = $this->getUpdateStatement($data);
343
+
344
+		// Wheres
345
+		list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE');
346
+
347
+		// Limit
348
+		$limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : '';
349
+
350
+		$sqlArray = [
351
+			'UPDATE',
352
+			$this->wrapSanitizer($table),
353
+			'SET ' . $updateStatement,
354
+			$whereCriteria,
355
+			$limit,
356
+		];
357
+
358
+		$sql = $this->concatenateQuery($this->stringifyValues($sqlArray));
359
+
360
+		$bindings = array_merge($bindings, $whereBindings);
361
+
362
+		return compact('sql', 'bindings');
363
+	}
364
+
365
+	/**
366
+	 * Build delete query
367
+	 *
368
+	 * @param array<string|Closure, mixed|mixed[]> $statements
369
+	 *
370
+	 * @return array{sql:string, bindings:mixed[]}
371
+	 *
372
+	 * @throws Exception
373
+	 */
374
+	public function delete($statements)
375
+	{
376
+		if (!isset($statements['tables'])) {
377
+			throw new Exception('No table specified', 3);
378
+		}
379
+
380
+		$table = end($statements['tables']);
381
+		// Ensure table name is a string
382
+		$table = $this->stringifyValue($this->wrapSanitizer($table));
383
+		if (null === $table) {
384
+			throw new Exception('Table must be a valid string.', 5);
385
+		}
386
+
387
+		// Wheres
388
+		list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE');
389
+
390
+		// Limit
391
+		$limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : '';
392
+
393
+		$sqlArray = ['DELETE FROM', $table, $whereCriteria];
394
+		$sql      = $this->concatenateQuery($sqlArray);
395
+		$bindings = $whereBindings;
396
+
397
+		return compact('sql', 'bindings');
398
+	}
399
+
400
+	/**
401
+	 * Array concatenating method, like implode.
402
+	 * But it does wrap sanitizer and trims last glue
403
+	 *
404
+	 * @param array<string|int, string> $pieces
405
+	 * @param string $glue
406
+	 *
407
+	 * @return string
408
+	 */
409
+	protected function arrayStr(array $pieces, string $glue): string
410
+	{
411
+		$str = '';
412
+		foreach ($pieces as $key => $piece) {
413
+			if (!is_int($key)) {
414
+				$piece = $key . ' AS ' . $piece;
415
+			}
416
+
417
+			$str .= $piece . $glue;
418
+		}
419
+
420
+		return trim($str, $glue);
421
+	}
422
+
423
+	/**
424
+	 * Join different part of queries with a space.
425
+	 *
426
+	 * @param array<string|int, string> $pieces
427
+	 *
428
+	 * @return string
429
+	 */
430
+	protected function concatenateQuery(array $pieces): string
431
+	{
432
+		$str = '';
433
+		foreach ($pieces as $piece) {
434
+			$str = trim($str) . ' ' . trim($piece);
435
+		}
436
+
437
+		return trim($str);
438
+	}
439
+
440
+	/**
441
+	 * Gets the type of a value, either from a binding or infered
442
+	 *
443
+	 * @param mixed $value
444
+	 * @return string
445
+	 */
446
+	public function getType($value): string
447
+	{
448
+		return $value instanceof Binding && $value->getType() !== null
449
+			? $value->getType() : $this->inferType($value) ;
450
+	}
451
+
452
+	/**
453
+	 * Get the value from a possible Bindings object.
454
+	 *
455
+	 * @param mixed $value
456
+	 * @return mixed
457
+	 */
458
+	public function getValue($value)
459
+	{
460
+		return $value instanceof Binding ? $value->getValue() : $value;
461
+	}
462
+
463
+	/**
464
+	 * Attempts to parse a raw query, if bindings are defined then they will be bound first.
465
+	 *
466
+	 * @param Raw $raw
467
+	 * @requires string
468
+	 */
469
+	public function parseRaw(Raw $raw): string
470
+	{
471
+		$bindings = $raw->getBindings();
472
+		return 0 === count($bindings)
473
+			? (string) $raw
474
+			: $this->interpolateQuery($raw->getValue(), $bindings);
475
+	}
476
+
477
+	/**
478
+	 * Interpolates a query
479
+	 *
480
+	 * @param string $query
481
+	 * @param array<mixed> $bindings
482
+	 * @return string
483
+	 */
484
+	public function interpolateQuery(string $query, array $bindings = []): string
485
+	{
486
+		if (0 === count($bindings)) {
487
+			return $query;
488
+		}
489
+
490
+
491
+		$bindings = array_map([$this, 'getValue'], $bindings);
492
+		$query = $this->connection->getDbInstance()->prepare($query, $bindings) ;
493
+		return is_string($query) ? $query : '';
494
+	}
495
+
496
+	/**
497
+	 * Build generic criteria string and bindings from statements, like "a = b and c = ?"
498
+	 *
499
+	 * @param array<string|Closure, mixed|mixed[]> $statements
500
+	 * @param bool $bindValues
501
+	 *
502
+	 * @return array{0:string,1:string[]}
503
+	 */
504
+	protected function buildCriteria(array $statements, bool $bindValues = true): array
505
+	{
506
+		$criteria = '';
507
+		$bindings = [];
508
+		foreach ($statements as $statement) {
509
+			$key   = $statement['key'];
510
+			$value = $statement['value'];
511
+
512
+			// If the value is a Raw Binding, cast to raw
513
+			if ($value instanceof Binding && Binding::RAW === $value->getType()) {
514
+				/** @var Raw */
515
+				$value = $value->getValue();
516
+			}
517
+
518
+			if (is_null($value) && $key instanceof Closure) {
519
+				// We have a closure, a nested criteria
520
+
521
+				// Build a new NestedCriteria class, keep it by reference so any changes made
522
+				// in the closure should reflect here
523
+				$nestedCriteria = $this->container->build(NestedCriteria::class, [$this->connection]);
524
+
525
+				$nestedCriteria = &$nestedCriteria;
526
+				// Call the closure with our new nestedCriteria object
527
+				$key($nestedCriteria);
528
+				// Get the criteria only query from the nestedCriteria object
529
+				$queryObject = $nestedCriteria->getQuery('criteriaOnly', true);
530
+				// Merge the bindings we get from nestedCriteria object
531
+				$bindings = array_merge($bindings, $queryObject->getBindings());
532
+				// Append the sql we get from the nestedCriteria object
533
+				$criteria .= $statement['joiner'] . ' (' . $queryObject->getSql() . ') ';
534
+			} elseif (is_array($value)) {
535
+				// where_in or between like query
536
+				$criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'];
537
+
538
+				switch ($statement['operator']) {
539
+					case 'BETWEEN':
540
+						$bindings = array_merge($bindings, $statement['value']);
541
+						$criteria .= sprintf(
542
+							' %s AND %s ',
543
+							$this->getType($value[0]),
544
+							$this->getType($value[1])
545
+						);
546
+
547
+						// Maybe cast the values bindings.
548
+						$value[0] = $this->getValue($value[0]);
549
+						$value[1] = $this->getValue($value[1]);
550
+
551
+						// Parse any raws.
552
+						$value = array_map(function ($value) {
553
+							return $value instanceof Raw
554
+								? $this->parseRaw($value)
555
+								: $value;
556
+						}, $value);
557
+						break;
558
+					default:
559
+						$valuePlaceholder = '';
560
+						foreach ($statement['value'] as $subValue) {
561
+							// Get its value.
562
+							if ($this->getValue($subValue) instanceof Raw) {
563
+								/** @var Raw $subValue */
564
+								$subValue = $this->getValue($subValue);
565
+								$valuePlaceholder .= sprintf('%s, ', $this->parseRaw($subValue));
566
+								continue;
567
+							}
568
+
569
+
570
+							// Add in format placeholders.
571
+							$valuePlaceholder .= sprintf('%s, ', $this->getType($subValue)); // glynn
572
+							$bindings[] = $this->getValue($subValue);
573
+						}
574
+
575
+						$valuePlaceholder = trim($valuePlaceholder, ', ');
576
+						$criteria .= ' (' . $valuePlaceholder . ') ';
577
+						break;
578
+				}
579
+			} elseif ($value instanceof Raw) {
580
+				$value = $this->parseRaw($value);
581
+				$criteria .= "{$statement['joiner']} {$key} {$statement['operator']} $value ";
582
+			} else {
583
+				// Usual where like criteria
584
+				if (!$bindValues) {
585
+					// Specially for joins
586
+					// We are not binding values, lets sanitize then
587
+					$value = $this->stringifyValue($this->wrapSanitizer($value)) ?? '';
588
+					$criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' ' . $value . ' ';
589
+				} elseif ($statement['key'] instanceof Raw) {
590
+					$criteria .= $statement['joiner'] . ' ' . $key . ' ';
591
+					$bindings = array_merge($bindings, $statement['key']->getBindings());
592
+				} else {
593
+					// For wheres
594
+					$bindings[] = $this->getValue($value);
595
+
596
+					$criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' '
597
+					. $this->getType($value) . ' ';
598
+				}
599
+			}
600
+		}
601
+
602
+		// Clear all white spaces, and, or from beginning and white spaces from ending
603
+		$criteria = preg_replace('/^(\s?AND ?|\s?OR ?)|\s$/i', '', $criteria);
604
+
605
+		return [$criteria ?? '', $bindings];
606
+	}
607
+
608
+	/**
609
+	 * Asserts the types place holder based on its value
610
+	 *
611
+	 * @param mixed $value
612
+	 *
613
+	 * @return string
614
+	 */
615
+	public function inferType($value): string
616
+	{
617
+		switch (true) {
618
+			case is_string($value):
619
+				return '%s';
620
+			case \is_int($value):
621
+			case is_bool($value):
622
+				return '%d';
623
+			case is_float($value):
624
+				return '%f';
625
+			default:
626
+				return '';
627
+		}
628
+	}
629
+
630
+	/**
631
+	 * Wrap values with adapter's sanitizer like, '`'
632
+	 *
633
+	 * @param string|Raw|Closure $value
634
+	 *
635
+	 * @return string|Closure
636
+	 */
637
+	public function wrapSanitizer($value)
638
+	{
639
+		// Its a raw query, just cast as string, object has __toString()
640
+		if ($value instanceof Raw) {
641
+			return $this->parseRaw($value);
642
+		} elseif ($value instanceof Closure) {
643
+			return $value;
644
+		}
645
+
646
+		// Separate our table and fields which are joined with a ".",
647
+		// like my_table.id
648
+		$valueArr = explode('.', $value, 2);
649
+
650
+		foreach ($valueArr as $key => $subValue) {
651
+			// Don't wrap if we have *, which is not a usual field
652
+			$valueArr[$key] = '*' == trim($subValue) ? $subValue : $this->sanitizer . $subValue . $this->sanitizer;
653
+		}
654
+
655
+		// Join these back with "." and return
656
+		return implode('.', $valueArr);
657
+	}
658
+
659
+	/**
660
+	 * Build criteria string and binding with various types added, like WHERE and Having
661
+	 *
662
+	 * @param array<string|Closure, mixed|mixed[]> $statements
663
+	 * @param string $key
664
+	 * @param string $type
665
+	 * @param bool $bindValues
666
+	 *
667
+	 * @return array{0:string, 1:string[]}
668
+	 */
669
+	protected function buildCriteriaWithType(array $statements, string $key, string $type, bool $bindValues = true)
670
+	{
671
+		$criteria = '';
672
+		$bindings = [];
673
+
674
+		if (isset($statements[$key])) {
675
+			// Get the generic/adapter agnostic criteria string from parent
676
+			list($criteria, $bindings) = $this->buildCriteria($statements[$key], $bindValues);
677
+
678
+			if ($criteria) {
679
+				$criteria = $type . ' ' . $criteria;
680
+			}
681
+		}
682
+
683
+		// Remove any multiple whitespace.
684
+		$criteria = (string) preg_replace('!\s+!', ' ', $criteria);
685
+
686
+		return [$criteria, $bindings];
687
+	}
688
+
689
+	/**
690
+	 * Build join string
691
+	 *
692
+	 * @param array<string|Closure, mixed|mixed[]> $statements
693
+	 *
694
+	 * @return string
695
+	 */
696
+	protected function buildJoin(array $statements): string
697
+	{
698
+		$sql = '';
699
+
700
+		if (!array_key_exists('joins', $statements) || !is_array($statements['joins'])) {
701
+			return $sql;
702
+		}
703
+
704
+		foreach ($statements['joins'] as $joinArr) {
705
+			if (is_array($joinArr['table'])) {
706
+				$mainTable  = $this->stringifyValue($this->wrapSanitizer($joinArr['table'][0]));
707
+				$aliasTable = $this->stringifyValue($this->wrapSanitizer($joinArr['table'][1]));
708
+				$table      = $mainTable . ' AS ' . $aliasTable;
709
+			} else {
710
+				$table = $joinArr['table'] instanceof Raw
711
+					? $this->parseRaw($joinArr['table'])
712
+					: $this->wrapSanitizer($joinArr['table']);
713
+			}
714
+			$joinBuilder = $joinArr['joinBuilder'];
715
+
716
+			/** @var string[] */
717
+			$sqlArr = [
718
+				$sql,
719
+				strtoupper($joinArr['type']),
720
+				'JOIN',
721
+				$table,
722
+				'ON',
723
+				$joinBuilder->getQuery('criteriaOnly', false)->getSql(),
724
+			];
725
+
726
+			$sql = $this->concatenateQuery($sqlArr);
727
+		}
728
+
729
+		return $sql;
730
+	}
731 731
 }
Please login to merge, or discard this patch.
Spacing   +43 added lines, -43 removed lines patch added patch discarded remove patch
@@ -49,9 +49,9 @@  discard block
 block discarded – undo
49 49
      */
50 50
     public function select(array $statements): array
51 51
     {
52
-        if (!array_key_exists('tables', $statements)) {
52
+        if ( ! array_key_exists('tables', $statements)) {
53 53
             throw new Exception('No table specified.', 3);
54
-        } elseif (!array_key_exists('selects', $statements)) {
54
+        } elseif ( ! array_key_exists('selects', $statements)) {
55 55
             $statements['selects'][] = '*';
56 56
         }
57 57
 
@@ -66,7 +66,7 @@  discard block
 block discarded – undo
66 66
         // Group bys
67 67
         $groupBys = '';
68 68
         if (isset($statements['groupBys']) && $groupBys = $this->arrayStr($statements['groupBys'], ', ')) {
69
-            $groupBys = 'GROUP BY ' . $groupBys;
69
+            $groupBys = 'GROUP BY '.$groupBys;
70 70
         }
71 71
 
72 72
         // Order bys
@@ -77,17 +77,17 @@  discard block
 block discarded – undo
77 77
                 if ($field instanceof Closure) {
78 78
                     continue;
79 79
                 }
80
-                $orderBys .= $field . ' ' . $orderBy['type'] . ', ';
80
+                $orderBys .= $field.' '.$orderBy['type'].', ';
81 81
             }
82 82
 
83 83
             if ($orderBys = trim($orderBys, ', ')) {
84
-                $orderBys = 'ORDER BY ' . $orderBys;
84
+                $orderBys = 'ORDER BY '.$orderBys;
85 85
             }
86 86
         }
87 87
 
88 88
         // Limit and offset
89
-        $limit  = isset($statements['limit']) ? 'LIMIT ' . (int) $statements['limit'] : '';
90
-        $offset = isset($statements['offset']) ? 'OFFSET ' . (int) $statements['offset'] : '';
89
+        $limit  = isset($statements['limit']) ? 'LIMIT '.(int) $statements['limit'] : '';
90
+        $offset = isset($statements['offset']) ? 'OFFSET '.(int) $statements['offset'] : '';
91 91
 
92 92
         // Having
93 93
         list($havingCriteria, $havingBindings) = $this->buildCriteriaWithType($statements, 'havings', 'HAVING');
@@ -97,7 +97,7 @@  discard block
 block discarded – undo
97 97
 
98 98
         /** @var string[] */
99 99
         $sqlArray = [
100
-            'SELECT' . (isset($statements['distinct']) ? ' DISTINCT' : ''),
100
+            'SELECT'.(isset($statements['distinct']) ? ' DISTINCT' : ''),
101 101
             $selects,
102 102
             'FROM',
103 103
             $tables,
@@ -131,7 +131,7 @@  discard block
 block discarded – undo
131 131
     public function criteriaOnly(array $statements, bool $bindValues = true): array
132 132
     {
133 133
         $sql = $bindings = [];
134
-        if (!isset($statements['criteria'])) {
134
+        if ( ! isset($statements['criteria'])) {
135 135
             return compact('sql', 'bindings');
136 136
         }
137 137
 
@@ -153,7 +153,7 @@  discard block
 block discarded – undo
153 153
      */
154 154
     private function doInsert(array $statements, array $data, string $type): array
155 155
     {
156
-        if (!isset($statements['tables'])) {
156
+        if ( ! isset($statements['tables'])) {
157 157
             throw new Exception('No table specified', 3);
158 158
         }
159 159
 
@@ -174,20 +174,20 @@  discard block
 block discarded – undo
174 174
             if ($value instanceof Raw) {
175 175
                 $values[] = $this->parseRaw($value);
176 176
             } elseif ($isBindings) {
177
-                $values[]   =  $value->getType();
177
+                $values[]   = $value->getType();
178 178
                 $bindings[] = $value->getValue();
179 179
             } else {
180
-                $values[]   =  $this->inferType($value);
180
+                $values[]   = $this->inferType($value);
181 181
                 $bindings[] = $value;
182 182
             }
183 183
         }
184 184
 
185 185
         $sqlArray = [
186
-        $type . ' INTO',
186
+        $type.' INTO',
187 187
         $this->wrapSanitizer($table),
188
-        '(' . $this->arrayStr($keys, ',') . ')',
188
+        '('.$this->arrayStr($keys, ',').')',
189 189
         'VALUES',
190
-        '(' . $this->arrayStr($values, ',') . ')',
190
+        '('.$this->arrayStr($values, ',').')',
191 191
         ];
192 192
 
193 193
         if (isset($statements['onduplicate'])) {
@@ -195,7 +195,7 @@  discard block
 block discarded – undo
195 195
                 throw new Exception('No data given.', 4);
196 196
             }
197 197
             list($updateStatement, $updateBindings) = $this->getUpdateStatement($statements['onduplicate']);
198
-            $sqlArray[]                             = 'ON DUPLICATE KEY UPDATE ' . $updateStatement;
198
+            $sqlArray[]                             = 'ON DUPLICATE KEY UPDATE '.$updateStatement;
199 199
             $bindings                               = array_merge($bindings, $updateBindings);
200 200
         }
201 201
 
@@ -303,12 +303,12 @@  discard block
 block discarded – undo
303 303
             }
304 304
 
305 305
             if ($value instanceof Raw) {
306
-                $statement .= $this->stringifyValue($this->wrapSanitizer($key)) . '=' . $value . ',';
306
+                $statement .= $this->stringifyValue($this->wrapSanitizer($key)).'='.$value.',';
307 307
             } elseif ($isBindings) {
308
-                $statement .= $this->stringifyValue($this->wrapSanitizer($key)) . sprintf('=%s,', $value->getType());
308
+                $statement .= $this->stringifyValue($this->wrapSanitizer($key)).sprintf('=%s,', $value->getType());
309 309
                 $bindings[] = $value->getValue();
310 310
             } else {
311
-                $statement .= $this->stringifyValue($this->wrapSanitizer($key)) . sprintf('=%s,', $this->inferType($value));
311
+                $statement .= $this->stringifyValue($this->wrapSanitizer($key)).sprintf('=%s,', $this->inferType($value));
312 312
                 $bindings[] = $value;
313 313
             }
314 314
         }
@@ -330,7 +330,7 @@  discard block
 block discarded – undo
330 330
      */
331 331
     public function update($statements, array $data)
332 332
     {
333
-        if (!isset($statements['tables'])) {
333
+        if ( ! isset($statements['tables'])) {
334 334
             throw new Exception('No table specified', 3);
335 335
         } elseif (count($data) < 1) {
336 336
             throw new Exception('No data given.', 4);
@@ -345,12 +345,12 @@  discard block
 block discarded – undo
345 345
         list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE');
346 346
 
347 347
         // Limit
348
-        $limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : '';
348
+        $limit = isset($statements['limit']) ? 'LIMIT '.$statements['limit'] : '';
349 349
 
350 350
         $sqlArray = [
351 351
             'UPDATE',
352 352
             $this->wrapSanitizer($table),
353
-            'SET ' . $updateStatement,
353
+            'SET '.$updateStatement,
354 354
             $whereCriteria,
355 355
             $limit,
356 356
         ];
@@ -373,7 +373,7 @@  discard block
 block discarded – undo
373 373
      */
374 374
     public function delete($statements)
375 375
     {
376
-        if (!isset($statements['tables'])) {
376
+        if ( ! isset($statements['tables'])) {
377 377
             throw new Exception('No table specified', 3);
378 378
         }
379 379
 
@@ -388,7 +388,7 @@  discard block
 block discarded – undo
388 388
         list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE');
389 389
 
390 390
         // Limit
391
-        $limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : '';
391
+        $limit = isset($statements['limit']) ? 'LIMIT '.$statements['limit'] : '';
392 392
 
393 393
         $sqlArray = ['DELETE FROM', $table, $whereCriteria];
394 394
         $sql      = $this->concatenateQuery($sqlArray);
@@ -410,11 +410,11 @@  discard block
 block discarded – undo
410 410
     {
411 411
         $str = '';
412 412
         foreach ($pieces as $key => $piece) {
413
-            if (!is_int($key)) {
414
-                $piece = $key . ' AS ' . $piece;
413
+            if ( ! is_int($key)) {
414
+                $piece = $key.' AS '.$piece;
415 415
             }
416 416
 
417
-            $str .= $piece . $glue;
417
+            $str .= $piece.$glue;
418 418
         }
419 419
 
420 420
         return trim($str, $glue);
@@ -431,7 +431,7 @@  discard block
 block discarded – undo
431 431
     {
432 432
         $str = '';
433 433
         foreach ($pieces as $piece) {
434
-            $str = trim($str) . ' ' . trim($piece);
434
+            $str = trim($str).' '.trim($piece);
435 435
         }
436 436
 
437 437
         return trim($str);
@@ -446,7 +446,7 @@  discard block
 block discarded – undo
446 446
     public function getType($value): string
447 447
     {
448 448
         return $value instanceof Binding && $value->getType() !== null
449
-            ? $value->getType() : $this->inferType($value) ;
449
+            ? $value->getType() : $this->inferType($value);
450 450
     }
451 451
 
452 452
     /**
@@ -489,7 +489,7 @@  discard block
 block discarded – undo
489 489
 
490 490
 
491 491
         $bindings = array_map([$this, 'getValue'], $bindings);
492
-        $query = $this->connection->getDbInstance()->prepare($query, $bindings) ;
492
+        $query = $this->connection->getDbInstance()->prepare($query, $bindings);
493 493
         return is_string($query) ? $query : '';
494 494
     }
495 495
 
@@ -530,10 +530,10 @@  discard block
 block discarded – undo
530 530
                 // Merge the bindings we get from nestedCriteria object
531 531
                 $bindings = array_merge($bindings, $queryObject->getBindings());
532 532
                 // Append the sql we get from the nestedCriteria object
533
-                $criteria .= $statement['joiner'] . ' (' . $queryObject->getSql() . ') ';
533
+                $criteria .= $statement['joiner'].' ('.$queryObject->getSql().') ';
534 534
             } elseif (is_array($value)) {
535 535
                 // where_in or between like query
536
-                $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'];
536
+                $criteria .= $statement['joiner'].' '.$key.' '.$statement['operator'];
537 537
 
538 538
                 switch ($statement['operator']) {
539 539
                     case 'BETWEEN':
@@ -549,7 +549,7 @@  discard block
 block discarded – undo
549 549
                         $value[1] = $this->getValue($value[1]);
550 550
 
551 551
                         // Parse any raws.
552
-                        $value = array_map(function ($value) {
552
+                        $value = array_map(function($value) {
553 553
                             return $value instanceof Raw
554 554
                                 ? $this->parseRaw($value)
555 555
                                 : $value;
@@ -573,7 +573,7 @@  discard block
 block discarded – undo
573 573
                         }
574 574
 
575 575
                         $valuePlaceholder = trim($valuePlaceholder, ', ');
576
-                        $criteria .= ' (' . $valuePlaceholder . ') ';
576
+                        $criteria .= ' ('.$valuePlaceholder.') ';
577 577
                         break;
578 578
                 }
579 579
             } elseif ($value instanceof Raw) {
@@ -581,20 +581,20 @@  discard block
 block discarded – undo
581 581
                 $criteria .= "{$statement['joiner']} {$key} {$statement['operator']} $value ";
582 582
             } else {
583 583
                 // Usual where like criteria
584
-                if (!$bindValues) {
584
+                if ( ! $bindValues) {
585 585
                     // Specially for joins
586 586
                     // We are not binding values, lets sanitize then
587 587
                     $value = $this->stringifyValue($this->wrapSanitizer($value)) ?? '';
588
-                    $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' ' . $value . ' ';
588
+                    $criteria .= $statement['joiner'].' '.$key.' '.$statement['operator'].' '.$value.' ';
589 589
                 } elseif ($statement['key'] instanceof Raw) {
590
-                    $criteria .= $statement['joiner'] . ' ' . $key . ' ';
590
+                    $criteria .= $statement['joiner'].' '.$key.' ';
591 591
                     $bindings = array_merge($bindings, $statement['key']->getBindings());
592 592
                 } else {
593 593
                     // For wheres
594 594
                     $bindings[] = $this->getValue($value);
595 595
 
596
-                    $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' '
597
-                    . $this->getType($value) . ' ';
596
+                    $criteria .= $statement['joiner'].' '.$key.' '.$statement['operator'].' '
597
+                    . $this->getType($value).' ';
598 598
                 }
599 599
             }
600 600
         }
@@ -649,7 +649,7 @@  discard block
 block discarded – undo
649 649
 
650 650
         foreach ($valueArr as $key => $subValue) {
651 651
             // Don't wrap if we have *, which is not a usual field
652
-            $valueArr[$key] = '*' == trim($subValue) ? $subValue : $this->sanitizer . $subValue . $this->sanitizer;
652
+            $valueArr[$key] = '*' == trim($subValue) ? $subValue : $this->sanitizer.$subValue.$this->sanitizer;
653 653
         }
654 654
 
655 655
         // Join these back with "." and return
@@ -676,7 +676,7 @@  discard block
 block discarded – undo
676 676
             list($criteria, $bindings) = $this->buildCriteria($statements[$key], $bindValues);
677 677
 
678 678
             if ($criteria) {
679
-                $criteria = $type . ' ' . $criteria;
679
+                $criteria = $type.' '.$criteria;
680 680
             }
681 681
         }
682 682
 
@@ -697,7 +697,7 @@  discard block
 block discarded – undo
697 697
     {
698 698
         $sql = '';
699 699
 
700
-        if (!array_key_exists('joins', $statements) || !is_array($statements['joins'])) {
700
+        if ( ! array_key_exists('joins', $statements) || ! is_array($statements['joins'])) {
701 701
             return $sql;
702 702
         }
703 703
 
@@ -705,7 +705,7 @@  discard block
 block discarded – undo
705 705
             if (is_array($joinArr['table'])) {
706 706
                 $mainTable  = $this->stringifyValue($this->wrapSanitizer($joinArr['table'][0]));
707 707
                 $aliasTable = $this->stringifyValue($this->wrapSanitizer($joinArr['table'][1]));
708
-                $table      = $mainTable . ' AS ' . $aliasTable;
708
+                $table      = $mainTable.' AS '.$aliasTable;
709 709
             } else {
710 710
                 $table = $joinArr['table'] instanceof Raw
711 711
                     ? $this->parseRaw($joinArr['table'])
Please login to merge, or discard this patch.
src/QueryBuilder/TablePrefixer.php 2 patches
Indentation   +63 added lines, -63 removed lines patch added patch discarded remove patch
@@ -9,77 +9,77 @@
 block discarded – undo
9 9
 
10 10
 trait TablePrefixer
11 11
 {
12
-    /**
13
-         * Add table prefix (if given) on given string.
14
-         *
15
-         * @param array<string|int, string|int|float|bool|Raw|Closure>|string|int|float|bool|Raw|Closure     $values
16
-         * @param bool $tableFieldMix If we have mixes of field and table names with a "."
17
-         *
18
-         * @return mixed|mixed[]
19
-         */
20
-    public function addTablePrefix($values, bool $tableFieldMix = true)
21
-    {
22
-        if (is_null($this->getTablePrefix())) {
23
-            return $values;
24
-        }
12
+	/**
13
+	 * Add table prefix (if given) on given string.
14
+	 *
15
+	 * @param array<string|int, string|int|float|bool|Raw|Closure>|string|int|float|bool|Raw|Closure     $values
16
+	 * @param bool $tableFieldMix If we have mixes of field and table names with a "."
17
+	 *
18
+	 * @return mixed|mixed[]
19
+	 */
20
+	public function addTablePrefix($values, bool $tableFieldMix = true)
21
+	{
22
+		if (is_null($this->getTablePrefix())) {
23
+			return $values;
24
+		}
25 25
 
26
-        // $value will be an array and we will add prefix to all table names
26
+		// $value will be an array and we will add prefix to all table names
27 27
 
28
-        // If supplied value is not an array then make it one
29
-        $single = false;
30
-        if (!is_array($values)) {
31
-            $values = [$values];
32
-            // We had single value, so should return a single value
33
-            $single = true;
34
-        }
28
+		// If supplied value is not an array then make it one
29
+		$single = false;
30
+		if (!is_array($values)) {
31
+			$values = [$values];
32
+			// We had single value, so should return a single value
33
+			$single = true;
34
+		}
35 35
 
36
-        $return = [];
36
+		$return = [];
37 37
 
38
-        foreach ($values as $key => $value) {
39
-            // It's a raw query, just add it to our return array and continue next
40
-            if ($value instanceof Raw || $value instanceof Closure) {
41
-                $return[$key] = $value;
42
-                continue;
43
-            }
38
+		foreach ($values as $key => $value) {
39
+			// It's a raw query, just add it to our return array and continue next
40
+			if ($value instanceof Raw || $value instanceof Closure) {
41
+				$return[$key] = $value;
42
+				continue;
43
+			}
44 44
 
45
-            // If key is not integer, it is likely a alias mapping,
46
-            // so we need to change prefix target
47
-            $target = &$value;
48
-            if (!is_int($key)) {
49
-                $target = &$key;
50
-            }
45
+			// If key is not integer, it is likely a alias mapping,
46
+			// so we need to change prefix target
47
+			$target = &$value;
48
+			if (!is_int($key)) {
49
+				$target = &$key;
50
+			}
51 51
 
52
-            // Do prefix if the target is an expression or function.
53
-            if (
54
-                !$tableFieldMix
55
-                || (
56
-                    is_string($target) // Must be a string
57
-                    && (bool) preg_match('/^[A-Za-z0-9_.]+$/', $target) // Can only contain letters, numbers, underscore and full stops
58
-                    && 1 === \substr_count($target, '.') // Contains a single full stop ONLY.
59
-                )
60
-            ) {
61
-                $target = $this->getTablePrefix() . $target;
62
-            }
52
+			// Do prefix if the target is an expression or function.
53
+			if (
54
+				!$tableFieldMix
55
+				|| (
56
+					is_string($target) // Must be a string
57
+					&& (bool) preg_match('/^[A-Za-z0-9_.]+$/', $target) // Can only contain letters, numbers, underscore and full stops
58
+					&& 1 === \substr_count($target, '.') // Contains a single full stop ONLY.
59
+				)
60
+			) {
61
+				$target = $this->getTablePrefix() . $target;
62
+			}
63 63
 
64
-            $return[$key] = $value;
65
-        }
64
+			$return[$key] = $value;
65
+		}
66 66
 
67
-        // If we had single value then we should return a single value (end value of the array)
68
-        return true === $single ? end($return) : $return;
69
-    }
67
+		// If we had single value then we should return a single value (end value of the array)
68
+		return true === $single ? end($return) : $return;
69
+	}
70 70
 
71
-    /**
72
-     * Returns the table prefix if defined in connection
73
-     *
74
-     * @return string|null
75
-     */
76
-    protected function getTablePrefix(): ?string
77
-    {
78
-        $adapterConfig = $this->getConnection()->getAdapterConfig();
79
-        return isset($adapterConfig[Connection::PREFIX])
80
-            ? $adapterConfig[Connection::PREFIX]
81
-            : null;
82
-    }
71
+	/**
72
+	 * Returns the table prefix if defined in connection
73
+	 *
74
+	 * @return string|null
75
+	 */
76
+	protected function getTablePrefix(): ?string
77
+	{
78
+		$adapterConfig = $this->getConnection()->getAdapterConfig();
79
+		return isset($adapterConfig[Connection::PREFIX])
80
+			? $adapterConfig[Connection::PREFIX]
81
+			: null;
82
+	}
83 83
 
84
-    abstract public function getConnection(): Connection;
84
+	abstract public function getConnection(): Connection;
85 85
 }
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -27,7 +27,7 @@  discard block
 block discarded – undo
27 27
 
28 28
         // If supplied value is not an array then make it one
29 29
         $single = false;
30
-        if (!is_array($values)) {
30
+        if ( ! is_array($values)) {
31 31
             $values = [$values];
32 32
             // We had single value, so should return a single value
33 33
             $single = true;
@@ -45,20 +45,20 @@  discard block
 block discarded – undo
45 45
             // If key is not integer, it is likely a alias mapping,
46 46
             // so we need to change prefix target
47 47
             $target = &$value;
48
-            if (!is_int($key)) {
48
+            if ( ! is_int($key)) {
49 49
                 $target = &$key;
50 50
             }
51 51
 
52 52
             // Do prefix if the target is an expression or function.
53 53
             if (
54
-                !$tableFieldMix
54
+                ! $tableFieldMix
55 55
                 || (
56 56
                     is_string($target) // Must be a string
57 57
                     && (bool) preg_match('/^[A-Za-z0-9_.]+$/', $target) // Can only contain letters, numbers, underscore and full stops
58 58
                     && 1 === \substr_count($target, '.') // Contains a single full stop ONLY.
59 59
                 )
60 60
             ) {
61
-                $target = $this->getTablePrefix() . $target;
61
+                $target = $this->getTablePrefix().$target;
62 62
             }
63 63
 
64 64
             $return[$key] = $value;
Please login to merge, or discard this patch.
src/QueryBuilder/QueryBuilderHandler.php 2 patches
Indentation   +1485 added lines, -1485 removed lines patch added patch discarded remove patch
@@ -24,1489 +24,1489 @@
 block discarded – undo
24 24
 
25 25
 class QueryBuilderHandler implements HasConnection
26 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
1145
-    {
1146
-        try {
1147
-            // Begin the transaction
1148
-            $this->dbInstance->query('START TRANSACTION');
1149
-
1150
-            // Get the Transaction class
1151
-            $transaction = $this->container->build(Transaction::class, [$this->connection]);
1152
-
1153
-            $this->handleTransactionCall($callback, $transaction);
1154
-
1155
-            // If no errors have been thrown or the transaction wasn't completed within
1156
-            $this->dbInstance->query('COMMIT');
1157
-
1158
-            return $this;
1159
-        } catch (TransactionHaltException $e) {
1160
-            // Commit or rollback behavior has been handled in the closure, so exit
1161
-            return $this;
1162
-        } catch (\Exception $e) {
1163
-            // something happened, rollback changes
1164
-            $this->dbInstance->query('ROLLBACK');
1165
-
1166
-            return $this;
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()
1380
-    {
1381
-        return $this->connection;
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
1435
-    {
1436
-        return $this->connection->getEventHandler()->getEvent($event, $table);
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')
1464
-    {
1465
-        if (':any' != $table) {
1466
-            $table = $this->addTablePrefix($table, false);
1467
-        }
1468
-
1469
-        $this->connection->getEventHandler()->removeEvent($event, $table);
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()
1489
-    {
1490
-        return $this->statements;
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
1509
-    {
1510
-        return new JsonQueryBuilder($this->getConnection(), $this->getFetchMode(), $this->hydratorConstructorArgs);
1511
-    }
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
1145
+	{
1146
+		try {
1147
+			// Begin the transaction
1148
+			$this->dbInstance->query('START TRANSACTION');
1149
+
1150
+			// Get the Transaction class
1151
+			$transaction = $this->container->build(Transaction::class, [$this->connection]);
1152
+
1153
+			$this->handleTransactionCall($callback, $transaction);
1154
+
1155
+			// If no errors have been thrown or the transaction wasn't completed within
1156
+			$this->dbInstance->query('COMMIT');
1157
+
1158
+			return $this;
1159
+		} catch (TransactionHaltException $e) {
1160
+			// Commit or rollback behavior has been handled in the closure, so exit
1161
+			return $this;
1162
+		} catch (\Exception $e) {
1163
+			// something happened, rollback changes
1164
+			$this->dbInstance->query('ROLLBACK');
1165
+
1166
+			return $this;
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()
1380
+	{
1381
+		return $this->connection;
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
1435
+	{
1436
+		return $this->connection->getEventHandler()->getEvent($event, $table);
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')
1464
+	{
1465
+		if (':any' != $table) {
1466
+			$table = $this->addTablePrefix($table, false);
1467
+		}
1468
+
1469
+		$this->connection->getEventHandler()->removeEvent($event, $table);
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()
1489
+	{
1490
+		return $this->statements;
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
1509
+	{
1510
+		return new JsonQueryBuilder($this->getConnection(), $this->getFetchMode(), $this->hydratorConstructorArgs);
1511
+	}
1512 1512
 }
Please login to merge, or discard this patch.
Spacing   +24 added lines, -24 removed lines patch added patch discarded remove patch
@@ -232,7 +232,7 @@  discard block
 block discarded – undo
232 232
         $start        = microtime(true);
233 233
         $sqlStatement = empty($bindings) ? $sql : $this->interpolateQuery($sql, $bindings);
234 234
 
235
-        if (!is_string($sqlStatement)) {
235
+        if ( ! is_string($sqlStatement)) {
236 236
             throw new Exception('Could not interpolate query', 1);
237 237
         }
238 238
 
@@ -249,7 +249,7 @@  discard block
 block discarded – undo
249 249
     public function get()
250 250
     {
251 251
         $eventResult = $this->fireEvents('before-select');
252
-        if (!is_null($eventResult)) {
252
+        if ( ! is_null($eventResult)) {
253 253
             return $eventResult;
254 254
         }
255 255
         $executionTime = 0;
@@ -274,7 +274,7 @@  discard block
 block discarded – undo
274 274
         $this->sqlStatement = null;
275 275
 
276 276
         // Ensure we have an array of results.
277
-        if (!is_array($result) && null !== $result) {
277
+        if ( ! is_array($result) && null !== $result) {
278 278
             $result = [$result];
279 279
         }
280 280
 
@@ -307,7 +307,7 @@  discard block
 block discarded – undo
307 307
      */
308 308
     protected function useHydrator(): bool
309 309
     {
310
-        return !in_array($this->getFetchMode(), [\ARRAY_A, \ARRAY_N, \OBJECT, \OBJECT_K]);
310
+        return ! in_array($this->getFetchMode(), [\ARRAY_A, \ARRAY_N, \OBJECT, \OBJECT_K]);
311 311
     }
312 312
 
313 313
     /**
@@ -397,7 +397,7 @@  discard block
 block discarded – undo
397 397
             ->select([$this->raw(sprintf('%s(%s) AS field', strtoupper($type), $field))])
398 398
             ->first();
399 399
 
400
-        return true === isset($count->field) ? (float)$count->field : 0;
400
+        return true === isset($count->field) ? (float) $count->field : 0;
401 401
     }
402 402
 
403 403
     /**
@@ -413,7 +413,7 @@  discard block
 block discarded – undo
413 413
      */
414 414
     public function count(string $field = '*'): int
415 415
     {
416
-        return (int)$this->aggregate('count', $field);
416
+        return (int) $this->aggregate('count', $field);
417 417
     }
418 418
 
419 419
     /**
@@ -491,8 +491,8 @@  discard block
 block discarded – undo
491 491
     public function getQuery(string $type = 'select', $dataToBePassed = [])
492 492
     {
493 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);
494
+        if ( ! in_array(strtolower($type), $allowedTypes)) {
495
+            throw new Exception($type.' is not a known type.', 2);
496 496
         }
497 497
 
498 498
         $queryArr = $this->adapterInstance->$type($this->statements, $dataToBePassed);
@@ -511,9 +511,9 @@  discard block
 block discarded – undo
511 511
      */
512 512
     public function subQuery(QueryBuilderHandler $queryBuilder, ?string $alias = null)
513 513
     {
514
-        $sql = '(' . $queryBuilder->getQuery()->getRawSql() . ')';
514
+        $sql = '('.$queryBuilder->getQuery()->getRawSql().')';
515 515
         if (is_string($alias) && 0 !== mb_strlen($alias)) {
516
-            $sql = $sql . ' as ' . $alias;
516
+            $sql = $sql.' as '.$alias;
517 517
         }
518 518
 
519 519
         return $queryBuilder->raw($sql);
@@ -530,12 +530,12 @@  discard block
 block discarded – undo
530 530
     private function doInsert(array $data, string $type)
531 531
     {
532 532
         $eventResult = $this->fireEvents('before-insert');
533
-        if (!is_null($eventResult)) {
533
+        if ( ! is_null($eventResult)) {
534 534
             return $eventResult;
535 535
         }
536 536
 
537 537
         // If first value is not an array () not a batch insert)
538
-        if (!is_array(current($data))) {
538
+        if ( ! is_array(current($data))) {
539 539
             $queryObject = $this->getQuery($type, $data);
540 540
 
541 541
             list($preparedQuery, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings());
@@ -605,7 +605,7 @@  discard block
 block discarded – undo
605 605
     public function update($data)
606 606
     {
607 607
         $eventResult = $this->fireEvents('before-update');
608
-        if (!is_null($eventResult)) {
608
+        if ( ! is_null($eventResult)) {
609 609
             return $eventResult;
610 610
         }
611 611
         $queryObject                         = $this->getQuery('update', $data);
@@ -651,7 +651,7 @@  discard block
 block discarded – undo
651 651
     public function delete(): int
652 652
     {
653 653
         $eventResult = $this->fireEvents('before-delete');
654
-        if (!is_null($eventResult)) {
654
+        if ( ! is_null($eventResult)) {
655 655
             return $eventResult;
656 656
         }
657 657
 
@@ -673,7 +673,7 @@  discard block
 block discarded – undo
673 673
      */
674 674
     public function table(...$tables)
675 675
     {
676
-        $instance =  $this->constructCurrentBuilderClass($this->connection);
676
+        $instance = $this->constructCurrentBuilderClass($this->connection);
677 677
         $this->setFetchMode($this->getFetchMode(), $this->hydratorConstructorArgs);
678 678
         $tables = $this->addTablePrefix($tables, false);
679 679
         $instance->addStatement('tables', $tables);
@@ -701,7 +701,7 @@  discard block
 block discarded – undo
701 701
      */
702 702
     public function select($fields): self
703 703
     {
704
-        if (!is_array($fields)) {
704
+        if ( ! is_array($fields)) {
705 705
             $fields = func_get_args();
706 706
         }
707 707
 
@@ -762,7 +762,7 @@  discard block
 block discarded – undo
762 762
      */
763 763
     public function orderBy($fields, string $defaultDirection = 'ASC'): self
764 764
     {
765
-        if (!is_array($fields)) {
765
+        if ( ! is_array($fields)) {
766 766
             $fields = [$fields];
767 767
         }
768 768
 
@@ -778,7 +778,7 @@  discard block
 block discarded – undo
778 778
                 $field = $this->jsonHandler->extractAndUnquoteFromJsonSelector($field);
779 779
             }
780 780
 
781
-            if (!$field instanceof Raw) {
781
+            if ( ! $field instanceof Raw) {
782 782
                 $field = $this->addTablePrefix($field);
783 783
             }
784 784
             $this->statements['orderBys'][] = compact('field', 'type');
@@ -1130,7 +1130,7 @@  discard block
 block discarded – undo
1130 1130
             throw new Exception('Key used for whereNull condition must be a string or raw exrpession.', 1);
1131 1131
         }
1132 1132
 
1133
-        return $this->{$operator . 'Where'}($this->raw("{$key} IS{$prefix} NULL"));
1133
+        return $this->{$operator.'Where'}($this->raw("{$key} IS{$prefix} NULL"));
1134 1134
     }
1135 1135
 
1136 1136
 
@@ -1227,8 +1227,8 @@  discard block
 block discarded – undo
1227 1227
             $value = $this->jsonHandler->extractAndUnquoteFromJsonSelector($value);
1228 1228
         }
1229 1229
 
1230
-        if (!$key instanceof Closure) {
1231
-            $key = function ($joinBuilder) use ($key, $operator, $value) {
1230
+        if ( ! $key instanceof Closure) {
1231
+            $key = function($joinBuilder) use ($key, $operator, $value) {
1232 1232
                 $joinBuilder->on($key, $operator, $value);
1233 1233
             };
1234 1234
         }
@@ -1323,7 +1323,7 @@  discard block
 block discarded – undo
1323 1323
      */
1324 1324
     public function joinUsing(string $table, string $key, string $type = 'INNER'): self
1325 1325
     {
1326
-        if (!array_key_exists('tables', $this->statements) || count($this->statements['tables']) !== 1) {
1326
+        if ( ! array_key_exists('tables', $this->statements) || count($this->statements['tables']) !== 1) {
1327 1327
             throw new Exception("JoinUsing can only be used with a single table set as the base of the query", 1);
1328 1328
         }
1329 1329
         $baseTable = end($this->statements['tables']);
@@ -1414,11 +1414,11 @@  discard block
 block discarded – undo
1414 1414
      */
1415 1415
     protected function addStatement($key, $value)
1416 1416
     {
1417
-        if (!is_array($value)) {
1417
+        if ( ! is_array($value)) {
1418 1418
             $value = [$value];
1419 1419
         }
1420 1420
 
1421
-        if (!array_key_exists($key, $this->statements)) {
1421
+        if ( ! array_key_exists($key, $this->statements)) {
1422 1422
             $this->statements[$key] = $value;
1423 1423
         } else {
1424 1424
             $this->statements[$key] = array_merge($this->statements[$key], $value);
Please login to merge, or discard this patch.
src/QueryBuilder/JsonQueryBuilder.php 1 patch
Indentation   +444 added lines, -444 removed lines patch added patch discarded remove patch
@@ -4,448 +4,448 @@
 block discarded – undo
4 4
 
5 5
 class JsonQueryBuilder extends QueryBuilderHandler
6 6
 {
7
-    /**
8
-    * @param string|Raw $key The database column which holds the JSON value
9
-    * @param string|Raw|string[] $jsonKey The json key/index to search
10
-    * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
11
-    * @param mixed|null $value
12
-    * @return static
13
-    */
14
-    public function whereJson($key, $jsonKey, $operator = null, $value = null): self
15
-    {
16
-        // If two params are given then assume operator is =
17
-        if (3 === func_num_args()) {
18
-            $value    = $operator;
19
-            $operator = '=';
20
-        }
21
-
22
-        return $this->whereJsonHandler($key, $jsonKey, $operator, $value, 'AND');
23
-    }
24
-
25
-    /**
26
-     * @param string|Raw $key The database column which holds the JSON value
27
-     * @param string|Raw|string[] $jsonKey The json key/index to search
28
-     * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
29
-     * @param mixed|null $value
30
-     * @return static
31
-     */
32
-    public function whereNotJson($key, $jsonKey, $operator = null, $value = null): self
33
-    {
34
-        // If two params are given then assume operator is =
35
-        if (3 === func_num_args()) {
36
-            $value    = $operator;
37
-            $operator = '=';
38
-        }
39
-
40
-        return $this->whereJsonHandler($key, $jsonKey, $operator, $value, 'AND NOT');
41
-    }
42
-
43
-    /**
44
-    * @param string|Raw $key The database column which holds the JSON value
45
-    * @param string|Raw|string[] $jsonKey The json key/index to search
46
-    * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
47
-    * @param mixed|null $value
48
-    * @return static
49
-    */
50
-    public function orWhereJson($key, $jsonKey, $operator = null, $value = null): self
51
-    {
52
-        // If two params are given then assume operator is =
53
-        if (3 === func_num_args()) {
54
-            $value    = $operator;
55
-            $operator = '=';
56
-        }
57
-
58
-        return $this->whereJsonHandler($key, $jsonKey, $operator, $value, 'OR');
59
-    }
60
-
61
-    /**
62
-    * @param string|Raw $key The database column which holds the JSON value
63
-    * @param string|Raw|string[] $jsonKey The json key/index to search
64
-    * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
65
-    * @param mixed|null $value
66
-    * @return static
67
-    */
68
-    public function orWhereNotJson($key, $jsonKey, $operator = null, $value = null): self
69
-    {
70
-        // If two params are given then assume operator is =
71
-        if (3 === func_num_args()) {
72
-            $value    = $operator;
73
-            $operator = '=';
74
-        }
75
-
76
-        return $this->whereJsonHandler($key, $jsonKey, $operator, $value, 'OR NOT');
77
-    }
78
-
79
-    /**
80
-    * @param string|Raw $key The database column which holds the JSON value
81
-    * @param string|Raw|string[] $jsonKey The json key/index to search
82
-    * @param mixed[] $values
83
-    * @return static
84
-    */
85
-    public function whereInJson($key, $jsonKey, $values): self
86
-    {
87
-        return $this->whereJsonHandler($key, $jsonKey, 'IN', $values, 'AND');
88
-    }
89
-
90
-    /**
91
-    * @param string|Raw $key The database column which holds the JSON value
92
-    * @param string|Raw|string[] $jsonKey The json key/index to search
93
-    * @param mixed[] $values
94
-    * @return static
95
-    */
96
-    public function whereNotInJson($key, $jsonKey, $values): self
97
-    {
98
-        return $this->whereJsonHandler($key, $jsonKey, 'NOT IN', $values, 'AND');
99
-    }
100
-
101
-    /**
102
-    * @param string|Raw $key The database column which holds the JSON value
103
-    * @param string|Raw|string[] $jsonKey The json key/index to search
104
-    * @param mixed[] $values
105
-    * @return static
106
-    */
107
-    public function orWhereInJson($key, $jsonKey, $values): self
108
-    {
109
-        return $this->whereJsonHandler($key, $jsonKey, 'IN', $values, 'OR');
110
-    }
111
-
112
-    /**
113
-    * @param string|Raw $key The database column which holds the JSON value
114
-    * @param string|Raw|string[] $jsonKey The json key/index to search
115
-    * @param mixed[] $values
116
-    * @return static
117
-    */
118
-    public function orWhereNotInJson($key, $jsonKey, $values): self
119
-    {
120
-        return $this->whereJsonHandler($key, $jsonKey, 'NOT IN', $values, 'OR');
121
-    }
122
-
123
-    /**
124
-     * @param string|Raw $key
125
-    * @param string|Raw|string[] $jsonKey The json key/index to search
126
-     * @param mixed $valueFrom
127
-     * @param mixed $valueTo
128
-     *
129
-     * @return static
130
-     */
131
-    public function whereBetweenJson($key, $jsonKey, $valueFrom, $valueTo): self
132
-    {
133
-        return $this->whereJsonHandler($key, $jsonKey, 'BETWEEN', [$valueFrom, $valueTo], 'AND');
134
-    }
135
-
136
-    /**
137
-     * @param string|Raw $key
138
-    * @param string|Raw|string[] $jsonKey The json key/index to search
139
-     * @param mixed $valueFrom
140
-     * @param mixed $valueTo
141
-     *
142
-     * @return static
143
-     */
144
-    public function orWhereBetweenJson($key, $jsonKey, $valueFrom, $valueTo): self
145
-    {
146
-        return $this->whereJsonHandler($key, $jsonKey, 'BETWEEN', [$valueFrom, $valueTo], 'OR');
147
-    }
148
-
149
-    /**
150
-    * @param string|Raw $key The database column which holds the JSON value
151
-    * @param string|Raw|string[] $jsonKey The json key/index to search
152
-    * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
153
-    * @param mixed|null $value
154
-    * @return static
155
-    */
156
-    public function whereDayJson($key, $jsonKey, $operator = null, $value = null): self
157
-    {
158
-        // If two params are given then assume operator is =
159
-        if (3 === func_num_args()) {
160
-            $value    = $operator;
161
-            $operator = '=';
162
-        }
163
-        return $this->whereFunctionCallJsonHandler($key, $jsonKey, 'DAY', $operator, $value);
164
-    }
165
-
166
-    /**
167
-    * @param string|Raw $key The database column which holds the JSON value
168
-    * @param string|Raw|string[] $jsonKey The json key/index to search
169
-    * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
170
-    * @param mixed|null $value
171
-    * @return static
172
-    */
173
-    public function whereMonthJson($key, $jsonKey, $operator = null, $value = null): self
174
-    {
175
-        // If two params are given then assume operator is =
176
-        if (3 === func_num_args()) {
177
-            $value    = $operator;
178
-            $operator = '=';
179
-        }
180
-        return $this->whereFunctionCallJsonHandler($key, $jsonKey, 'MONTH', $operator, $value);
181
-    }
182
-
183
-    /**
184
-    * @param string|Raw $key The database column which holds the JSON value
185
-    * @param string|Raw|string[] $jsonKey The json key/index to search
186
-    * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
187
-    * @param mixed|null $value
188
-    * @return static
189
-    */
190
-    public function whereYearJson($key, $jsonKey, $operator = null, $value = null): self
191
-    {
192
-        // If two params are given then assume operator is =
193
-        if (3 === func_num_args()) {
194
-            $value    = $operator;
195
-            $operator = '=';
196
-        }
197
-        return $this->whereFunctionCallJsonHandler($key, $jsonKey, 'YEAR', $operator, $value);
198
-    }
199
-
200
-    /**
201
-    * @param string|Raw $key The database column which holds the JSON value
202
-    * @param string|Raw|string[] $jsonKey The json key/index to search
203
-    * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
204
-    * @param mixed|null $value
205
-    * @return static
206
-    */
207
-    public function whereDateJson($key, $jsonKey, $operator = null, $value = null): self
208
-    {
209
-        // If two params are given then assume operator is =
210
-        if (3 === func_num_args()) {
211
-            $value    = $operator;
212
-            $operator = '=';
213
-        }
214
-        return $this->whereFunctionCallJsonHandler($key, $jsonKey, 'DATE', $operator, $value);
215
-    }
216
-
217
-    /**
218
-     * Maps a function call for a JSON where condition
219
-     *
220
-     * @param string|Raw $key
221
-     * @param string|Raw|string[] $jsonKey
222
-     * @param string $function
223
-     * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
224
-     * @param mixed|null $value
225
-     * @return static
226
-     */
227
-    protected function whereFunctionCallJsonHandler($key, $jsonKey, $function, $operator, $value): self
228
-    {
229
-        // Handle potential raw values.
230
-        if ($key instanceof Raw) {
231
-            $key = $this->adapterInstance->parseRaw($key);
232
-        }
233
-        if ($jsonKey instanceof Raw) {
234
-            $jsonKey = $this->adapterInstance->parseRaw($jsonKey);
235
-        }
236
-
237
-        return $this->whereFunctionCallHandler(
238
-            $this->jsonHandler->jsonExpressionFactory()->extractAndUnquote($key, $jsonKey),
239
-            $function,
240
-            $operator,
241
-            $value
242
-        );
243
-    }
244
-
245
-    /**
246
-    * @param string|Raw $key The database column which holds the JSON value
247
-    * @param string|Raw|string[] $jsonKey The json key/index to search
248
-    * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
249
-    * @param mixed|null $value
250
-    * @param string $joiner
251
-    * @return static
252
-    */
253
-    protected function whereJsonHandler($key, $jsonKey, $operator = null, $value = null, string $joiner = 'AND'): self
254
-    {
255
-        // Handle potential raw values.
256
-        if ($key instanceof Raw) {
257
-            $key = $this->adapterInstance->parseRaw($key);
258
-        }
259
-        if ($jsonKey instanceof Raw) {
260
-            $jsonKey = $this->adapterInstance->parseRaw($jsonKey);
261
-        }
262
-
263
-        return $this->whereHandler(
264
-            $this->jsonHandler->jsonExpressionFactory()->extractAndUnquote($key, $jsonKey),
265
-            $operator,
266
-            $value,
267
-            $joiner
268
-        );
269
-    }
270
-
271
-    /**
272
-     * @param string|Raw $table
273
-     * @param string|Raw $remoteColumn
274
-     * @param string|Raw|string[]|null $remoteJsonKeys The json key/index to search
275
-     * @param string $operator
276
-     * @param string|Raw $localColumn
277
-     * @param string|Raw|string[]|null $localJsonKeys
278
-     * @param string $type
279
-     *
280
-     * @return static
281
-     */
282
-    public function joinJson(
283
-        $table,
284
-        $remoteColumn,
285
-        $remoteJsonKeys,
286
-        string $operator,
287
-        $localColumn,
288
-        $localJsonKeys,
289
-        $type = 'inner'
290
-    ): self {
291
-        // Convert key if json
292
-        if (null !== $localJsonKeys) {
293
-            $localColumn = $this->jsonHandler->jsonExpressionFactory()->extractAndUnquote($localColumn, $localJsonKeys);
294
-        }
295
-
296
-        // Convert key if json
297
-        if (null !== $remoteJsonKeys) {
298
-            $remoteColumn = $this->jsonHandler->jsonExpressionFactory()->extractAndUnquote($remoteColumn, $remoteJsonKeys);
299
-        }
300
-
301
-        return $this->join($table, $remoteColumn, $operator, $localColumn, $type);
302
-    }
303
-
304
-    /**
305
-     * @param string|Raw $table
306
-     * @param string|Raw $remoteColumn
307
-     * @param string|Raw|string[]|null $remoteJsonKeys The json key/index to search
308
-     * @param string $operator
309
-     * @param string|Raw $localColumn
310
-     * @param string|Raw|string[]|null $localJsonKeys
311
-     *
312
-     * @return static
313
-     */
314
-    public function leftJoinJson(
315
-        $table,
316
-        $remoteColumn,
317
-        $remoteJsonKeys,
318
-        string $operator,
319
-        $localColumn,
320
-        $localJsonKeys
321
-    ): self {
322
-        return $this->joinJson(
323
-            $table,
324
-            $remoteColumn,
325
-            $remoteJsonKeys,
326
-            $operator,
327
-            $localColumn,
328
-            $localJsonKeys,
329
-            'left'
330
-        );
331
-    }
332
-
333
-    /**
334
-     * @param string|Raw $table
335
-     * @param string|Raw $remoteColumn
336
-     * @param string|Raw|string[]|null $remoteJsonKeys The json key/index to search
337
-     * @param string $operator
338
-     * @param string|Raw $localColumn
339
-     * @param string|Raw|string[]|null $localJsonKeys
340
-     *
341
-     * @return static
342
-     */
343
-    public function rightJoinJson(
344
-        $table,
345
-        $remoteColumn,
346
-        $remoteJsonKeys,
347
-        string $operator,
348
-        $localColumn,
349
-        $localJsonKeys
350
-    ): self {
351
-        return $this->joinJson(
352
-            $table,
353
-            $remoteColumn,
354
-            $remoteJsonKeys,
355
-            $operator,
356
-            $localColumn,
357
-            $localJsonKeys,
358
-            'right'
359
-        );
360
-    }
361
-
362
-    /**
363
-     * @param string|Raw $table
364
-     * @param string|Raw $remoteColumn
365
-     * @param string|Raw|string[]|null $remoteJsonKeys The json key/index to search
366
-     * @param string $operator
367
-     * @param string|Raw $localColumn
368
-     * @param string|Raw|string[]|null $localJsonKeys
369
-     *
370
-     * @return static
371
-     */
372
-    public function outerJoinJson(
373
-        $table,
374
-        $remoteColumn,
375
-        $remoteJsonKeys,
376
-        string $operator,
377
-        $localColumn,
378
-        $localJsonKeys
379
-    ): self {
380
-        return $this->joinJson(
381
-            $table,
382
-            $remoteColumn,
383
-            $remoteJsonKeys,
384
-            $operator,
385
-            $localColumn,
386
-            $localJsonKeys,
387
-            'outer'
388
-        );
389
-    }
390
-
391
-    /**
392
-     * @param string|Raw $table
393
-     * @param string|Raw $remoteColumn
394
-     * @param string|Raw|string[]|null $remoteJsonKeys The json key/index to search
395
-     * @param string $operator
396
-     * @param string|Raw $localColumn
397
-     * @param string|Raw|string[]|null $localJsonKeys
398
-     *
399
-     * @return static
400
-     */
401
-    public function crossJoinJson(
402
-        $table,
403
-        $remoteColumn,
404
-        $remoteJsonKeys,
405
-        string $operator,
406
-        $localColumn,
407
-        $localJsonKeys
408
-    ): self {
409
-        return $this->joinJson(
410
-            $table,
411
-            $remoteColumn,
412
-            $remoteJsonKeys,
413
-            $operator,
414
-            $localColumn,
415
-            $localJsonKeys,
416
-            'cross'
417
-        );
418
-    }
419
-
420
-
421
-
422
-    // JSON
423
-
424
-    /**
425
-     * @param string|Raw $key The database column which holds the JSON value
426
-     * @param string|Raw|string[] $jsonKey The json key/index to search
427
-     * @param string|null $alias The alias used to define the value in results, if not defined will use json_{$jsonKey}
428
-     * @return static
429
-     */
430
-    public function selectJson($key, $jsonKey, ?string $alias = null): self
431
-    {
432
-        // Handle potential raw values.
433
-        if ($key instanceof Raw) {
434
-            $key = $this->adapterInstance->parseRaw($key);
435
-        }
436
-        if ($jsonKey instanceof Raw) {
437
-            $jsonKey = $this->adapterInstance->parseRaw($jsonKey);
438
-        }
439
-
440
-        // If deeply nested jsonKey.
441
-        if (is_array($jsonKey)) {
442
-            $jsonKey = \implode('.', $jsonKey);
443
-        }
444
-
445
-        // Add any possible prefixes to the key
446
-        $key = $this->addTablePrefix($key, true);
447
-
448
-        $alias = null === $alias ? "json_{$jsonKey}" : $alias;
449
-        return  $this->select(new Raw("JSON_UNQUOTE(JSON_EXTRACT({$key}, \"$.{$jsonKey}\")) as {$alias}"));
450
-    }
7
+	/**
8
+	 * @param string|Raw $key The database column which holds the JSON value
9
+	 * @param string|Raw|string[] $jsonKey The json key/index to search
10
+	 * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
11
+	 * @param mixed|null $value
12
+	 * @return static
13
+	 */
14
+	public function whereJson($key, $jsonKey, $operator = null, $value = null): self
15
+	{
16
+		// If two params are given then assume operator is =
17
+		if (3 === func_num_args()) {
18
+			$value    = $operator;
19
+			$operator = '=';
20
+		}
21
+
22
+		return $this->whereJsonHandler($key, $jsonKey, $operator, $value, 'AND');
23
+	}
24
+
25
+	/**
26
+	 * @param string|Raw $key The database column which holds the JSON value
27
+	 * @param string|Raw|string[] $jsonKey The json key/index to search
28
+	 * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
29
+	 * @param mixed|null $value
30
+	 * @return static
31
+	 */
32
+	public function whereNotJson($key, $jsonKey, $operator = null, $value = null): self
33
+	{
34
+		// If two params are given then assume operator is =
35
+		if (3 === func_num_args()) {
36
+			$value    = $operator;
37
+			$operator = '=';
38
+		}
39
+
40
+		return $this->whereJsonHandler($key, $jsonKey, $operator, $value, 'AND NOT');
41
+	}
42
+
43
+	/**
44
+	 * @param string|Raw $key The database column which holds the JSON value
45
+	 * @param string|Raw|string[] $jsonKey The json key/index to search
46
+	 * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
47
+	 * @param mixed|null $value
48
+	 * @return static
49
+	 */
50
+	public function orWhereJson($key, $jsonKey, $operator = null, $value = null): self
51
+	{
52
+		// If two params are given then assume operator is =
53
+		if (3 === func_num_args()) {
54
+			$value    = $operator;
55
+			$operator = '=';
56
+		}
57
+
58
+		return $this->whereJsonHandler($key, $jsonKey, $operator, $value, 'OR');
59
+	}
60
+
61
+	/**
62
+	 * @param string|Raw $key The database column which holds the JSON value
63
+	 * @param string|Raw|string[] $jsonKey The json key/index to search
64
+	 * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
65
+	 * @param mixed|null $value
66
+	 * @return static
67
+	 */
68
+	public function orWhereNotJson($key, $jsonKey, $operator = null, $value = null): self
69
+	{
70
+		// If two params are given then assume operator is =
71
+		if (3 === func_num_args()) {
72
+			$value    = $operator;
73
+			$operator = '=';
74
+		}
75
+
76
+		return $this->whereJsonHandler($key, $jsonKey, $operator, $value, 'OR NOT');
77
+	}
78
+
79
+	/**
80
+	 * @param string|Raw $key The database column which holds the JSON value
81
+	 * @param string|Raw|string[] $jsonKey The json key/index to search
82
+	 * @param mixed[] $values
83
+	 * @return static
84
+	 */
85
+	public function whereInJson($key, $jsonKey, $values): self
86
+	{
87
+		return $this->whereJsonHandler($key, $jsonKey, 'IN', $values, 'AND');
88
+	}
89
+
90
+	/**
91
+	 * @param string|Raw $key The database column which holds the JSON value
92
+	 * @param string|Raw|string[] $jsonKey The json key/index to search
93
+	 * @param mixed[] $values
94
+	 * @return static
95
+	 */
96
+	public function whereNotInJson($key, $jsonKey, $values): self
97
+	{
98
+		return $this->whereJsonHandler($key, $jsonKey, 'NOT IN', $values, 'AND');
99
+	}
100
+
101
+	/**
102
+	 * @param string|Raw $key The database column which holds the JSON value
103
+	 * @param string|Raw|string[] $jsonKey The json key/index to search
104
+	 * @param mixed[] $values
105
+	 * @return static
106
+	 */
107
+	public function orWhereInJson($key, $jsonKey, $values): self
108
+	{
109
+		return $this->whereJsonHandler($key, $jsonKey, 'IN', $values, 'OR');
110
+	}
111
+
112
+	/**
113
+	 * @param string|Raw $key The database column which holds the JSON value
114
+	 * @param string|Raw|string[] $jsonKey The json key/index to search
115
+	 * @param mixed[] $values
116
+	 * @return static
117
+	 */
118
+	public function orWhereNotInJson($key, $jsonKey, $values): self
119
+	{
120
+		return $this->whereJsonHandler($key, $jsonKey, 'NOT IN', $values, 'OR');
121
+	}
122
+
123
+	/**
124
+	 * @param string|Raw $key
125
+	 * @param string|Raw|string[] $jsonKey The json key/index to search
126
+	 * @param mixed $valueFrom
127
+	 * @param mixed $valueTo
128
+	 *
129
+	 * @return static
130
+	 */
131
+	public function whereBetweenJson($key, $jsonKey, $valueFrom, $valueTo): self
132
+	{
133
+		return $this->whereJsonHandler($key, $jsonKey, 'BETWEEN', [$valueFrom, $valueTo], 'AND');
134
+	}
135
+
136
+	/**
137
+	 * @param string|Raw $key
138
+	 * @param string|Raw|string[] $jsonKey The json key/index to search
139
+	 * @param mixed $valueFrom
140
+	 * @param mixed $valueTo
141
+	 *
142
+	 * @return static
143
+	 */
144
+	public function orWhereBetweenJson($key, $jsonKey, $valueFrom, $valueTo): self
145
+	{
146
+		return $this->whereJsonHandler($key, $jsonKey, 'BETWEEN', [$valueFrom, $valueTo], 'OR');
147
+	}
148
+
149
+	/**
150
+	 * @param string|Raw $key The database column which holds the JSON value
151
+	 * @param string|Raw|string[] $jsonKey The json key/index to search
152
+	 * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
153
+	 * @param mixed|null $value
154
+	 * @return static
155
+	 */
156
+	public function whereDayJson($key, $jsonKey, $operator = null, $value = null): self
157
+	{
158
+		// If two params are given then assume operator is =
159
+		if (3 === func_num_args()) {
160
+			$value    = $operator;
161
+			$operator = '=';
162
+		}
163
+		return $this->whereFunctionCallJsonHandler($key, $jsonKey, 'DAY', $operator, $value);
164
+	}
165
+
166
+	/**
167
+	 * @param string|Raw $key The database column which holds the JSON value
168
+	 * @param string|Raw|string[] $jsonKey The json key/index to search
169
+	 * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
170
+	 * @param mixed|null $value
171
+	 * @return static
172
+	 */
173
+	public function whereMonthJson($key, $jsonKey, $operator = null, $value = null): self
174
+	{
175
+		// If two params are given then assume operator is =
176
+		if (3 === func_num_args()) {
177
+			$value    = $operator;
178
+			$operator = '=';
179
+		}
180
+		return $this->whereFunctionCallJsonHandler($key, $jsonKey, 'MONTH', $operator, $value);
181
+	}
182
+
183
+	/**
184
+	 * @param string|Raw $key The database column which holds the JSON value
185
+	 * @param string|Raw|string[] $jsonKey The json key/index to search
186
+	 * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
187
+	 * @param mixed|null $value
188
+	 * @return static
189
+	 */
190
+	public function whereYearJson($key, $jsonKey, $operator = null, $value = null): self
191
+	{
192
+		// If two params are given then assume operator is =
193
+		if (3 === func_num_args()) {
194
+			$value    = $operator;
195
+			$operator = '=';
196
+		}
197
+		return $this->whereFunctionCallJsonHandler($key, $jsonKey, 'YEAR', $operator, $value);
198
+	}
199
+
200
+	/**
201
+	 * @param string|Raw $key The database column which holds the JSON value
202
+	 * @param string|Raw|string[] $jsonKey The json key/index to search
203
+	 * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
204
+	 * @param mixed|null $value
205
+	 * @return static
206
+	 */
207
+	public function whereDateJson($key, $jsonKey, $operator = null, $value = null): self
208
+	{
209
+		// If two params are given then assume operator is =
210
+		if (3 === func_num_args()) {
211
+			$value    = $operator;
212
+			$operator = '=';
213
+		}
214
+		return $this->whereFunctionCallJsonHandler($key, $jsonKey, 'DATE', $operator, $value);
215
+	}
216
+
217
+	/**
218
+	 * Maps a function call for a JSON where condition
219
+	 *
220
+	 * @param string|Raw $key
221
+	 * @param string|Raw|string[] $jsonKey
222
+	 * @param string $function
223
+	 * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
224
+	 * @param mixed|null $value
225
+	 * @return static
226
+	 */
227
+	protected function whereFunctionCallJsonHandler($key, $jsonKey, $function, $operator, $value): self
228
+	{
229
+		// Handle potential raw values.
230
+		if ($key instanceof Raw) {
231
+			$key = $this->adapterInstance->parseRaw($key);
232
+		}
233
+		if ($jsonKey instanceof Raw) {
234
+			$jsonKey = $this->adapterInstance->parseRaw($jsonKey);
235
+		}
236
+
237
+		return $this->whereFunctionCallHandler(
238
+			$this->jsonHandler->jsonExpressionFactory()->extractAndUnquote($key, $jsonKey),
239
+			$function,
240
+			$operator,
241
+			$value
242
+		);
243
+	}
244
+
245
+	/**
246
+	 * @param string|Raw $key The database column which holds the JSON value
247
+	 * @param string|Raw|string[] $jsonKey The json key/index to search
248
+	 * @param string|mixed|null $operator Can be used as value, if 3rd arg not passed
249
+	 * @param mixed|null $value
250
+	 * @param string $joiner
251
+	 * @return static
252
+	 */
253
+	protected function whereJsonHandler($key, $jsonKey, $operator = null, $value = null, string $joiner = 'AND'): self
254
+	{
255
+		// Handle potential raw values.
256
+		if ($key instanceof Raw) {
257
+			$key = $this->adapterInstance->parseRaw($key);
258
+		}
259
+		if ($jsonKey instanceof Raw) {
260
+			$jsonKey = $this->adapterInstance->parseRaw($jsonKey);
261
+		}
262
+
263
+		return $this->whereHandler(
264
+			$this->jsonHandler->jsonExpressionFactory()->extractAndUnquote($key, $jsonKey),
265
+			$operator,
266
+			$value,
267
+			$joiner
268
+		);
269
+	}
270
+
271
+	/**
272
+	 * @param string|Raw $table
273
+	 * @param string|Raw $remoteColumn
274
+	 * @param string|Raw|string[]|null $remoteJsonKeys The json key/index to search
275
+	 * @param string $operator
276
+	 * @param string|Raw $localColumn
277
+	 * @param string|Raw|string[]|null $localJsonKeys
278
+	 * @param string $type
279
+	 *
280
+	 * @return static
281
+	 */
282
+	public function joinJson(
283
+		$table,
284
+		$remoteColumn,
285
+		$remoteJsonKeys,
286
+		string $operator,
287
+		$localColumn,
288
+		$localJsonKeys,
289
+		$type = 'inner'
290
+	): self {
291
+		// Convert key if json
292
+		if (null !== $localJsonKeys) {
293
+			$localColumn = $this->jsonHandler->jsonExpressionFactory()->extractAndUnquote($localColumn, $localJsonKeys);
294
+		}
295
+
296
+		// Convert key if json
297
+		if (null !== $remoteJsonKeys) {
298
+			$remoteColumn = $this->jsonHandler->jsonExpressionFactory()->extractAndUnquote($remoteColumn, $remoteJsonKeys);
299
+		}
300
+
301
+		return $this->join($table, $remoteColumn, $operator, $localColumn, $type);
302
+	}
303
+
304
+	/**
305
+	 * @param string|Raw $table
306
+	 * @param string|Raw $remoteColumn
307
+	 * @param string|Raw|string[]|null $remoteJsonKeys The json key/index to search
308
+	 * @param string $operator
309
+	 * @param string|Raw $localColumn
310
+	 * @param string|Raw|string[]|null $localJsonKeys
311
+	 *
312
+	 * @return static
313
+	 */
314
+	public function leftJoinJson(
315
+		$table,
316
+		$remoteColumn,
317
+		$remoteJsonKeys,
318
+		string $operator,
319
+		$localColumn,
320
+		$localJsonKeys
321
+	): self {
322
+		return $this->joinJson(
323
+			$table,
324
+			$remoteColumn,
325
+			$remoteJsonKeys,
326
+			$operator,
327
+			$localColumn,
328
+			$localJsonKeys,
329
+			'left'
330
+		);
331
+	}
332
+
333
+	/**
334
+	 * @param string|Raw $table
335
+	 * @param string|Raw $remoteColumn
336
+	 * @param string|Raw|string[]|null $remoteJsonKeys The json key/index to search
337
+	 * @param string $operator
338
+	 * @param string|Raw $localColumn
339
+	 * @param string|Raw|string[]|null $localJsonKeys
340
+	 *
341
+	 * @return static
342
+	 */
343
+	public function rightJoinJson(
344
+		$table,
345
+		$remoteColumn,
346
+		$remoteJsonKeys,
347
+		string $operator,
348
+		$localColumn,
349
+		$localJsonKeys
350
+	): self {
351
+		return $this->joinJson(
352
+			$table,
353
+			$remoteColumn,
354
+			$remoteJsonKeys,
355
+			$operator,
356
+			$localColumn,
357
+			$localJsonKeys,
358
+			'right'
359
+		);
360
+	}
361
+
362
+	/**
363
+	 * @param string|Raw $table
364
+	 * @param string|Raw $remoteColumn
365
+	 * @param string|Raw|string[]|null $remoteJsonKeys The json key/index to search
366
+	 * @param string $operator
367
+	 * @param string|Raw $localColumn
368
+	 * @param string|Raw|string[]|null $localJsonKeys
369
+	 *
370
+	 * @return static
371
+	 */
372
+	public function outerJoinJson(
373
+		$table,
374
+		$remoteColumn,
375
+		$remoteJsonKeys,
376
+		string $operator,
377
+		$localColumn,
378
+		$localJsonKeys
379
+	): self {
380
+		return $this->joinJson(
381
+			$table,
382
+			$remoteColumn,
383
+			$remoteJsonKeys,
384
+			$operator,
385
+			$localColumn,
386
+			$localJsonKeys,
387
+			'outer'
388
+		);
389
+	}
390
+
391
+	/**
392
+	 * @param string|Raw $table
393
+	 * @param string|Raw $remoteColumn
394
+	 * @param string|Raw|string[]|null $remoteJsonKeys The json key/index to search
395
+	 * @param string $operator
396
+	 * @param string|Raw $localColumn
397
+	 * @param string|Raw|string[]|null $localJsonKeys
398
+	 *
399
+	 * @return static
400
+	 */
401
+	public function crossJoinJson(
402
+		$table,
403
+		$remoteColumn,
404
+		$remoteJsonKeys,
405
+		string $operator,
406
+		$localColumn,
407
+		$localJsonKeys
408
+	): self {
409
+		return $this->joinJson(
410
+			$table,
411
+			$remoteColumn,
412
+			$remoteJsonKeys,
413
+			$operator,
414
+			$localColumn,
415
+			$localJsonKeys,
416
+			'cross'
417
+		);
418
+	}
419
+
420
+
421
+
422
+	// JSON
423
+
424
+	/**
425
+	 * @param string|Raw $key The database column which holds the JSON value
426
+	 * @param string|Raw|string[] $jsonKey The json key/index to search
427
+	 * @param string|null $alias The alias used to define the value in results, if not defined will use json_{$jsonKey}
428
+	 * @return static
429
+	 */
430
+	public function selectJson($key, $jsonKey, ?string $alias = null): self
431
+	{
432
+		// Handle potential raw values.
433
+		if ($key instanceof Raw) {
434
+			$key = $this->adapterInstance->parseRaw($key);
435
+		}
436
+		if ($jsonKey instanceof Raw) {
437
+			$jsonKey = $this->adapterInstance->parseRaw($jsonKey);
438
+		}
439
+
440
+		// If deeply nested jsonKey.
441
+		if (is_array($jsonKey)) {
442
+			$jsonKey = \implode('.', $jsonKey);
443
+		}
444
+
445
+		// Add any possible prefixes to the key
446
+		$key = $this->addTablePrefix($key, true);
447
+
448
+		$alias = null === $alias ? "json_{$jsonKey}" : $alias;
449
+		return  $this->select(new Raw("JSON_UNQUOTE(JSON_EXTRACT({$key}, \"$.{$jsonKey}\")) as {$alias}"));
450
+	}
451 451
 }
Please login to merge, or discard this patch.
src/JSON/JsonSelectorHandler.php 2 patches
Indentation   +71 added lines, -71 removed lines patch added patch discarded remove patch
@@ -9,86 +9,86 @@
 block discarded – undo
9 9
 
10 10
 class JsonSelectorHandler implements HasConnection
11 11
 {
12
-    use TablePrefixer;
12
+	use TablePrefixer;
13 13
 
14
-    /** @var Connection */
15
-    protected $connection;
14
+	/** @var Connection */
15
+	protected $connection;
16 16
 
17
-    public function __construct(Connection $connection)
18
-    {
19
-        $this->connection = $connection;
20
-    }
17
+	public function __construct(Connection $connection)
18
+	{
19
+		$this->connection = $connection;
20
+	}
21 21
 
22
-    /**
23
-     * Returns the current connection instance.
24
-     *
25
-     * @return connection
26
-     */
27
-    public function getConnection(): Connection
28
-    {
29
-        return $this->connection;
30
-    }
22
+	/**
23
+	 * Returns the current connection instance.
24
+	 *
25
+	 * @return connection
26
+	 */
27
+	public function getConnection(): Connection
28
+	{
29
+		return $this->connection;
30
+	}
31 31
 
32
-    /**
33
-     * Checks if the passed expression is for JSON
34
-     * this->denotes->json
35
-     *
36
-     * @param string $expression
37
-     * @return bool
38
-     */
39
-    public function isJsonSelector($expression): bool
40
-    {
41
-        return is_string($expression)
42
-        && 2 <= count(array_diff(explode('->', $expression), array("")));
43
-    }
32
+	/**
33
+	 * Checks if the passed expression is for JSON
34
+	 * this->denotes->json
35
+	 *
36
+	 * @param string $expression
37
+	 * @return bool
38
+	 */
39
+	public function isJsonSelector($expression): bool
40
+	{
41
+		return is_string($expression)
42
+		&& 2 <= count(array_diff(explode('->', $expression), array("")));
43
+	}
44 44
 
45
-    /**
46
-    * Gets the column name form a potential array
47
-    *
48
-    * @param string $expression
49
-    * @return string
50
-    * @throws Exception If invalid JSON Selector string passed.
51
-    */
52
-    public function getColumn(string $expression): string
53
-    {
54
-        return $this->toJsonSelector($expression)->getColumn();
55
-    }
45
+	/**
46
+	 * Gets the column name form a potential array
47
+	 *
48
+	 * @param string $expression
49
+	 * @return string
50
+	 * @throws Exception If invalid JSON Selector string passed.
51
+	 */
52
+	public function getColumn(string $expression): string
53
+	{
54
+		return $this->toJsonSelector($expression)->getColumn();
55
+	}
56 56
 
57
-    /**
58
-     * Gets all JSON object keys while removing the column name.
59
-     *
60
-     * @param string $expression
61
-     * @return string[]
62
-     * @throws Exception If invalid JSON Selector string passed.
63
-     */
64
-    public function getNodes(string $expression): array
65
-    {
66
-        return $this->toJsonSelector($expression)->getNodes();
67
-    }
57
+	/**
58
+	 * Gets all JSON object keys while removing the column name.
59
+	 *
60
+	 * @param string $expression
61
+	 * @return string[]
62
+	 * @throws Exception If invalid JSON Selector string passed.
63
+	 */
64
+	public function getNodes(string $expression): array
65
+	{
66
+		return $this->toJsonSelector($expression)->getNodes();
67
+	}
68 68
 
69
-    /**
70
-     * Casts a valid JSON selector to a JsonSelector object.
71
-     *
72
-     * @param string $expression
73
-     * @return JsonSelector
74
-     * @throws Exception If invalid JSON Selector string passed.
75
-     */
76
-    public function toJsonSelector(string $expression): JsonSelector
77
-    {
78
-        if (! $this->isJsonSelector($expression)) {
79
-            throw new Exception('JSON expression must contain at least 2 values, the table column and at least 1 node.', 1);
80
-        }
69
+	/**
70
+	 * Casts a valid JSON selector to a JsonSelector object.
71
+	 *
72
+	 * @param string $expression
73
+	 * @return JsonSelector
74
+	 * @throws Exception If invalid JSON Selector string passed.
75
+	 */
76
+	public function toJsonSelector(string $expression): JsonSelector
77
+	{
78
+		if (! $this->isJsonSelector($expression)) {
79
+			throw new Exception('JSON expression must contain at least 2 values, the table column and at least 1 node.', 1);
80
+		}
81 81
 
82
-        /** @var string[] Check done above. */
83
-        $parts = array_diff(explode('->', $expression), array(""));
82
+		/** @var string[] Check done above. */
83
+		$parts = array_diff(explode('->', $expression), array(""));
84 84
 
85
-        $column = array_shift($parts);
86
-        $nodes = $parts;
85
+		$column = array_shift($parts);
86
+		$nodes = $parts;
87 87
 
88
-        if (! is_string($column)) {
89
-            throw new Exception('JSON expression must contain a valid column name', 1);
90
-        }
88
+		if (! is_string($column)) {
89
+			throw new Exception('JSON expression must contain a valid column name', 1);
90
+		}
91 91
 
92
-        return new JsonSelector($column, $nodes);
93
-    }
92
+		return new JsonSelector($column, $nodes);
93
+	}
94 94
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -75,7 +75,7 @@  discard block
 block discarded – undo
75 75
      */
76 76
     public function toJsonSelector(string $expression): JsonSelector
77 77
     {
78
-        if (! $this->isJsonSelector($expression)) {
78
+        if ( ! $this->isJsonSelector($expression)) {
79 79
             throw new Exception('JSON expression must contain at least 2 values, the table column and at least 1 node.', 1);
80 80
         }
81 81
 
@@ -85,7 +85,7 @@  discard block
 block discarded – undo
85 85
         $column = array_shift($parts);
86 86
         $nodes = $parts;
87 87
 
88
-        if (! is_string($column)) {
88
+        if ( ! is_string($column)) {
89 89
             throw new Exception('JSON expression must contain a valid column name', 1);
90 90
         }
91 91
 
Please login to merge, or discard this patch.
src/JSON/JsonSelector.php 1 patch
Indentation   +39 added lines, -39 removed lines patch added patch discarded remove patch
@@ -4,47 +4,47 @@
 block discarded – undo
4 4
 
5 5
 class JsonSelector
6 6
 {
7
-    /**
8
-     * The table column
9
-     *
10
-     * @var string
11
-     */
12
-    protected $column;
7
+	/**
8
+	 * The table column
9
+	 *
10
+	 * @var string
11
+	 */
12
+	protected $column;
13 13
 
14
-    /**
15
-     * JSON Nodes
16
-     *
17
-     * @var string[]
18
-     */
19
-    protected $nodes;
14
+	/**
15
+	 * JSON Nodes
16
+	 *
17
+	 * @var string[]
18
+	 */
19
+	protected $nodes;
20 20
 
21
-    /**
22
-     * @param string $column
23
-     * @param string[] $nodes
24
-     */
25
-    public function __construct(string $column, array $nodes)
26
-    {
27
-        $this->column = $column;
28
-        $this->nodes = $nodes;
29
-    }
21
+	/**
22
+	 * @param string $column
23
+	 * @param string[] $nodes
24
+	 */
25
+	public function __construct(string $column, array $nodes)
26
+	{
27
+		$this->column = $column;
28
+		$this->nodes = $nodes;
29
+	}
30 30
 
31
-    /**
32
-     * Get the table column
33
-     *
34
-     * @return string
35
-     */
36
-    public function getColumn(): string
37
-    {
38
-        return $this->column;
39
-    }
31
+	/**
32
+	 * Get the table column
33
+	 *
34
+	 * @return string
35
+	 */
36
+	public function getColumn(): string
37
+	{
38
+		return $this->column;
39
+	}
40 40
 
41
-    /**
42
-     * Get jSON Nodes
43
-     *
44
-     * @return string[]
45
-     */
46
-    public function getNodes(): array
47
-    {
48
-        return $this->nodes;
49
-    }
41
+	/**
42
+	 * Get jSON Nodes
43
+	 *
44
+	 * @return string[]
45
+	 */
46
+	public function getNodes(): array
47
+	{
48
+		return $this->nodes;
49
+	}
50 50
 }
Please login to merge, or discard this patch.
src/JSON/JsonHandler.php 1 patch
Indentation   +54 added lines, -54 removed lines patch added patch discarded remove patch
@@ -6,65 +6,65 @@
 block discarded – undo
6 6
 
7 7
 class JsonHandler
8 8
 {
9
-    /** @var Connection */
10
-    protected $connection;
9
+	/** @var Connection */
10
+	protected $connection;
11 11
 
12
-    /** @var JsonSelectorHandler */
13
-    protected $jsonSelectorHandler;
12
+	/** @var JsonSelectorHandler */
13
+	protected $jsonSelectorHandler;
14 14
 
15
-    /** @var JsonExpressionFactory */
16
-    protected $jsonExpressionFactory;
15
+	/** @var JsonExpressionFactory */
16
+	protected $jsonExpressionFactory;
17 17
 
18
-    public function __construct(Connection $connection)
19
-    {
20
-        $this->connection = $connection;
21
-        $this->jsonSelectorHandler = new JsonSelectorHandler($connection);
22
-        $this->jsonExpressionFactory = new JsonExpressionFactory($connection);
23
-    }
18
+	public function __construct(Connection $connection)
19
+	{
20
+		$this->connection = $connection;
21
+		$this->jsonSelectorHandler = new JsonSelectorHandler($connection);
22
+		$this->jsonExpressionFactory = new JsonExpressionFactory($connection);
23
+	}
24 24
 
25
-    /**
26
-     * Returns the JSON Selector Handler
27
-     *
28
-     * @return JsonSelectorHandler
29
-     */
30
-    public function jsonSelectorHandler(): JsonSelectorHandler
31
-    {
32
-        return $this->jsonSelectorHandler;
33
-    }
25
+	/**
26
+	 * Returns the JSON Selector Handler
27
+	 *
28
+	 * @return JsonSelectorHandler
29
+	 */
30
+	public function jsonSelectorHandler(): JsonSelectorHandler
31
+	{
32
+		return $this->jsonSelectorHandler;
33
+	}
34 34
 
35
-    /**
36
-     * Returns the JSON Expression library
37
-     *
38
-     * @return JsonExpressionFactory
39
-     */
40
-    public function jsonExpressionFactory(): JsonExpressionFactory
41
-    {
42
-        return $this->jsonExpressionFactory;
43
-    }
35
+	/**
36
+	 * Returns the JSON Expression library
37
+	 *
38
+	 * @return JsonExpressionFactory
39
+	 */
40
+	public function jsonExpressionFactory(): JsonExpressionFactory
41
+	{
42
+		return $this->jsonExpressionFactory;
43
+	}
44 44
 
45
-    /**
46
-     * Parses a JSON selector and returns as an Extract and Unquote expression.
47
-     *
48
-     * @param string $selector
49
-     * @return string
50
-     */
51
-    public function extractAndUnquoteFromJsonSelector(string $selector): string
52
-    {
53
-        $selector = $this->jsonSelectorHandler()->toJsonSelector($selector);
54
-        return $this->jsonExpressionFactory()->extractAndUnquote(
55
-            $selector->getColumn(),
56
-            $selector->getNodes()
57
-        );
58
-    }
45
+	/**
46
+	 * Parses a JSON selector and returns as an Extract and Unquote expression.
47
+	 *
48
+	 * @param string $selector
49
+	 * @return string
50
+	 */
51
+	public function extractAndUnquoteFromJsonSelector(string $selector): string
52
+	{
53
+		$selector = $this->jsonSelectorHandler()->toJsonSelector($selector);
54
+		return $this->jsonExpressionFactory()->extractAndUnquote(
55
+			$selector->getColumn(),
56
+			$selector->getNodes()
57
+		);
58
+	}
59 59
 
60
-    /**
61
-     * Checks if the passed values is a valid JSON Selector
62
-     *
63
-     * @param mixed $expression
64
-     * @return bool
65
-     */
66
-    public function isJsonSelector($expression): bool
67
-    {
68
-        return $this->jsonSelectorHandler()->isJsonSelector($expression);
69
-    }
60
+	/**
61
+	 * Checks if the passed values is a valid JSON Selector
62
+	 *
63
+	 * @param mixed $expression
64
+	 * @return bool
65
+	 */
66
+	public function isJsonSelector($expression): bool
67
+	{
68
+		return $this->jsonSelectorHandler()->isJsonSelector($expression);
69
+	}
70 70
 }
Please login to merge, or discard this patch.
src/JSON/JsonExpressionFactory.php 1 patch
Indentation   +45 added lines, -45 removed lines patch added patch discarded remove patch
@@ -8,57 +8,57 @@
 block discarded – undo
8 8
 
9 9
 class JsonExpressionFactory
10 10
 {
11
-    use TablePrefixer;
11
+	use TablePrefixer;
12 12
 
13
-    /** @var Connection */
14
-    protected $connection;
13
+	/** @var Connection */
14
+	protected $connection;
15 15
 
16
-    public function __construct(Connection $connection)
17
-    {
18
-        $this->connection = $connection;
19
-    }
16
+	public function __construct(Connection $connection)
17
+	{
18
+		$this->connection = $connection;
19
+	}
20 20
 
21
-    /**
22
-     * Returns the current connection instance.
23
-     *
24
-     * @return connection
25
-     */
26
-    public function getConnection(): Connection
27
-    {
28
-        return $this->connection;
29
-    }
21
+	/**
22
+	 * Returns the current connection instance.
23
+	 *
24
+	 * @return connection
25
+	 */
26
+	public function getConnection(): Connection
27
+	{
28
+		return $this->connection;
29
+	}
30 30
 
31
-    /**
32
-     * @param string          $column  The database column which holds the JSON value
33
-     * @param string|string[] $nodes   The json key/index to search
34
-     * @return \Pixie\QueryBuilder\Raw
35
-     */
36
-    public function extractAndUnquote(string $column, $nodes): Raw
37
-    {
31
+	/**
32
+	 * @param string          $column  The database column which holds the JSON value
33
+	 * @param string|string[] $nodes   The json key/index to search
34
+	 * @return \Pixie\QueryBuilder\Raw
35
+	 */
36
+	public function extractAndUnquote(string $column, $nodes): Raw
37
+	{
38 38
 
39
-        // Unpack any nodes.
40
-        if (is_array($nodes)) {
41
-            $nodes = \implode('.', $nodes);
42
-        }
39
+		// Unpack any nodes.
40
+		if (is_array($nodes)) {
41
+			$nodes = \implode('.', $nodes);
42
+		}
43 43
 
44
-        // Add any possible prefixes to the key
45
-        $column = $this->addTablePrefix($column, true);
44
+		// Add any possible prefixes to the key
45
+		$column = $this->addTablePrefix($column, true);
46 46
 
47
-        return new Raw("JSON_UNQUOTE(JSON_EXTRACT({$column}, \"$.{$nodes}\"))");
48
-    }
47
+		return new Raw("JSON_UNQUOTE(JSON_EXTRACT({$column}, \"$.{$nodes}\"))");
48
+	}
49 49
 
50
-    /**
51
-     * @param string          $column  The database column which holds the JSON value
52
-     * @param string|string[] $nodes   The json key/index to search
53
-     * @param string|null     $alias   The alias to apply to this expression. Will be set as "json_{$column}" if not defined.
54
-     * @return \Pixie\QueryBuilder\Raw
55
-     */
56
-    public function extractAndUnquoteWithAlias(string $column, $nodes, ?string $alias = null): Raw
57
-    {
58
-        return new Raw(sprintf(
59
-            "%s AS %s",
60
-            $this->extractAndUnquote($column, $nodes)->getValue(),
61
-            null === $alias ? "json_{$column}" : $alias
62
-        ));
63
-    }
50
+	/**
51
+	 * @param string          $column  The database column which holds the JSON value
52
+	 * @param string|string[] $nodes   The json key/index to search
53
+	 * @param string|null     $alias   The alias to apply to this expression. Will be set as "json_{$column}" if not defined.
54
+	 * @return \Pixie\QueryBuilder\Raw
55
+	 */
56
+	public function extractAndUnquoteWithAlias(string $column, $nodes, ?string $alias = null): Raw
57
+	{
58
+		return new Raw(sprintf(
59
+			"%s AS %s",
60
+			$this->extractAndUnquote($column, $nodes)->getValue(),
61
+			null === $alias ? "json_{$column}" : $alias
62
+		));
63
+	}
64 64
 }
Please login to merge, or discard this patch.