PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 1.5.21
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v1.5.21
2.4.0 2.3.0 2.2.5 2.2.0 2.1.2 2.1.1 trunk 1.10.0 1.10.01 1.10.02 1.5.0 1.5.01 1.5.02 1.5.1 1.5.10 1.5.20 1.5.21 1.5.22 1.5.23 1.5.24 1.5.25 1.6.0 1.7.0 1.7.1 1.7.2 All 33 releases
fluent-booking / vendor / wpfluent / framework / src / WPFluent / Database / Query / Grammar.php

Grammar.php in Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution 1.5.21, at vendor/wpfluent/framework/src/WPFluent/Database/Query/Grammar.php

1,334 lines 36.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentBooking\Framework\Database\Query;
4
5 use RuntimeException;
6 use FluentBooking\Framework\Support\Arr;
7 use FluentBooking\Framework\Support\Str;
8 use FluentBooking\Framework\Support\Helper;
9 use FluentBooking\Framework\Database\BaseGrammar;
10 use FluentBooking\Framework\Database\Query\Builder;
11 use FluentBooking\Framework\Database\Query\JoinClause;
12
13 class Grammar extends BaseGrammar
14 {
15 /**
16 * The grammar specific operators.
17 *
18 * @var array
19 */
20 protected $operators = [];
21
22 /**
23 * The grammar specific bitwise operators.
24 *
25 * @var array
26 */
27 protected $bitwiseOperators = [];
28
29 /**
30 * The components that make up a select clause.
31 *
32 * @var string[]
33 */
34 protected $selectComponents = [
35 'aggregate',
36 'columns',
37 'from',
38 'joins',
39 'wheres',
40 'groups',
41 'havings',
42 'orders',
43 'limit',
44 'offset',
45 'lock',
46 ];
47
48 /**
49 * Compile a select query into SQL.
50 *
51 * @param \FluentBooking\Framework\Database\Query\Builder $query
52 * @return string
53 */
54 public function compileSelect(Builder $query)
55 {
56 if (($query->unions || $query->havings) && $query->aggregate) {
57 return $this->compileUnionAggregate($query);
58 }
59
60 // If the query does not have any columns set, we'll set the columns to the
61 // * character to just get all of the columns from the database. Then we
62 // can build the query and concatenate all the pieces together as one.
63 $original = $query->columns;
64
65 if (is_null($query->columns)) {
66 $query->columns = ['*'];
67 }
68
69 // To compile the query, we'll spin through each component of the query and
70 // see if that component exists. If it does we'll just call the compiler
71 // function for the component which is responsible for making the SQL.
72 $sql = trim($this->concatenate(
73 $this->compileComponents($query))
74 );
75
76 if ($query->unions) {
77 $sql = $this->wrapUnion($sql).' '.$this->compileUnions($query);
78 }
79
80 $query->columns = $original;
81
82 return $sql;
83 }
84
85 /**
86 * Compile the components necessary for a select clause.
87 *
88 * @param \FluentBooking\Framework\Database\Query\Builder $query
89 * @return array
90 */
91 protected function compileComponents(Builder $query)
92 {
93 $sql = [];
94
95 foreach ($this->selectComponents as $component) {
96 if (isset($query->$component)) {
97 $method = 'compile'.ucfirst($component);
98
99 $sql[$component] = $this->$method($query, $query->$component);
100 }
101 }
102
103 return $sql;
104 }
105
106 /**
107 * Compile an aggregated select clause.
108 *
109 * @param \FluentBooking\Framework\Database\Query\Builder $query
110 * @param array $aggregate
111 * @return string
112 */
113 protected function compileAggregate(Builder $query, $aggregate)
114 {
115 $column = $this->columnize($aggregate['columns']);
116
117 // If the query has a "distinct" constraint and we're not asking for all columns
118 // we need to prepend "distinct" onto the column name so that the query takes
119 // it into account when it performs the aggregating operations on the data.
120 if (is_array($query->distinct)) {
121 $column = 'distinct '.$this->columnize($query->distinct);
122 } elseif ($query->distinct && $column !== '*') {
123 $column = 'distinct '.$column;
124 }
125
126 return 'select '.$aggregate['function'].'('.$column.') as aggregate';
127 }
128
129 /**
130 * Compile the "select *" portion of the query.
131 *
132 * @param \FluentBooking\Framework\Database\Query\Builder $query
133 * @param array $columns
134 * @return string|null
135 */
136 protected function compileColumns(Builder $query, $columns)
137 {
138 // If the query is actually performing an aggregating select, we will let that
139 // compiler handle the building of the select clauses, as it will need some
140 // more syntax that is best handled by that function to keep things neat.
141 if (! is_null($query->aggregate)) {
142 return;
143 }
144
145 if ($query->distinct) {
146 $select = 'select distinct ';
147 } else {
148 $select = 'select ';
149 }
150
151 return $select.$this->columnize($columns);
152 }
153
154 /**
155 * Compile the "from" portion of the query.
156 *
157 * @param \FluentBooking\Framework\Database\Query\Builder $query
158 * @param string $table
159 * @return string
160 */
161 protected function compileFrom(Builder $query, $table)
162 {
163 return 'from '.$this->wrapTable($table);
164 }
165
166 /**
167 * Compile the "join" portions of the query.
168 *
169 * @param \FluentBooking\Framework\Database\Query\Builder $query
170 * @param array $joins
171 * @return string
172 */
173 protected function compileJoins(Builder $query, $joins)
174 {
175 return Helper::collect($joins)->map(function ($join) use ($query) {
176 $table = $this->wrapTable($join->table);
177
178 $nestedJoins = is_null($join->joins) ? '' : ' '.$this->compileJoins($query, $join->joins);
179
180 $tableAndNestedJoins = is_null($join->joins) ? $table : '('.$table.$nestedJoins.')';
181
182 return trim("{$join->type} join {$tableAndNestedJoins} {$this->compileWheres($join)}");
183 })->implode(' ');
184 }
185
186 /**
187 * Compile the "where" portions of the query.
188 *
189 * @param \FluentBooking\Framework\Database\Query\Builder $query
190 * @return string
191 */
192 public function compileWheres(Builder $query)
193 {
194 // Each type of where clauses has its own compiler function which is responsible
195 // for actually creating the where clauses SQL. This helps keep the code nice
196 // and maintainable since each clause has a very small method that it uses.
197 if (is_null($query->wheres)) {
198 return '';
199 }
200
201 // If we actually have some where clauses, we will strip off the first boolean
202 // operator, which is added by the query builders for convenience so we can
203 // avoid checking for the first clauses in each of the compilers methods.
204 if (count($sql = $this->compileWheresToArray($query)) > 0) {
205 return $this->concatenateWhereClauses($query, $sql);
206 }
207
208 return '';
209 }
210
211 /**
212 * Get an array of all the where clauses for the query.
213 *
214 * @param \FluentBooking\Framework\Database\Query\Builder $query
215 * @return array
216 */
217 protected function compileWheresToArray($query)
218 {
219 return Helper::collect($query->wheres)->map(function ($where) use ($query) {
220 return $where['boolean'].' '.$this->{"where{$where['type']}"}($query, $where);
221 })->all();
222 }
223
224 /**
225 * Format the where clause statements into one string.
226 *
227 * @param \FluentBooking\Framework\Database\Query\Builder $query
228 * @param array $sql
229 * @return string
230 */
231 protected function concatenateWhereClauses($query, $sql)
232 {
233 $conjunction = $query instanceof JoinClause ? 'on' : 'where';
234
235 return $conjunction.' '.$this->removeLeadingBoolean(implode(' ', $sql));
236 }
237
238 /**
239 * Compile a raw where clause.
240 *
241 * @param \FluentBooking\Framework\Database\Query\Builder $query
242 * @param array $where
243 * @return string
244 */
245 protected function whereRaw(Builder $query, $where)
246 {
247 return $where['sql'];
248 }
249
250 /**
251 * Compile a basic where clause.
252 *
253 * @param \FluentBooking\Framework\Database\Query\Builder $query
254 * @param array $where
255 * @return string
256 */
257 protected function whereBasic(Builder $query, $where)
258 {
259 $value = $this->parameter($where['value']);
260
261 $operator = str_replace('?', '??', $where['operator']);
262
263 return $this->wrap($where['column']).' '.$operator.' '.$value;
264 }
265
266 /**
267 * Compile a bitwise operator where clause.
268 *
269 * @param \FluentBooking\Framework\Database\Query\Builder $query
270 * @param array $where
271 * @return string
272 */
273 protected function whereBitwise(Builder $query, $where)
274 {
275 return $this->whereBasic($query, $where);
276 }
277
278 /**
279 * Compile a "where in" clause.
280 *
281 * @param \FluentBooking\Framework\Database\Query\Builder $query
282 * @param array $where
283 * @return string
284 */
285 protected function whereIn(Builder $query, $where)
286 {
287 if (! empty($where['values'])) {
288 return $this->wrap($where['column']).' in ('.$this->parameterize($where['values']).')';
289 }
290
291 return '0 = 1';
292 }
293
294 /**
295 * Compile a "where not in" clause.
296 *
297 * @param \FluentBooking\Framework\Database\Query\Builder $query
298 * @param array $where
299 * @return string
300 */
301 protected function whereNotIn(Builder $query, $where)
302 {
303 if (! empty($where['values'])) {
304 return $this->wrap($where['column']).' not in ('.$this->parameterize($where['values']).')';
305 }
306
307 return '1 = 1';
308 }
309
310 /**
311 * Compile a "where not in raw" clause.
312 *
313 * For safety, whereIntegerInRaw ensures this method is only used with integer values.
314 *
315 * @param \FluentBooking\Framework\Database\Query\Builder $query
316 * @param array $where
317 * @return string
318 */
319 protected function whereNotInRaw(Builder $query, $where)
320 {
321 if (! empty($where['values'])) {
322 return $this->wrap($where['column']).' not in ('.implode(', ', $where['values']).')';
323 }
324
325 return '1 = 1';
326 }
327
328 /**
329 * Compile a "where in raw" clause.
330 *
331 * For safety, whereIntegerInRaw ensures this method is only used with integer values.
332 *
333 * @param \FluentBooking\Framework\Database\Query\Builder $query
334 * @param array $where
335 * @return string
336 */
337 protected function whereInRaw(Builder $query, $where)
338 {
339 if (! empty($where['values'])) {
340 return $this->wrap($where['column']).' in ('.implode(', ', $where['values']).')';
341 }
342
343 return '0 = 1';
344 }
345
346 /**
347 * Compile a "where null" clause.
348 *
349 * @param \FluentBooking\Framework\Database\Query\Builder $query
350 * @param array $where
351 * @return string
352 */
353 protected function whereNull(Builder $query, $where)
354 {
355 return $this->wrap($where['column']).' is null';
356 }
357
358 /**
359 * Compile a "where not null" clause.
360 *
361 * @param \FluentBooking\Framework\Database\Query\Builder $query
362 * @param array $where
363 * @return string
364 */
365 protected function whereNotNull(Builder $query, $where)
366 {
367 return $this->wrap($where['column']).' is not null';
368 }
369
370 /**
371 * Compile a "between" where clause.
372 *
373 * @param \FluentBooking\Framework\Database\Query\Builder $query
374 * @param array $where
375 * @return string
376 */
377 protected function whereBetween(Builder $query, $where)
378 {
379 $between = $where['not'] ? 'not between' : 'between';
380
381 $min = $this->parameter(reset($where['values']));
382
383 $max = $this->parameter(end($where['values']));
384
385 return $this->wrap($where['column']).' '.$between.' '.$min.' and '.$max;
386 }
387
388 /**
389 * Compile a "between" where clause.
390 *
391 * @param \FluentBooking\Framework\Database\Query\Builder $query
392 * @param array $where
393 * @return string
394 */
395 protected function whereBetweenColumns(Builder $query, $where)
396 {
397 $between = $where['not'] ? 'not between' : 'between';
398
399 $min = $this->wrap(reset($where['values']));
400
401 $max = $this->wrap(end($where['values']));
402
403 return $this->wrap($where['column']).' '.$between.' '.$min.' and '.$max;
404 }
405
406 /**
407 * Compile a "where date" clause.
408 *
409 * @param \FluentBooking\Framework\Database\Query\Builder $query
410 * @param array $where
411 * @return string
412 */
413 protected function whereDate(Builder $query, $where)
414 {
415 return $this->dateBasedWhere('date', $query, $where);
416 }
417
418 /**
419 * Compile a "where time" clause.
420 *
421 * @param \FluentBooking\Framework\Database\Query\Builder $query
422 * @param array $where
423 * @return string
424 */
425 protected function whereTime(Builder $query, $where)
426 {
427 return $this->dateBasedWhere('time', $query, $where);
428 }
429
430 /**
431 * Compile a "where day" clause.
432 *
433 * @param \FluentBooking\Framework\Database\Query\Builder $query
434 * @param array $where
435 * @return string
436 */
437 protected function whereDay(Builder $query, $where)
438 {
439 return $this->dateBasedWhere('day', $query, $where);
440 }
441
442 /**
443 * Compile a "where month" clause.
444 *
445 * @param \FluentBooking\Framework\Database\Query\Builder $query
446 * @param array $where
447 * @return string
448 */
449 protected function whereMonth(Builder $query, $where)
450 {
451 return $this->dateBasedWhere('month', $query, $where);
452 }
453
454 /**
455 * Compile a "where year" clause.
456 *
457 * @param \FluentBooking\Framework\Database\Query\Builder $query
458 * @param array $where
459 * @return string
460 */
461 protected function whereYear(Builder $query, $where)
462 {
463 return $this->dateBasedWhere('year', $query, $where);
464 }
465
466 /**
467 * Compile a date based where clause.
468 *
469 * @param string $type
470 * @param \FluentBooking\Framework\Database\Query\Builder $query
471 * @param array $where
472 * @return string
473 */
474 protected function dateBasedWhere($type, Builder $query, $where)
475 {
476 $value = $this->parameter($where['value']);
477
478 return $type.'('.$this->wrap($where['column']).') '.$where['operator'].' '.$value;
479 }
480
481 /**
482 * Compile a where clause comparing two columns.
483 *
484 * @param \FluentBooking\Framework\Database\Query\Builder $query
485 * @param array $where
486 * @return string
487 */
488 protected function whereColumn(Builder $query, $where)
489 {
490 return $this->wrap($where['first']).' '.$where['operator'].' '.$this->wrap($where['second']);
491 }
492
493 /**
494 * Compile a nested where clause.
495 *
496 * @param \FluentBooking\Framework\Database\Query\Builder $query
497 * @param array $where
498 * @return string
499 */
500 protected function whereNested(Builder $query, $where)
501 {
502 // Here we will calculate what portion of the string we need to remove. If this
503 // is a join clause query, we need to remove the "on" portion of the SQL and
504 // if it is a normal query we need to take the leading "where" of queries.
505 $offset = $query instanceof JoinClause ? 3 : 6;
506
507 return '('.substr($this->compileWheres($where['query']), $offset).')';
508 }
509
510 /**
511 * Compile a where condition with a sub-select.
512 *
513 * @param \FluentBooking\Framework\Database\Query\Builder $query
514 * @param array $where
515 * @return string
516 */
517 protected function whereSub(Builder $query, $where)
518 {
519 $select = $this->compileSelect($where['query']);
520
521 return $this->wrap($where['column']).' '.$where['operator']." ($select)";
522 }
523
524 /**
525 * Compile a where exists clause.
526 *
527 * @param \FluentBooking\Framework\Database\Query\Builder $query
528 * @param array $where
529 * @return string
530 */
531 protected function whereExists(Builder $query, $where)
532 {
533 return 'exists ('.$this->compileSelect($where['query']).')';
534 }
535
536 /**
537 * Compile a where exists clause.
538 *
539 * @param \FluentBooking\Framework\Database\Query\Builder $query
540 * @param array $where
541 * @return string
542 */
543 protected function whereNotExists(Builder $query, $where)
544 {
545 return 'not exists ('.$this->compileSelect($where['query']).')';
546 }
547
548 /**
549 * Compile a where row values condition.
550 *
551 * @param \FluentBooking\Framework\Database\Query\Builder $query
552 * @param array $where
553 * @return string
554 */
555 protected function whereRowValues(Builder $query, $where)
556 {
557 $columns = $this->columnize($where['columns']);
558
559 $values = $this->parameterize($where['values']);
560
561 return '('.$columns.') '.$where['operator'].' ('.$values.')';
562 }
563
564 /**
565 * Compile a "where JSON boolean" clause.
566 *
567 * @param \FluentBooking\Framework\Database\Query\Builder $query
568 * @param array $where
569 * @return string
570 */
571 protected function whereJsonBoolean(Builder $query, $where)
572 {
573 $column = $this->wrapJsonBooleanSelector($where['column']);
574
575 $value = $this->wrapJsonBooleanValue(
576 $this->parameter($where['value'])
577 );
578
579 return $column.' '.$where['operator'].' '.$value;
580 }
581
582 /**
583 * Compile a "where JSON contains" clause.
584 *
585 * @param \FluentBooking\Framework\Database\Query\Builder $query
586 * @param array $where
587 * @return string
588 */
589 protected function whereJsonContains(Builder $query, $where)
590 {
591 $not = $where['not'] ? 'not ' : '';
592
593 return $not.$this->compileJsonContains(
594 $where['column'],
595 $this->parameter($where['value'])
596 );
597 }
598
599 /**
600 * Compile a "JSON contains" statement into SQL.
601 *
602 * @param string $column
603 * @param string $value
604 * @return string
605 *
606 * @throws \RuntimeException
607 */
608 protected function compileJsonContains($column, $value)
609 {
610 throw new RuntimeException('This database engine does not support JSON contains operations.');
611 }
612
613 /**
614 * Prepare the binding for a "JSON contains" statement.
615 *
616 * @param mixed $binding
617 * @return string
618 */
619 public function prepareBindingForJsonContains($binding)
620 {
621 return json_encode($binding);
622 }
623
624 /**
625 * Compile a "where JSON length" clause.
626 *
627 * @param \FluentBooking\Framework\Database\Query\Builder $query
628 * @param array $where
629 * @return string
630 */
631 protected function whereJsonLength(Builder $query, $where)
632 {
633 return $this->compileJsonLength(
634 $where['column'],
635 $where['operator'],
636 $this->parameter($where['value'])
637 );
638 }
639
640 /**
641 * Compile a "JSON length" statement into SQL.
642 *
643 * @param string $column
644 * @param string $operator
645 * @param string $value
646 * @return string
647 *
648 * @throws \RuntimeException
649 */
650 protected function compileJsonLength($column, $operator, $value)
651 {
652 throw new RuntimeException('This database engine does not support JSON length operations.');
653 }
654
655 /**
656 * Compile a "where fulltext" clause.
657 *
658 * @param \FluentBooking\Framework\Database\Query\Builder $query
659 * @param array $where
660 * @return string
661 */
662 public function whereFullText(Builder $query, $where)
663 {
664 throw new RuntimeException('This database engine does not support fulltext search operations.');
665 }
666
667 /**
668 * Compile the "group by" portions of the query.
669 *
670 * @param \FluentBooking\Framework\Database\Query\Builder $query
671 * @param array $groups
672 * @return string
673 */
674 protected function compileGroups(Builder $query, $groups)
675 {
676 return 'group by '.$this->columnize($groups);
677 }
678
679 /**
680 * Compile the "having" portions of the query.
681 *
682 * @param \FluentBooking\Framework\Database\Query\Builder $query
683 * @param array $havings
684 * @return string
685 */
686 protected function compileHavings(Builder $query, $havings)
687 {
688 $sql = implode(' ', array_map([$this, 'compileHaving'], $havings));
689
690 return 'having '.$this->removeLeadingBoolean($sql);
691 }
692
693 /**
694 * Compile a single having clause.
695 *
696 * @param array $having
697 * @return string
698 */
699 protected function compileHaving(array $having)
700 {
701 // If the having clause is "raw", we can just return the clause straight away
702 // without doing any more processing on it. Otherwise, we will compile the
703 // clause into SQL based on the components that make it up from builder.
704 if ($having['type'] === 'Raw') {
705 return $having['boolean'].' '.$having['sql'];
706 }
707 if ($having['type'] === 'between') {
708 return $this->compileHavingBetween($having);
709 }
710
711 return $this->compileBasicHaving($having);
712 }
713
714 /**
715 * Compile a basic having clause.
716 *
717 * @param array $having
718 * @return string
719 */
720 protected function compileBasicHaving($having)
721 {
722 $column = $this->wrap($having['column']);
723
724 $parameter = $this->parameter($having['value']);
725
726 return $having['boolean'].' '.$column.' '.$having['operator'].' '.$parameter;
727 }
728
729 /**
730 * Compile a "between" having clause.
731 *
732 * @param array $having
733 * @return string
734 */
735 protected function compileHavingBetween($having)
736 {
737 $between = $having['not'] ? 'not between' : 'between';
738
739 $column = $this->wrap($having['column']);
740
741 $min = $this->parameter(Helper::head($having['values'])); // @need_fix
742
743 $max = $this->parameter(Helper::last($having['values'])); // @need_fix
744
745 return $having['boolean'].' '.$column.' '.$between.' '.$min.' and '.$max;
746 }
747
748 /**
749 * Compile the "order by" portions of the query.
750 *
751 * @param \FluentBooking\Framework\Database\Query\Builder $query
752 * @param array $orders
753 * @return string
754 */
755 protected function compileOrders(Builder $query, $orders)
756 {
757 if (! empty($orders)) {
758 return 'order by '.implode(', ', $this->compileOrdersToArray($query, $orders));
759 }
760
761 return '';
762 }
763
764 /**
765 * Compile the query orders to an array.
766 *
767 * @param \FluentBooking\Framework\Database\Query\Builder $query
768 * @param array $orders
769 * @return array
770 */
771 protected function compileOrdersToArray(Builder $query, $orders)
772 {
773 return array_map(function ($order) {
774 return $order['sql'] ?? $this->wrap($order['column']).' '.$order['direction'];
775 }, $orders);
776 }
777
778 /**
779 * Compile the random statement into SQL.
780 *
781 * @param string $seed
782 * @return string
783 */
784 public function compileRandom($seed)
785 {
786 return 'RANDOM()';
787 }
788
789 /**
790 * Compile the "limit" portions of the query.
791 *
792 * @param \FluentBooking\Framework\Database\Query\Builder $query
793 * @param int $limit
794 * @return string
795 */
796 protected function compileLimit(Builder $query, $limit)
797 {
798 return 'limit '.(int) $limit;
799 }
800
801 /**
802 * Compile the "offset" portions of the query.
803 *
804 * @param \FluentBooking\Framework\Database\Query\Builder $query
805 * @param int $offset
806 * @return string
807 */
808 protected function compileOffset(Builder $query, $offset)
809 {
810 return 'offset '.(int) $offset;
811 }
812
813 /**
814 * Compile the "union" queries attached to the main query.
815 *
816 * @param \FluentBooking\Framework\Database\Query\Builder $query
817 * @return string
818 */
819 protected function compileUnions(Builder $query)
820 {
821 $sql = '';
822
823 foreach ($query->unions as $union) {
824 $sql .= $this->compileUnion($union);
825 }
826
827 if (! empty($query->unionOrders)) {
828 $sql .= ' '.$this->compileOrders($query, $query->unionOrders);
829 }
830
831 if (isset($query->unionLimit)) {
832 $sql .= ' '.$this->compileLimit($query, $query->unionLimit);
833 }
834
835 if (isset($query->unionOffset)) {
836 $sql .= ' '.$this->compileOffset($query, $query->unionOffset);
837 }
838
839 return ltrim($sql);
840 }
841
842 /**
843 * Compile a single union statement.
844 *
845 * @param array $union
846 * @return string
847 */
848 protected function compileUnion(array $union)
849 {
850 $conjunction = $union['all'] ? ' union all ' : ' union ';
851
852 return $conjunction.$this->wrapUnion($union['query']->toSql());
853 }
854
855 /**
856 * Wrap a union subquery in parentheses.
857 *
858 * @param string $sql
859 * @return string
860 */
861 protected function wrapUnion($sql)
862 {
863 return '('.$sql.')';
864 }
865
866 /**
867 * Compile a union aggregate query into SQL.
868 *
869 * @param \FluentBooking\Framework\Database\Query\Builder $query
870 * @return string
871 */
872 protected function compileUnionAggregate(Builder $query)
873 {
874 $sql = $this->compileAggregate($query, $query->aggregate);
875
876 $query->aggregate = null;
877
878 return $sql.' from ('.$this->compileSelect($query).') as '.$this->wrapTable('temp_table');
879 }
880
881 /**
882 * Compile an exists statement into SQL.
883 *
884 * @param \FluentBooking\Framework\Database\Query\Builder $query
885 * @return string
886 */
887 public function compileExists(Builder $query)
888 {
889 $select = $this->compileSelect($query);
890
891 return "select exists({$select}) as {$this->wrap('exists')}";
892 }
893
894 /**
895 * Compile an insert statement into SQL.
896 *
897 * @param \FluentBooking\Framework\Database\Query\Builder $query
898 * @param array $values
899 * @return string
900 */
901 public function compileInsert(Builder $query, array $values)
902 {
903 // Essentially we will force every insert to be treated as a batch insert which
904 // simply makes creating the SQL easier for us since we can utilize the same
905 // basic routine regardless of an amount of records given to us to insert.
906 $table = $this->wrapTable($query->from);
907
908 if (empty($values)) {
909 return "insert into {$table} default values";
910 }
911
912 if (! is_array(reset($values))) {
913 $values = [$values];
914 }
915
916 $columns = $this->columnize(array_keys(reset($values)));
917
918 // We need to build a list of parameter place-holders of values that are bound
919 // to the query. Each insert should have the exact same amount of parameter
920 // bindings so we will loop through the record and parameterize them all.
921 $parameters = Helper::collect($values)->map(function ($record) {
922 return '('.$this->parameterize($record).')';
923 })->implode(', ');
924
925 return "insert into $table ($columns) values $parameters";
926 }
927
928 /**
929 * Compile an insert ignore statement into SQL.
930 *
931 * @param \FluentBooking\Framework\Database\Query\Builder $query
932 * @param array $values
933 * @return string
934 *
935 * @throws \RuntimeException
936 */
937 public function compileInsertOrIgnore(Builder $query, array $values)
938 {
939 throw new RuntimeException('This database engine does not support inserting while ignoring errors.');
940 }
941
942 /**
943 * Compile an insert and get ID statement into SQL.
944 *
945 * @param \FluentBooking\Framework\Database\Query\Builder $query
946 * @param array $values
947 * @param string $sequence
948 * @return string
949 */
950 public function compileInsertGetId(Builder $query, $values, $sequence)
951 {
952 return $this->compileInsert($query, $values);
953 }
954
955 /**
956 * Compile an insert statement using a subquery into SQL.
957 *
958 * @param \FluentBooking\Framework\Database\Query\Builder $query
959 * @param array $columns
960 * @param string $sql
961 * @return string
962 */
963 public function compileInsertUsing(Builder $query, array $columns, string $sql)
964 {
965 return "insert into {$this->wrapTable($query->from)} ({$this->columnize($columns)}) $sql";
966 }
967
968 /**
969 * Compile an update statement into SQL.
970 *
971 * @param \FluentBooking\Framework\Database\Query\Builder $query
972 * @param array $values
973 * @return string
974 */
975 public function compileUpdate(Builder $query, array $values)
976 {
977 $table = $this->wrapTable($query->from);
978
979 $columns = $this->compileUpdateColumns($query, $values);
980
981 $where = $this->compileWheres($query);
982
983 return trim(
984 isset($query->joins)
985 ? $this->compileUpdateWithJoins($query, $table, $columns, $where)
986 : $this->compileUpdateWithoutJoins($query, $table, $columns, $where)
987 );
988 }
989
990 /**
991 * Compile the columns for an update statement.
992 *
993 * @param \FluentBooking\Framework\Database\Query\Builder $query
994 * @param array $values
995 * @return string
996 */
997 protected function compileUpdateColumns(Builder $query, array $values)
998 {
999 return Helper::collect($values)->map(function ($value, $key) {
1000 return $this->wrap($key).' = '.$this->parameter($value);
1001 })->implode(', ');
1002 }
1003
1004 /**
1005 * Compile an update statement without joins into SQL.
1006 *
1007 * @param \FluentBooking\Framework\Database\Query\Builder $query
1008 * @param string $table
1009 * @param string $columns
1010 * @param string $where
1011 * @return string
1012 */
1013 protected function compileUpdateWithoutJoins(Builder $query, $table, $columns, $where)
1014 {
1015 return "update {$table} set {$columns} {$where}";
1016 }
1017
1018 /**
1019 * Compile an update statement with joins into SQL.
1020 *
1021 * @param \FluentBooking\Framework\Database\Query\Builder $query
1022 * @param string $table
1023 * @param string $columns
1024 * @param string $where
1025 * @return string
1026 */
1027 protected function compileUpdateWithJoins(Builder $query, $table, $columns, $where)
1028 {
1029 $joins = $this->compileJoins($query, $query->joins);
1030
1031 return "update {$table} {$joins} set {$columns} {$where}";
1032 }
1033
1034 /**
1035 * Compile an "upsert" statement into SQL.
1036 *
1037 * @param \FluentBooking\Framework\Database\Query\Builder $query
1038 * @param array $values
1039 * @param array $uniqueBy
1040 * @param array $update
1041 * @return string
1042 *
1043 * @throws \RuntimeException
1044 */
1045 public function compileUpsert(Builder $query, array $values, array $uniqueBy, array $update)
1046 {
1047 throw new RuntimeException('This database engine does not support upserts.');
1048 }
1049
1050 /**
1051 * Prepare the bindings for an update statement.
1052 *
1053 * @param array $bindings
1054 * @param array $values
1055 * @return array
1056 */
1057 public function prepareBindingsForUpdate(array $bindings, array $values)
1058 {
1059 $cleanBindings = Arr::except($bindings, ['select', 'join']);
1060
1061 return array_values(
1062 array_merge($bindings['join'], $values, Arr::flatten($cleanBindings))
1063 );
1064 }
1065
1066 /**
1067 * Compile a delete statement into SQL.
1068 *
1069 * @param \FluentBooking\Framework\Database\Query\Builder $query
1070 * @return string
1071 */
1072 public function compileDelete(Builder $query)
1073 {
1074 $table = $this->wrapTable($query->from);
1075
1076 $where = $this->compileWheres($query);
1077
1078 return trim(
1079 isset($query->joins)
1080 ? $this->compileDeleteWithJoins($query, $table, $where)
1081 : $this->compileDeleteWithoutJoins($query, $table, $where)
1082 );
1083 }
1084
1085 /**
1086 * Compile a delete statement without joins into SQL.
1087 *
1088 * @param \FluentBooking\Framework\Database\Query\Builder $query
1089 * @param string $table
1090 * @param string $where
1091 * @return string
1092 */
1093 protected function compileDeleteWithoutJoins(Builder $query, $table, $where)
1094 {
1095 return "delete from {$table} {$where}";
1096 }
1097
1098 /**
1099 * Compile a delete statement with joins into SQL.
1100 *
1101 * @param \FluentBooking\Framework\Database\Query\Builder $query
1102 * @param string $table
1103 * @param string $where
1104 * @return string
1105 */
1106 protected function compileDeleteWithJoins(Builder $query, $table, $where)
1107 {
1108 $alias = Helper::last(explode(' as ', $table)); // @need_fix
1109
1110 $joins = $this->compileJoins($query, $query->joins);
1111
1112 return "delete {$alias} from {$table} {$joins} {$where}";
1113 }
1114
1115 /**
1116 * Prepare the bindings for a delete statement.
1117 *
1118 * @param array $bindings
1119 * @return array
1120 */
1121 public function prepareBindingsForDelete(array $bindings)
1122 {
1123 return Arr::flatten(
1124 Arr::except($bindings, 'select')
1125 );
1126 }
1127
1128 /**
1129 * Compile a truncate table statement into SQL.
1130 *
1131 * @param \FluentBooking\Framework\Database\Query\Builder $query
1132 * @return array
1133 */
1134 public function compileTruncate(Builder $query)
1135 {
1136 return ['truncate table '.$this->wrapTable($query->from) => []];
1137 }
1138
1139 /**
1140 * Compile the lock into SQL.
1141 *
1142 * @param \FluentBooking\Framework\Database\Query\Builder $query
1143 * @param bool|string $value
1144 * @return string
1145 */
1146 protected function compileLock(Builder $query, $value)
1147 {
1148 return is_string($value) ? $value : '';
1149 }
1150
1151 /**
1152 * Determine if the grammar supports savepoints.
1153 *
1154 * @return bool
1155 */
1156 public function supportsSavepoints()
1157 {
1158 return true;
1159 }
1160
1161 /**
1162 * Compile the SQL statement to define a savepoint.
1163 *
1164 * @param string $name
1165 * @return string
1166 */
1167 public function compileSavepoint($name)
1168 {
1169 return 'SAVEPOINT '.$name;
1170 }
1171
1172 /**
1173 * Compile the SQL statement to execute a savepoint rollback.
1174 *
1175 * @param string $name
1176 * @return string
1177 */
1178 public function compileSavepointRollBack($name)
1179 {
1180 return 'ROLLBACK TO SAVEPOINT '.$name;
1181 }
1182
1183 /**
1184 * Wrap a value in keyword identifiers.
1185 *
1186 * @param \FluentBooking\Framework\Database\Query\Expression|string $value
1187 * @param bool $prefixAlias
1188 * @return string
1189 */
1190 public function wrap($value, $prefixAlias = false)
1191 {
1192 if ($this->isExpression($value)) {
1193 return $this->getValue($value);
1194 }
1195
1196 // If the value being wrapped has a column alias we will need to separate out
1197 // the pieces so we can wrap each of the segments of the expression on its
1198 // own, and then join these both back together using the "as" connector.
1199 if (stripos($value, ' as ') !== false) {
1200 return $this->wrapAliasedValue($value, $prefixAlias);
1201 }
1202
1203 // If the given value is a JSON selector we will wrap it differently than a
1204 // traditional value. We will need to split this path and wrap each part
1205 // wrapped, etc. Otherwise, we will simply wrap the value as a string.
1206 if ($this->isJsonSelector($value)) {
1207 return $this->wrapJsonSelector($value);
1208 }
1209
1210 return $this->wrapSegments(explode('.', $value));
1211 }
1212
1213 /**
1214 * Wrap the given JSON selector.
1215 *
1216 * @param string $value
1217 * @return string
1218 *
1219 * @throws \RuntimeException
1220 */
1221 protected function wrapJsonSelector($value)
1222 {
1223 throw new RuntimeException('This database engine does not support JSON operations.');
1224 }
1225
1226 /**
1227 * Wrap the given JSON selector for boolean values.
1228 *
1229 * @param string $value
1230 * @return string
1231 */
1232 protected function wrapJsonBooleanSelector($value)
1233 {
1234 return $this->wrapJsonSelector($value);
1235 }
1236
1237 /**
1238 * Wrap the given JSON boolean value.
1239 *
1240 * @param string $value
1241 * @return string
1242 */
1243 protected function wrapJsonBooleanValue($value)
1244 {
1245 return $value;
1246 }
1247
1248 /**
1249 * Split the given JSON selector into the field and the optional path and wrap them separately.
1250 *
1251 * @param string $column
1252 * @return array
1253 */
1254 protected function wrapJsonFieldAndPath($column)
1255 {
1256 $parts = explode('->', $column, 2);
1257
1258 $field = $this->wrap($parts[0]);
1259
1260 $path = count($parts) > 1 ? ', '.$this->wrapJsonPath($parts[1], '->') : '';
1261
1262 return [$field, $path];
1263 }
1264
1265 /**
1266 * Wrap the given JSON path.
1267 *
1268 * @param string $value
1269 * @param string $delimiter
1270 * @return string
1271 */
1272 protected function wrapJsonPath($value, $delimiter = '->')
1273 {
1274 $value = preg_replace("/([\\\\]+)?\\'/", "''", $value);
1275
1276 return '\'$."'.str_replace($delimiter, '"."', $value).'"\'';
1277 }
1278
1279 /**
1280 * Determine if the given string is a JSON selector.
1281 *
1282 * @param string $value
1283 * @return bool
1284 */
1285 protected function isJsonSelector($value)
1286 {
1287 return Str::contains($value, '->');
1288 }
1289
1290 /**
1291 * Concatenate an array of segments, removing empties.
1292 *
1293 * @param array $segments
1294 * @return string
1295 */
1296 protected function concatenate($segments)
1297 {
1298 return implode(' ', array_filter($segments, function ($value) {
1299 return (string) $value !== '';
1300 }));
1301 }
1302
1303 /**
1304 * Remove the leading boolean from a statement.
1305 *
1306 * @param string $value
1307 * @return string
1308 */
1309 protected function removeLeadingBoolean($value)
1310 {
1311 return preg_replace('/and |or /i', '', $value, 1);
1312 }
1313
1314 /**
1315 * Get the grammar specific operators.
1316 *
1317 * @return array
1318 */
1319 public function getOperators()
1320 {
1321 return $this->operators;
1322 }
1323
1324 /**
1325 * Get the grammar specific bitwise operators.
1326 *
1327 * @return array
1328 */
1329 public function getBitwiseOperators()
1330 {
1331 return $this->bitwiseOperators;
1332 }
1333 }
1334