Subversion-Projekte lars-tiefland.laravel_shop

Revision

Revision 150 | Zur aktuellen Revision | Details | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
namespace Illuminate\Database\Query;
4
 
5
use BackedEnum;
6
use Carbon\CarbonPeriod;
7
use Closure;
8
use DateTimeInterface;
9
use Illuminate\Contracts\Database\Query\Builder as BuilderContract;
10
use Illuminate\Contracts\Support\Arrayable;
11
use Illuminate\Database\Concerns\BuildsQueries;
12
use Illuminate\Database\Concerns\ExplainsQueries;
13
use Illuminate\Database\ConnectionInterface;
14
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
15
use Illuminate\Database\Eloquent\Relations\Relation;
16
use Illuminate\Database\Query\Grammars\Grammar;
17
use Illuminate\Database\Query\Processors\Processor;
18
use Illuminate\Pagination\Paginator;
19
use Illuminate\Support\Arr;
20
use Illuminate\Support\Collection;
21
use Illuminate\Support\LazyCollection;
22
use Illuminate\Support\Str;
23
use Illuminate\Support\Traits\ForwardsCalls;
24
use Illuminate\Support\Traits\Macroable;
25
use InvalidArgumentException;
26
use LogicException;
27
use RuntimeException;
28
 
29
class Builder implements BuilderContract
30
{
31
    use BuildsQueries, ExplainsQueries, ForwardsCalls, Macroable {
32
        __call as macroCall;
33
    }
34
 
35
    /**
36
     * The database connection instance.
37
     *
38
     * @var \Illuminate\Database\ConnectionInterface
39
     */
40
    public $connection;
41
 
42
    /**
43
     * The database query grammar instance.
44
     *
45
     * @var \Illuminate\Database\Query\Grammars\Grammar
46
     */
47
    public $grammar;
48
 
49
    /**
50
     * The database query post processor instance.
51
     *
52
     * @var \Illuminate\Database\Query\Processors\Processor
53
     */
54
    public $processor;
55
 
56
    /**
57
     * The current query value bindings.
58
     *
59
     * @var array
60
     */
61
    public $bindings = [
62
        'select' => [],
63
        'from' => [],
64
        'join' => [],
65
        'where' => [],
66
        'groupBy' => [],
67
        'having' => [],
68
        'order' => [],
69
        'union' => [],
70
        'unionOrder' => [],
71
    ];
72
 
73
    /**
74
     * An aggregate function and column to be run.
75
     *
76
     * @var array
77
     */
78
    public $aggregate;
79
 
80
    /**
81
     * The columns that should be returned.
82
     *
83
     * @var array
84
     */
85
    public $columns;
86
 
87
    /**
88
     * Indicates if the query returns distinct results.
89
     *
90
     * Occasionally contains the columns that should be distinct.
91
     *
92
     * @var bool|array
93
     */
94
    public $distinct = false;
95
 
96
    /**
97
     * The table which the query is targeting.
98
     *
99
     * @var string
100
     */
101
    public $from;
102
 
103
    /**
104
     * The table joins for the query.
105
     *
106
     * @var array
107
     */
108
    public $joins;
109
 
110
    /**
111
     * The where constraints for the query.
112
     *
113
     * @var array
114
     */
115
    public $wheres = [];
116
 
117
    /**
118
     * The groupings for the query.
119
     *
120
     * @var array
121
     */
122
    public $groups;
123
 
124
    /**
125
     * The having constraints for the query.
126
     *
127
     * @var array
128
     */
129
    public $havings;
130
 
131
    /**
132
     * The orderings for the query.
133
     *
134
     * @var array
135
     */
136
    public $orders;
137
 
138
    /**
139
     * The maximum number of records to return.
140
     *
141
     * @var int
142
     */
143
    public $limit;
144
 
145
    /**
146
     * The number of records to skip.
147
     *
148
     * @var int
149
     */
150
    public $offset;
151
 
152
    /**
153
     * The query union statements.
154
     *
155
     * @var array
156
     */
157
    public $unions;
158
 
159
    /**
160
     * The maximum number of union records to return.
161
     *
162
     * @var int
163
     */
164
    public $unionLimit;
165
 
166
    /**
167
     * The number of union records to skip.
168
     *
169
     * @var int
170
     */
171
    public $unionOffset;
172
 
173
    /**
174
     * The orderings for the union query.
175
     *
176
     * @var array
177
     */
178
    public $unionOrders;
179
 
180
    /**
181
     * Indicates whether row locking is being used.
182
     *
183
     * @var string|bool
184
     */
185
    public $lock;
186
 
187
    /**
188
     * The callbacks that should be invoked before the query is executed.
189
     *
190
     * @var array
191
     */
192
    public $beforeQueryCallbacks = [];
193
 
194
    /**
195
     * All of the available clause operators.
196
     *
197
     * @var string[]
198
     */
199
    public $operators = [
200
        '=', '<', '>', '<=', '>=', '<>', '!=', '<=>',
201
        'like', 'like binary', 'not like', 'ilike',
202
        '&', '|', '^', '<<', '>>', '&~', 'is', 'is not',
203
        'rlike', 'not rlike', 'regexp', 'not regexp',
204
        '~', '~*', '!~', '!~*', 'similar to',
205
        'not similar to', 'not ilike', '~~*', '!~~*',
206
    ];
207
 
208
    /**
209
     * All of the available bitwise operators.
210
     *
211
     * @var string[]
212
     */
213
    public $bitwiseOperators = [
214
        '&', '|', '^', '<<', '>>', '&~',
215
    ];
216
 
217
    /**
218
     * Whether to use write pdo for the select.
219
     *
220
     * @var bool
221
     */
222
    public $useWritePdo = false;
223
 
224
    /**
225
     * Create a new query builder instance.
226
     *
227
     * @param  \Illuminate\Database\ConnectionInterface  $connection
228
     * @param  \Illuminate\Database\Query\Grammars\Grammar|null  $grammar
229
     * @param  \Illuminate\Database\Query\Processors\Processor|null  $processor
230
     * @return void
231
     */
232
    public function __construct(ConnectionInterface $connection,
233
                                Grammar $grammar = null,
234
                                Processor $processor = null)
235
    {
236
        $this->connection = $connection;
237
        $this->grammar = $grammar ?: $connection->getQueryGrammar();
238
        $this->processor = $processor ?: $connection->getPostProcessor();
239
    }
240
 
241
    /**
242
     * Set the columns to be selected.
243
     *
244
     * @param  array|mixed  $columns
245
     * @return $this
246
     */
247
    public function select($columns = ['*'])
248
    {
249
        $this->columns = [];
250
        $this->bindings['select'] = [];
251
 
252
        $columns = is_array($columns) ? $columns : func_get_args();
253
 
254
        foreach ($columns as $as => $column) {
255
            if (is_string($as) && $this->isQueryable($column)) {
256
                $this->selectSub($column, $as);
257
            } else {
258
                $this->columns[] = $column;
259
            }
260
        }
261
 
262
        return $this;
263
    }
264
 
265
    /**
266
     * Add a subselect expression to the query.
267
     *
268
     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string  $query
269
     * @param  string  $as
270
     * @return $this
271
     *
272
     * @throws \InvalidArgumentException
273
     */
274
    public function selectSub($query, $as)
275
    {
276
        [$query, $bindings] = $this->createSub($query);
277
 
278
        return $this->selectRaw(
279
            '('.$query.') as '.$this->grammar->wrap($as), $bindings
280
        );
281
    }
282
 
283
    /**
284
     * Add a new "raw" select expression to the query.
285
     *
286
     * @param  string  $expression
287
     * @param  array  $bindings
288
     * @return $this
289
     */
290
    public function selectRaw($expression, array $bindings = [])
291
    {
292
        $this->addSelect(new Expression($expression));
293
 
294
        if ($bindings) {
295
            $this->addBinding($bindings, 'select');
296
        }
297
 
298
        return $this;
299
    }
300
 
301
    /**
302
     * Makes "from" fetch from a subquery.
303
     *
304
     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string  $query
305
     * @param  string  $as
306
     * @return $this
307
     *
308
     * @throws \InvalidArgumentException
309
     */
310
    public function fromSub($query, $as)
311
    {
312
        [$query, $bindings] = $this->createSub($query);
313
 
314
        return $this->fromRaw('('.$query.') as '.$this->grammar->wrapTable($as), $bindings);
315
    }
316
 
317
    /**
318
     * Add a raw from clause to the query.
319
     *
320
     * @param  string  $expression
321
     * @param  mixed  $bindings
322
     * @return $this
323
     */
324
    public function fromRaw($expression, $bindings = [])
325
    {
326
        $this->from = new Expression($expression);
327
 
328
        $this->addBinding($bindings, 'from');
329
 
330
        return $this;
331
    }
332
 
333
    /**
334
     * Creates a subquery and parse it.
335
     *
336
     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string  $query
337
     * @return array
338
     */
339
    protected function createSub($query)
340
    {
341
        // If the given query is a Closure, we will execute it while passing in a new
342
        // query instance to the Closure. This will give the developer a chance to
343
        // format and work with the query before we cast it to a raw SQL string.
344
        if ($query instanceof Closure) {
345
            $callback = $query;
346
 
347
            $callback($query = $this->forSubQuery());
348
        }
349
 
350
        return $this->parseSub($query);
351
    }
352
 
353
    /**
354
     * Parse the subquery into SQL and bindings.
355
     *
356
     * @param  mixed  $query
357
     * @return array
358
     *
359
     * @throws \InvalidArgumentException
360
     */
361
    protected function parseSub($query)
362
    {
363
        if ($query instanceof self || $query instanceof EloquentBuilder || $query instanceof Relation) {
364
            $query = $this->prependDatabaseNameIfCrossDatabaseQuery($query);
365
 
366
            return [$query->toSql(), $query->getBindings()];
367
        } elseif (is_string($query)) {
368
            return [$query, []];
369
        } else {
370
            throw new InvalidArgumentException(
371
                'A subquery must be a query builder instance, a Closure, or a string.'
372
            );
373
        }
374
    }
375
 
376
    /**
377
     * Prepend the database name if the given query is on another database.
378
     *
379
     * @param  mixed  $query
380
     * @return mixed
381
     */
382
    protected function prependDatabaseNameIfCrossDatabaseQuery($query)
383
    {
384
        if ($query->getConnection()->getDatabaseName() !==
385
            $this->getConnection()->getDatabaseName()) {
386
            $databaseName = $query->getConnection()->getDatabaseName();
387
 
388
            if (! str_starts_with($query->from, $databaseName) && ! str_contains($query->from, '.')) {
389
                $query->from($databaseName.'.'.$query->from);
390
            }
391
        }
392
 
393
        return $query;
394
    }
395
 
396
    /**
397
     * Add a new select column to the query.
398
     *
399
     * @param  array|mixed  $column
400
     * @return $this
401
     */
402
    public function addSelect($column)
403
    {
404
        $columns = is_array($column) ? $column : func_get_args();
405
 
406
        foreach ($columns as $as => $column) {
407
            if (is_string($as) && $this->isQueryable($column)) {
408
                if (is_null($this->columns)) {
409
                    $this->select($this->from.'.*');
410
                }
411
 
412
                $this->selectSub($column, $as);
413
            } else {
414
                $this->columns[] = $column;
415
            }
416
        }
417
 
418
        return $this;
419
    }
420
 
421
    /**
422
     * Force the query to only return distinct results.
423
     *
424
     * @return $this
425
     */
426
    public function distinct()
427
    {
428
        $columns = func_get_args();
429
 
430
        if (count($columns) > 0) {
431
            $this->distinct = is_array($columns[0]) || is_bool($columns[0]) ? $columns[0] : $columns;
432
        } else {
433
            $this->distinct = true;
434
        }
435
 
436
        return $this;
437
    }
438
 
439
    /**
440
     * Set the table which the query is targeting.
441
     *
442
     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string  $table
443
     * @param  string|null  $as
444
     * @return $this
445
     */
446
    public function from($table, $as = null)
447
    {
448
        if ($this->isQueryable($table)) {
449
            return $this->fromSub($table, $as);
450
        }
451
 
452
        $this->from = $as ? "{$table} as {$as}" : $table;
453
 
454
        return $this;
455
    }
456
 
457
    /**
458
     * Add a join clause to the query.
459
     *
460
     * @param  string  $table
461
     * @param  \Closure|string  $first
462
     * @param  string|null  $operator
463
     * @param  string|null  $second
464
     * @param  string  $type
465
     * @param  bool  $where
466
     * @return $this
467
     */
468
    public function join($table, $first, $operator = null, $second = null, $type = 'inner', $where = false)
469
    {
470
        $join = $this->newJoinClause($this, $type, $table);
471
 
472
        // If the first "column" of the join is really a Closure instance the developer
473
        // is trying to build a join with a complex "on" clause containing more than
474
        // one condition, so we'll add the join and call a Closure with the query.
475
        if ($first instanceof Closure) {
476
            $first($join);
477
 
478
            $this->joins[] = $join;
479
 
480
            $this->addBinding($join->getBindings(), 'join');
481
        }
482
 
483
        // If the column is simply a string, we can assume the join simply has a basic
484
        // "on" clause with a single condition. So we will just build the join with
485
        // this simple join clauses attached to it. There is not a join callback.
486
        else {
487
            $method = $where ? 'where' : 'on';
488
 
489
            $this->joins[] = $join->$method($first, $operator, $second);
490
 
491
            $this->addBinding($join->getBindings(), 'join');
492
        }
493
 
494
        return $this;
495
    }
496
 
497
    /**
498
     * Add a "join where" clause to the query.
499
     *
500
     * @param  string  $table
501
     * @param  \Closure|string  $first
502
     * @param  string  $operator
503
     * @param  string  $second
504
     * @param  string  $type
505
     * @return $this
506
     */
507
    public function joinWhere($table, $first, $operator, $second, $type = 'inner')
508
    {
509
        return $this->join($table, $first, $operator, $second, $type, true);
510
    }
511
 
512
    /**
513
     * Add a subquery join clause to the query.
514
     *
515
     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string  $query
516
     * @param  string  $as
517
     * @param  \Closure|string  $first
518
     * @param  string|null  $operator
519
     * @param  string|null  $second
520
     * @param  string  $type
521
     * @param  bool  $where
522
     * @return $this
523
     *
524
     * @throws \InvalidArgumentException
525
     */
526
    public function joinSub($query, $as, $first, $operator = null, $second = null, $type = 'inner', $where = false)
527
    {
528
        [$query, $bindings] = $this->createSub($query);
529
 
530
        $expression = '('.$query.') as '.$this->grammar->wrapTable($as);
531
 
532
        $this->addBinding($bindings, 'join');
533
 
534
        return $this->join(new Expression($expression), $first, $operator, $second, $type, $where);
535
    }
536
 
537
    /**
538
     * Add a left join to the query.
539
     *
540
     * @param  string  $table
541
     * @param  \Closure|string  $first
542
     * @param  string|null  $operator
543
     * @param  string|null  $second
544
     * @return $this
545
     */
546
    public function leftJoin($table, $first, $operator = null, $second = null)
547
    {
548
        return $this->join($table, $first, $operator, $second, 'left');
549
    }
550
 
551
    /**
552
     * Add a "join where" clause to the query.
553
     *
554
     * @param  string  $table
555
     * @param  \Closure|string  $first
556
     * @param  string  $operator
557
     * @param  string  $second
558
     * @return $this
559
     */
560
    public function leftJoinWhere($table, $first, $operator, $second)
561
    {
562
        return $this->joinWhere($table, $first, $operator, $second, 'left');
563
    }
564
 
565
    /**
566
     * Add a subquery left join to the query.
567
     *
568
     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string  $query
569
     * @param  string  $as
570
     * @param  \Closure|string  $first
571
     * @param  string|null  $operator
572
     * @param  string|null  $second
573
     * @return $this
574
     */
575
    public function leftJoinSub($query, $as, $first, $operator = null, $second = null)
576
    {
577
        return $this->joinSub($query, $as, $first, $operator, $second, 'left');
578
    }
579
 
580
    /**
581
     * Add a right join to the query.
582
     *
583
     * @param  string  $table
584
     * @param  \Closure|string  $first
585
     * @param  string|null  $operator
586
     * @param  string|null  $second
587
     * @return $this
588
     */
589
    public function rightJoin($table, $first, $operator = null, $second = null)
590
    {
591
        return $this->join($table, $first, $operator, $second, 'right');
592
    }
593
 
594
    /**
595
     * Add a "right join where" clause to the query.
596
     *
597
     * @param  string  $table
598
     * @param  \Closure|string  $first
599
     * @param  string  $operator
600
     * @param  string  $second
601
     * @return $this
602
     */
603
    public function rightJoinWhere($table, $first, $operator, $second)
604
    {
605
        return $this->joinWhere($table, $first, $operator, $second, 'right');
606
    }
607
 
608
    /**
609
     * Add a subquery right join to the query.
610
     *
611
     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string  $query
612
     * @param  string  $as
613
     * @param  \Closure|string  $first
614
     * @param  string|null  $operator
615
     * @param  string|null  $second
616
     * @return $this
617
     */
618
    public function rightJoinSub($query, $as, $first, $operator = null, $second = null)
619
    {
620
        return $this->joinSub($query, $as, $first, $operator, $second, 'right');
621
    }
622
 
623
    /**
624
     * Add a "cross join" clause to the query.
625
     *
626
     * @param  string  $table
627
     * @param  \Closure|string|null  $first
628
     * @param  string|null  $operator
629
     * @param  string|null  $second
630
     * @return $this
631
     */
632
    public function crossJoin($table, $first = null, $operator = null, $second = null)
633
    {
634
        if ($first) {
635
            return $this->join($table, $first, $operator, $second, 'cross');
636
        }
637
 
638
        $this->joins[] = $this->newJoinClause($this, 'cross', $table);
639
 
640
        return $this;
641
    }
642
 
643
    /**
644
     * Add a subquery cross join to the query.
645
     *
646
     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string  $query
647
     * @param  string  $as
648
     * @return $this
649
     */
650
    public function crossJoinSub($query, $as)
651
    {
652
        [$query, $bindings] = $this->createSub($query);
653
 
654
        $expression = '('.$query.') as '.$this->grammar->wrapTable($as);
655
 
656
        $this->addBinding($bindings, 'join');
657
 
658
        $this->joins[] = $this->newJoinClause($this, 'cross', new Expression($expression));
659
 
660
        return $this;
661
    }
662
 
663
    /**
664
     * Get a new join clause.
665
     *
666
     * @param  \Illuminate\Database\Query\Builder  $parentQuery
667
     * @param  string  $type
668
     * @param  string  $table
669
     * @return \Illuminate\Database\Query\JoinClause
670
     */
671
    protected function newJoinClause(self $parentQuery, $type, $table)
672
    {
673
        return new JoinClause($parentQuery, $type, $table);
674
    }
675
 
676
    /**
677
     * Merge an array of where clauses and bindings.
678
     *
679
     * @param  array  $wheres
680
     * @param  array  $bindings
681
     * @return $this
682
     */
683
    public function mergeWheres($wheres, $bindings)
684
    {
685
        $this->wheres = array_merge($this->wheres, (array) $wheres);
686
 
687
        $this->bindings['where'] = array_values(
688
            array_merge($this->bindings['where'], (array) $bindings)
689
        );
690
 
691
        return $this;
692
    }
693
 
694
    /**
695
     * Add a basic where clause to the query.
696
     *
697
     * @param  \Closure|string|array  $column
698
     * @param  mixed  $operator
699
     * @param  mixed  $value
700
     * @param  string  $boolean
701
     * @return $this
702
     */
703
    public function where($column, $operator = null, $value = null, $boolean = 'and')
704
    {
705
        // If the column is an array, we will assume it is an array of key-value pairs
706
        // and can add them each as a where clause. We will maintain the boolean we
707
        // received when the method was called and pass it into the nested where.
708
        if (is_array($column)) {
709
            return $this->addArrayOfWheres($column, $boolean);
710
        }
711
 
712
        // Here we will make some assumptions about the operator. If only 2 values are
713
        // passed to the method, we will assume that the operator is an equals sign
714
        // and keep going. Otherwise, we'll require the operator to be passed in.
715
        [$value, $operator] = $this->prepareValueAndOperator(
716
            $value, $operator, func_num_args() === 2
717
        );
718
 
719
        // If the column is actually a Closure instance, we will assume the developer
720
        // wants to begin a nested where statement which is wrapped in parentheses.
721
        // We will add that Closure to the query and return back out immediately.
722
        if ($column instanceof Closure && is_null($operator)) {
723
            return $this->whereNested($column, $boolean);
724
        }
725
 
726
        // If the column is a Closure instance and there is an operator value, we will
727
        // assume the developer wants to run a subquery and then compare the result
728
        // of that subquery with the given value that was provided to the method.
729
        if ($this->isQueryable($column) && ! is_null($operator)) {
730
            [$sub, $bindings] = $this->createSub($column);
731
 
732
            return $this->addBinding($bindings, 'where')
733
                ->where(new Expression('('.$sub.')'), $operator, $value, $boolean);
734
        }
735
 
736
        // If the given operator is not found in the list of valid operators we will
737
        // assume that the developer is just short-cutting the '=' operators and
738
        // we will set the operators to '=' and set the values appropriately.
739
        if ($this->invalidOperator($operator)) {
740
            [$value, $operator] = [$operator, '='];
741
        }
742
 
743
        // If the value is a Closure, it means the developer is performing an entire
744
        // sub-select within the query and we will need to compile the sub-select
745
        // within the where clause to get the appropriate query record results.
746
        if ($value instanceof Closure) {
747
            return $this->whereSub($column, $operator, $value, $boolean);
748
        }
749
 
750
        // If the value is "null", we will just assume the developer wants to add a
751
        // where null clause to the query. So, we will allow a short-cut here to
752
        // that method for convenience so the developer doesn't have to check.
753
        if (is_null($value)) {
754
            return $this->whereNull($column, $boolean, $operator !== '=');
755
        }
756
 
757
        $type = 'Basic';
758
 
759
        // If the column is making a JSON reference we'll check to see if the value
760
        // is a boolean. If it is, we'll add the raw boolean string as an actual
761
        // value to the query to ensure this is properly handled by the query.
762
        if (str_contains($column, '->') && is_bool($value)) {
763
            $value = new Expression($value ? 'true' : 'false');
764
 
765
            if (is_string($column)) {
766
                $type = 'JsonBoolean';
767
            }
768
        }
769
 
770
        if ($this->isBitwiseOperator($operator)) {
771
            $type = 'Bitwise';
772
        }
773
 
774
        // Now that we are working with just a simple query we can put the elements
775
        // in our array and add the query binding to our array of bindings that
776
        // will be bound to each SQL statements when it is finally executed.
777
        $this->wheres[] = compact(
778
            'type', 'column', 'operator', 'value', 'boolean'
779
        );
780
 
781
        if (! $value instanceof Expression) {
782
            $this->addBinding($this->flattenValue($value), 'where');
783
        }
784
 
785
        return $this;
786
    }
787
 
788
    /**
789
     * Add an array of where clauses to the query.
790
     *
791
     * @param  array  $column
792
     * @param  string  $boolean
793
     * @param  string  $method
794
     * @return $this
795
     */
796
    protected function addArrayOfWheres($column, $boolean, $method = 'where')
797
    {
798
        return $this->whereNested(function ($query) use ($column, $method, $boolean) {
799
            foreach ($column as $key => $value) {
800
                if (is_numeric($key) && is_array($value)) {
801
                    $query->{$method}(...array_values($value));
802
                } else {
803
                    $query->{$method}($key, '=', $value, $boolean);
804
                }
805
            }
806
        }, $boolean);
807
    }
808
 
809
    /**
810
     * Prepare the value and operator for a where clause.
811
     *
812
     * @param  string  $value
813
     * @param  string  $operator
814
     * @param  bool  $useDefault
815
     * @return array
816
     *
817
     * @throws \InvalidArgumentException
818
     */
819
    public function prepareValueAndOperator($value, $operator, $useDefault = false)
820
    {
821
        if ($useDefault) {
822
            return [$operator, '='];
823
        } elseif ($this->invalidOperatorAndValue($operator, $value)) {
824
            throw new InvalidArgumentException('Illegal operator and value combination.');
825
        }
826
 
827
        return [$value, $operator];
828
    }
829
 
830
    /**
831
     * Determine if the given operator and value combination is legal.
832
     *
833
     * Prevents using Null values with invalid operators.
834
     *
835
     * @param  string  $operator
836
     * @param  mixed  $value
837
     * @return bool
838
     */
839
    protected function invalidOperatorAndValue($operator, $value)
840
    {
841
        return is_null($value) && in_array($operator, $this->operators) &&
842
             ! in_array($operator, ['=', '<>', '!=']);
843
    }
844
 
845
    /**
846
     * Determine if the given operator is supported.
847
     *
848
     * @param  string  $operator
849
     * @return bool
850
     */
851
    protected function invalidOperator($operator)
852
    {
853
        return ! is_string($operator) || (! in_array(strtolower($operator), $this->operators, true) &&
854
               ! in_array(strtolower($operator), $this->grammar->getOperators(), true));
855
    }
856
 
857
    /**
858
     * Determine if the operator is a bitwise operator.
859
     *
860
     * @param  string  $operator
861
     * @return bool
862
     */
863
    protected function isBitwiseOperator($operator)
864
    {
865
        return in_array(strtolower($operator), $this->bitwiseOperators, true) ||
866
               in_array(strtolower($operator), $this->grammar->getBitwiseOperators(), true);
867
    }
868
 
869
    /**
870
     * Add an "or where" clause to the query.
871
     *
872
     * @param  \Closure|string|array  $column
873
     * @param  mixed  $operator
874
     * @param  mixed  $value
875
     * @return $this
876
     */
877
    public function orWhere($column, $operator = null, $value = null)
878
    {
879
        [$value, $operator] = $this->prepareValueAndOperator(
880
            $value, $operator, func_num_args() === 2
881
        );
882
 
883
        return $this->where($column, $operator, $value, 'or');
884
    }
885
 
886
    /**
887
     * Add a basic "where not" clause to the query.
888
     *
889
     * @param  \Closure|string|array  $column
890
     * @param  mixed  $operator
891
     * @param  mixed  $value
892
     * @param  string  $boolean
893
     * @return $this
894
     */
895
    public function whereNot($column, $operator = null, $value = null, $boolean = 'and')
896
    {
897
        if (is_array($column)) {
898
            return $this->whereNested(function ($query) use ($column, $operator, $value, $boolean) {
899
                $query->where($column, $operator, $value, $boolean);
900
            }, $boolean.' not');
901
        }
902
 
903
        return $this->where($column, $operator, $value, $boolean.' not');
904
    }
905
 
906
    /**
907
     * Add an "or where not" clause to the query.
908
     *
909
     * @param  \Closure|string|array  $column
910
     * @param  mixed  $operator
911
     * @param  mixed  $value
912
     * @return $this
913
     */
914
    public function orWhereNot($column, $operator = null, $value = null)
915
    {
916
        return $this->whereNot($column, $operator, $value, 'or');
917
    }
918
 
919
    /**
920
     * Add a "where" clause comparing two columns to the query.
921
     *
922
     * @param  string|array  $first
923
     * @param  string|null  $operator
924
     * @param  string|null  $second
925
     * @param  string|null  $boolean
926
     * @return $this
927
     */
928
    public function whereColumn($first, $operator = null, $second = null, $boolean = 'and')
929
    {
930
        // If the column is an array, we will assume it is an array of key-value pairs
931
        // and can add them each as a where clause. We will maintain the boolean we
932
        // received when the method was called and pass it into the nested where.
933
        if (is_array($first)) {
934
            return $this->addArrayOfWheres($first, $boolean, 'whereColumn');
935
        }
936
 
937
        // If the given operator is not found in the list of valid operators we will
938
        // assume that the developer is just short-cutting the '=' operators and
939
        // we will set the operators to '=' and set the values appropriately.
940
        if ($this->invalidOperator($operator)) {
941
            [$second, $operator] = [$operator, '='];
942
        }
943
 
944
        // Finally, we will add this where clause into this array of clauses that we
945
        // are building for the query. All of them will be compiled via a grammar
946
        // once the query is about to be executed and run against the database.
947
        $type = 'Column';
948
 
949
        $this->wheres[] = compact(
950
            'type', 'first', 'operator', 'second', 'boolean'
951
        );
952
 
953
        return $this;
954
    }
955
 
956
    /**
957
     * Add an "or where" clause comparing two columns to the query.
958
     *
959
     * @param  string|array  $first
960
     * @param  string|null  $operator
961
     * @param  string|null  $second
962
     * @return $this
963
     */
964
    public function orWhereColumn($first, $operator = null, $second = null)
965
    {
966
        return $this->whereColumn($first, $operator, $second, 'or');
967
    }
968
 
969
    /**
970
     * Add a raw where clause to the query.
971
     *
972
     * @param  string  $sql
973
     * @param  mixed  $bindings
974
     * @param  string  $boolean
975
     * @return $this
976
     */
977
    public function whereRaw($sql, $bindings = [], $boolean = 'and')
978
    {
979
        $this->wheres[] = ['type' => 'raw', 'sql' => $sql, 'boolean' => $boolean];
980
 
981
        $this->addBinding((array) $bindings, 'where');
982
 
983
        return $this;
984
    }
985
 
986
    /**
987
     * Add a raw or where clause to the query.
988
     *
989
     * @param  string  $sql
990
     * @param  mixed  $bindings
991
     * @return $this
992
     */
993
    public function orWhereRaw($sql, $bindings = [])
994
    {
995
        return $this->whereRaw($sql, $bindings, 'or');
996
    }
997
 
998
    /**
999
     * Add a "where in" clause to the query.
1000
     *
1001
     * @param  string  $column
1002
     * @param  mixed  $values
1003
     * @param  string  $boolean
1004
     * @param  bool  $not
1005
     * @return $this
1006
     */
1007
    public function whereIn($column, $values, $boolean = 'and', $not = false)
1008
    {
1009
        $type = $not ? 'NotIn' : 'In';
1010
 
1011
        // If the value is a query builder instance we will assume the developer wants to
1012
        // look for any values that exist within this given query. So, we will add the
1013
        // query accordingly so that this query is properly executed when it is run.
1014
        if ($this->isQueryable($values)) {
1015
            [$query, $bindings] = $this->createSub($values);
1016
 
1017
            $values = [new Expression($query)];
1018
 
1019
            $this->addBinding($bindings, 'where');
1020
        }
1021
 
1022
        // Next, if the value is Arrayable we need to cast it to its raw array form so we
1023
        // have the underlying array value instead of an Arrayable object which is not
1024
        // able to be added as a binding, etc. We will then add to the wheres array.
1025
        if ($values instanceof Arrayable) {
1026
            $values = $values->toArray();
1027
        }
1028
 
1029
        $this->wheres[] = compact('type', 'column', 'values', 'boolean');
1030
 
1031
        if (count($values) !== count(Arr::flatten($values, 1))) {
1032
            throw new InvalidArgumentException('Nested arrays may not be passed to whereIn method.');
1033
        }
1034
 
1035
        // Finally, we'll add a binding for each value unless that value is an expression
1036
        // in which case we will just skip over it since it will be the query as a raw
1037
        // string and not as a parameterized place-holder to be replaced by the PDO.
1038
        $this->addBinding($this->cleanBindings($values), 'where');
1039
 
1040
        return $this;
1041
    }
1042
 
1043
    /**
1044
     * Add an "or where in" clause to the query.
1045
     *
1046
     * @param  string  $column
1047
     * @param  mixed  $values
1048
     * @return $this
1049
     */
1050
    public function orWhereIn($column, $values)
1051
    {
1052
        return $this->whereIn($column, $values, 'or');
1053
    }
1054
 
1055
    /**
1056
     * Add a "where not in" clause to the query.
1057
     *
1058
     * @param  string  $column
1059
     * @param  mixed  $values
1060
     * @param  string  $boolean
1061
     * @return $this
1062
     */
1063
    public function whereNotIn($column, $values, $boolean = 'and')
1064
    {
1065
        return $this->whereIn($column, $values, $boolean, true);
1066
    }
1067
 
1068
    /**
1069
     * Add an "or where not in" clause to the query.
1070
     *
1071
     * @param  string  $column
1072
     * @param  mixed  $values
1073
     * @return $this
1074
     */
1075
    public function orWhereNotIn($column, $values)
1076
    {
1077
        return $this->whereNotIn($column, $values, 'or');
1078
    }
1079
 
1080
    /**
1081
     * Add a "where in raw" clause for integer values to the query.
1082
     *
1083
     * @param  string  $column
1084
     * @param  \Illuminate\Contracts\Support\Arrayable|array  $values
1085
     * @param  string  $boolean
1086
     * @param  bool  $not
1087
     * @return $this
1088
     */
1089
    public function whereIntegerInRaw($column, $values, $boolean = 'and', $not = false)
1090
    {
1091
        $type = $not ? 'NotInRaw' : 'InRaw';
1092
 
1093
        if ($values instanceof Arrayable) {
1094
            $values = $values->toArray();
1095
        }
1096
 
1097
        $values = Arr::flatten($values);
1098
 
1099
        foreach ($values as &$value) {
1100
            $value = (int) $value;
1101
        }
1102
 
1103
        $this->wheres[] = compact('type', 'column', 'values', 'boolean');
1104
 
1105
        return $this;
1106
    }
1107
 
1108
    /**
1109
     * Add an "or where in raw" clause for integer values to the query.
1110
     *
1111
     * @param  string  $column
1112
     * @param  \Illuminate\Contracts\Support\Arrayable|array  $values
1113
     * @return $this
1114
     */
1115
    public function orWhereIntegerInRaw($column, $values)
1116
    {
1117
        return $this->whereIntegerInRaw($column, $values, 'or');
1118
    }
1119
 
1120
    /**
1121
     * Add a "where not in raw" clause for integer values to the query.
1122
     *
1123
     * @param  string  $column
1124
     * @param  \Illuminate\Contracts\Support\Arrayable|array  $values
1125
     * @param  string  $boolean
1126
     * @return $this
1127
     */
1128
    public function whereIntegerNotInRaw($column, $values, $boolean = 'and')
1129
    {
1130
        return $this->whereIntegerInRaw($column, $values, $boolean, true);
1131
    }
1132
 
1133
    /**
1134
     * Add an "or where not in raw" clause for integer values to the query.
1135
     *
1136
     * @param  string  $column
1137
     * @param  \Illuminate\Contracts\Support\Arrayable|array  $values
1138
     * @return $this
1139
     */
1140
    public function orWhereIntegerNotInRaw($column, $values)
1141
    {
1142
        return $this->whereIntegerNotInRaw($column, $values, 'or');
1143
    }
1144
 
1145
    /**
1146
     * Add a "where null" clause to the query.
1147
     *
1148
     * @param  string|array  $columns
1149
     * @param  string  $boolean
1150
     * @param  bool  $not
1151
     * @return $this
1152
     */
1153
    public function whereNull($columns, $boolean = 'and', $not = false)
1154
    {
1155
        $type = $not ? 'NotNull' : 'Null';
1156
 
1157
        foreach (Arr::wrap($columns) as $column) {
1158
            $this->wheres[] = compact('type', 'column', 'boolean');
1159
        }
1160
 
1161
        return $this;
1162
    }
1163
 
1164
    /**
1165
     * Add an "or where null" clause to the query.
1166
     *
1167
     * @param  string|array  $column
1168
     * @return $this
1169
     */
1170
    public function orWhereNull($column)
1171
    {
1172
        return $this->whereNull($column, 'or');
1173
    }
1174
 
1175
    /**
1176
     * Add a "where not null" clause to the query.
1177
     *
1178
     * @param  string|array  $columns
1179
     * @param  string  $boolean
1180
     * @return $this
1181
     */
1182
    public function whereNotNull($columns, $boolean = 'and')
1183
    {
1184
        return $this->whereNull($columns, $boolean, true);
1185
    }
1186
 
1187
    /**
1188
     * Add a where between statement to the query.
1189
     *
1190
     * @param  string|\Illuminate\Database\Query\Expression  $column
1191
     * @param  iterable  $values
1192
     * @param  string  $boolean
1193
     * @param  bool  $not
1194
     * @return $this
1195
     */
1196
    public function whereBetween($column, iterable $values, $boolean = 'and', $not = false)
1197
    {
1198
        $type = 'between';
1199
 
1200
        if ($values instanceof CarbonPeriod) {
1201
            $values = $values->toArray();
1202
        }
1203
 
1204
        $this->wheres[] = compact('type', 'column', 'values', 'boolean', 'not');
1205
 
1206
        $this->addBinding(array_slice($this->cleanBindings(Arr::flatten($values)), 0, 2), 'where');
1207
 
1208
        return $this;
1209
    }
1210
 
1211
    /**
1212
     * Add a where between statement using columns to the query.
1213
     *
1214
     * @param  string  $column
1215
     * @param  array  $values
1216
     * @param  string  $boolean
1217
     * @param  bool  $not
1218
     * @return $this
1219
     */
1220
    public function whereBetweenColumns($column, array $values, $boolean = 'and', $not = false)
1221
    {
1222
        $type = 'betweenColumns';
1223
 
1224
        $this->wheres[] = compact('type', 'column', 'values', 'boolean', 'not');
1225
 
1226
        return $this;
1227
    }
1228
 
1229
    /**
1230
     * Add an or where between statement to the query.
1231
     *
1232
     * @param  string  $column
1233
     * @param  iterable  $values
1234
     * @return $this
1235
     */
1236
    public function orWhereBetween($column, iterable $values)
1237
    {
1238
        return $this->whereBetween($column, $values, 'or');
1239
    }
1240
 
1241
    /**
1242
     * Add an or where between statement using columns to the query.
1243
     *
1244
     * @param  string  $column
1245
     * @param  array  $values
1246
     * @return $this
1247
     */
1248
    public function orWhereBetweenColumns($column, array $values)
1249
    {
1250
        return $this->whereBetweenColumns($column, $values, 'or');
1251
    }
1252
 
1253
    /**
1254
     * Add a where not between statement to the query.
1255
     *
1256
     * @param  string  $column
1257
     * @param  iterable  $values
1258
     * @param  string  $boolean
1259
     * @return $this
1260
     */
1261
    public function whereNotBetween($column, iterable $values, $boolean = 'and')
1262
    {
1263
        return $this->whereBetween($column, $values, $boolean, true);
1264
    }
1265
 
1266
    /**
1267
     * Add a where not between statement using columns to the query.
1268
     *
1269
     * @param  string  $column
1270
     * @param  array  $values
1271
     * @param  string  $boolean
1272
     * @return $this
1273
     */
1274
    public function whereNotBetweenColumns($column, array $values, $boolean = 'and')
1275
    {
1276
        return $this->whereBetweenColumns($column, $values, $boolean, true);
1277
    }
1278
 
1279
    /**
1280
     * Add an or where not between statement to the query.
1281
     *
1282
     * @param  string  $column
1283
     * @param  iterable  $values
1284
     * @return $this
1285
     */
1286
    public function orWhereNotBetween($column, iterable $values)
1287
    {
1288
        return $this->whereNotBetween($column, $values, 'or');
1289
    }
1290
 
1291
    /**
1292
     * Add an or where not between statement using columns to the query.
1293
     *
1294
     * @param  string  $column
1295
     * @param  array  $values
1296
     * @return $this
1297
     */
1298
    public function orWhereNotBetweenColumns($column, array $values)
1299
    {
1300
        return $this->whereNotBetweenColumns($column, $values, 'or');
1301
    }
1302
 
1303
    /**
1304
     * Add an "or where not null" clause to the query.
1305
     *
1306
     * @param  string  $column
1307
     * @return $this
1308
     */
1309
    public function orWhereNotNull($column)
1310
    {
1311
        return $this->whereNotNull($column, 'or');
1312
    }
1313
 
1314
    /**
1315
     * Add a "where date" statement to the query.
1316
     *
1317
     * @param  string  $column
1318
     * @param  string  $operator
1319
     * @param  \DateTimeInterface|string|null  $value
1320
     * @param  string  $boolean
1321
     * @return $this
1322
     */
1323
    public function whereDate($column, $operator, $value = null, $boolean = 'and')
1324
    {
1325
        [$value, $operator] = $this->prepareValueAndOperator(
1326
            $value, $operator, func_num_args() === 2
1327
        );
1328
 
1329
        $value = $this->flattenValue($value);
1330
 
1331
        if ($value instanceof DateTimeInterface) {
1332
            $value = $value->format('Y-m-d');
1333
        }
1334
 
1335
        return $this->addDateBasedWhere('Date', $column, $operator, $value, $boolean);
1336
    }
1337
 
1338
    /**
1339
     * Add an "or where date" statement to the query.
1340
     *
1341
     * @param  string  $column
1342
     * @param  string  $operator
1343
     * @param  \DateTimeInterface|string|null  $value
1344
     * @return $this
1345
     */
1346
    public function orWhereDate($column, $operator, $value = null)
1347
    {
1348
        [$value, $operator] = $this->prepareValueAndOperator(
1349
            $value, $operator, func_num_args() === 2
1350
        );
1351
 
1352
        return $this->whereDate($column, $operator, $value, 'or');
1353
    }
1354
 
1355
    /**
1356
     * Add a "where time" statement to the query.
1357
     *
1358
     * @param  string  $column
1359
     * @param  string  $operator
1360
     * @param  \DateTimeInterface|string|null  $value
1361
     * @param  string  $boolean
1362
     * @return $this
1363
     */
1364
    public function whereTime($column, $operator, $value = null, $boolean = 'and')
1365
    {
1366
        [$value, $operator] = $this->prepareValueAndOperator(
1367
            $value, $operator, func_num_args() === 2
1368
        );
1369
 
1370
        $value = $this->flattenValue($value);
1371
 
1372
        if ($value instanceof DateTimeInterface) {
1373
            $value = $value->format('H:i:s');
1374
        }
1375
 
1376
        return $this->addDateBasedWhere('Time', $column, $operator, $value, $boolean);
1377
    }
1378
 
1379
    /**
1380
     * Add an "or where time" statement to the query.
1381
     *
1382
     * @param  string  $column
1383
     * @param  string  $operator
1384
     * @param  \DateTimeInterface|string|null  $value
1385
     * @return $this
1386
     */
1387
    public function orWhereTime($column, $operator, $value = null)
1388
    {
1389
        [$value, $operator] = $this->prepareValueAndOperator(
1390
            $value, $operator, func_num_args() === 2
1391
        );
1392
 
1393
        return $this->whereTime($column, $operator, $value, 'or');
1394
    }
1395
 
1396
    /**
1397
     * Add a "where day" statement to the query.
1398
     *
1399
     * @param  string  $column
1400
     * @param  string  $operator
1401
     * @param  \DateTimeInterface|string|int|null  $value
1402
     * @param  string  $boolean
1403
     * @return $this
1404
     */
1405
    public function whereDay($column, $operator, $value = null, $boolean = 'and')
1406
    {
1407
        [$value, $operator] = $this->prepareValueAndOperator(
1408
            $value, $operator, func_num_args() === 2
1409
        );
1410
 
1411
        $value = $this->flattenValue($value);
1412
 
1413
        if ($value instanceof DateTimeInterface) {
1414
            $value = $value->format('d');
1415
        }
1416
 
1417
        if (! $value instanceof Expression) {
1418
            $value = sprintf('%02d', $value);
1419
        }
1420
 
1421
        return $this->addDateBasedWhere('Day', $column, $operator, $value, $boolean);
1422
    }
1423
 
1424
    /**
1425
     * Add an "or where day" statement to the query.
1426
     *
1427
     * @param  string  $column
1428
     * @param  string  $operator
1429
     * @param  \DateTimeInterface|string|int|null  $value
1430
     * @return $this
1431
     */
1432
    public function orWhereDay($column, $operator, $value = null)
1433
    {
1434
        [$value, $operator] = $this->prepareValueAndOperator(
1435
            $value, $operator, func_num_args() === 2
1436
        );
1437
 
1438
        return $this->whereDay($column, $operator, $value, 'or');
1439
    }
1440
 
1441
    /**
1442
     * Add a "where month" statement to the query.
1443
     *
1444
     * @param  string  $column
1445
     * @param  string  $operator
1446
     * @param  \DateTimeInterface|string|int|null  $value
1447
     * @param  string  $boolean
1448
     * @return $this
1449
     */
1450
    public function whereMonth($column, $operator, $value = null, $boolean = 'and')
1451
    {
1452
        [$value, $operator] = $this->prepareValueAndOperator(
1453
            $value, $operator, func_num_args() === 2
1454
        );
1455
 
1456
        $value = $this->flattenValue($value);
1457
 
1458
        if ($value instanceof DateTimeInterface) {
1459
            $value = $value->format('m');
1460
        }
1461
 
1462
        if (! $value instanceof Expression) {
1463
            $value = sprintf('%02d', $value);
1464
        }
1465
 
1466
        return $this->addDateBasedWhere('Month', $column, $operator, $value, $boolean);
1467
    }
1468
 
1469
    /**
1470
     * Add an "or where month" statement to the query.
1471
     *
1472
     * @param  string  $column
1473
     * @param  string  $operator
1474
     * @param  \DateTimeInterface|string|int|null  $value
1475
     * @return $this
1476
     */
1477
    public function orWhereMonth($column, $operator, $value = null)
1478
    {
1479
        [$value, $operator] = $this->prepareValueAndOperator(
1480
            $value, $operator, func_num_args() === 2
1481
        );
1482
 
1483
        return $this->whereMonth($column, $operator, $value, 'or');
1484
    }
1485
 
1486
    /**
1487
     * Add a "where year" statement to the query.
1488
     *
1489
     * @param  string  $column
1490
     * @param  string  $operator
1491
     * @param  \DateTimeInterface|string|int|null  $value
1492
     * @param  string  $boolean
1493
     * @return $this
1494
     */
1495
    public function whereYear($column, $operator, $value = null, $boolean = 'and')
1496
    {
1497
        [$value, $operator] = $this->prepareValueAndOperator(
1498
            $value, $operator, func_num_args() === 2
1499
        );
1500
 
1501
        $value = $this->flattenValue($value);
1502
 
1503
        if ($value instanceof DateTimeInterface) {
1504
            $value = $value->format('Y');
1505
        }
1506
 
1507
        return $this->addDateBasedWhere('Year', $column, $operator, $value, $boolean);
1508
    }
1509
 
1510
    /**
1511
     * Add an "or where year" statement to the query.
1512
     *
1513
     * @param  string  $column
1514
     * @param  string  $operator
1515
     * @param  \DateTimeInterface|string|int|null  $value
1516
     * @return $this
1517
     */
1518
    public function orWhereYear($column, $operator, $value = null)
1519
    {
1520
        [$value, $operator] = $this->prepareValueAndOperator(
1521
            $value, $operator, func_num_args() === 2
1522
        );
1523
 
1524
        return $this->whereYear($column, $operator, $value, 'or');
1525
    }
1526
 
1527
    /**
1528
     * Add a date based (year, month, day, time) statement to the query.
1529
     *
1530
     * @param  string  $type
1531
     * @param  string  $column
1532
     * @param  string  $operator
1533
     * @param  mixed  $value
1534
     * @param  string  $boolean
1535
     * @return $this
1536
     */
1537
    protected function addDateBasedWhere($type, $column, $operator, $value, $boolean = 'and')
1538
    {
1539
        $this->wheres[] = compact('column', 'type', 'boolean', 'operator', 'value');
1540
 
1541
        if (! $value instanceof Expression) {
1542
            $this->addBinding($value, 'where');
1543
        }
1544
 
1545
        return $this;
1546
    }
1547
 
1548
    /**
1549
     * Add a nested where statement to the query.
1550
     *
1551
     * @param  \Closure  $callback
1552
     * @param  string  $boolean
1553
     * @return $this
1554
     */
1555
    public function whereNested(Closure $callback, $boolean = 'and')
1556
    {
1557
        $callback($query = $this->forNestedWhere());
1558
 
1559
        return $this->addNestedWhereQuery($query, $boolean);
1560
    }
1561
 
1562
    /**
1563
     * Create a new query instance for nested where condition.
1564
     *
1565
     * @return \Illuminate\Database\Query\Builder
1566
     */
1567
    public function forNestedWhere()
1568
    {
1569
        return $this->newQuery()->from($this->from);
1570
    }
1571
 
1572
    /**
1573
     * Add another query builder as a nested where to the query builder.
1574
     *
1575
     * @param  \Illuminate\Database\Query\Builder  $query
1576
     * @param  string  $boolean
1577
     * @return $this
1578
     */
1579
    public function addNestedWhereQuery($query, $boolean = 'and')
1580
    {
1581
        if (count($query->wheres)) {
1582
            $type = 'Nested';
1583
 
1584
            $this->wheres[] = compact('type', 'query', 'boolean');
1585
 
1586
            $this->addBinding($query->getRawBindings()['where'], 'where');
1587
        }
1588
 
1589
        return $this;
1590
    }
1591
 
1592
    /**
1593
     * Add a full sub-select to the query.
1594
     *
1595
     * @param  string  $column
1596
     * @param  string  $operator
1597
     * @param  \Closure  $callback
1598
     * @param  string  $boolean
1599
     * @return $this
1600
     */
1601
    protected function whereSub($column, $operator, Closure $callback, $boolean)
1602
    {
1603
        $type = 'Sub';
1604
 
1605
        // Once we have the query instance we can simply execute it so it can add all
1606
        // of the sub-select's conditions to itself, and then we can cache it off
1607
        // in the array of where clauses for the "main" parent query instance.
1608
        $callback($query = $this->forSubQuery());
1609
 
1610
        $this->wheres[] = compact(
1611
            'type', 'column', 'operator', 'query', 'boolean'
1612
        );
1613
 
1614
        $this->addBinding($query->getBindings(), 'where');
1615
 
1616
        return $this;
1617
    }
1618
 
1619
    /**
1620
     * Add an exists clause to the query.
1621
     *
1622
     * @param  \Closure  $callback
1623
     * @param  string  $boolean
1624
     * @param  bool  $not
1625
     * @return $this
1626
     */
1627
    public function whereExists(Closure $callback, $boolean = 'and', $not = false)
1628
    {
1629
        $query = $this->forSubQuery();
1630
 
1631
        // Similar to the sub-select clause, we will create a new query instance so
1632
        // the developer may cleanly specify the entire exists query and we will
1633
        // compile the whole thing in the grammar and insert it into the SQL.
1634
        $callback($query);
1635
 
1636
        return $this->addWhereExistsQuery($query, $boolean, $not);
1637
    }
1638
 
1639
    /**
1640
     * Add an or exists clause to the query.
1641
     *
1642
     * @param  \Closure  $callback
1643
     * @param  bool  $not
1644
     * @return $this
1645
     */
1646
    public function orWhereExists(Closure $callback, $not = false)
1647
    {
1648
        return $this->whereExists($callback, 'or', $not);
1649
    }
1650
 
1651
    /**
1652
     * Add a where not exists clause to the query.
1653
     *
1654
     * @param  \Closure  $callback
1655
     * @param  string  $boolean
1656
     * @return $this
1657
     */
1658
    public function whereNotExists(Closure $callback, $boolean = 'and')
1659
    {
1660
        return $this->whereExists($callback, $boolean, true);
1661
    }
1662
 
1663
    /**
1664
     * Add a where not exists clause to the query.
1665
     *
1666
     * @param  \Closure  $callback
1667
     * @return $this
1668
     */
1669
    public function orWhereNotExists(Closure $callback)
1670
    {
1671
        return $this->orWhereExists($callback, true);
1672
    }
1673
 
1674
    /**
1675
     * Add an exists clause to the query.
1676
     *
1677
     * @param  \Illuminate\Database\Query\Builder  $query
1678
     * @param  string  $boolean
1679
     * @param  bool  $not
1680
     * @return $this
1681
     */
1682
    public function addWhereExistsQuery(self $query, $boolean = 'and', $not = false)
1683
    {
1684
        $type = $not ? 'NotExists' : 'Exists';
1685
 
1686
        $this->wheres[] = compact('type', 'query', 'boolean');
1687
 
1688
        $this->addBinding($query->getBindings(), 'where');
1689
 
1690
        return $this;
1691
    }
1692
 
1693
    /**
1694
     * Adds a where condition using row values.
1695
     *
1696
     * @param  array  $columns
1697
     * @param  string  $operator
1698
     * @param  array  $values
1699
     * @param  string  $boolean
1700
     * @return $this
1701
     *
1702
     * @throws \InvalidArgumentException
1703
     */
1704
    public function whereRowValues($columns, $operator, $values, $boolean = 'and')
1705
    {
1706
        if (count($columns) !== count($values)) {
1707
            throw new InvalidArgumentException('The number of columns must match the number of values');
1708
        }
1709
 
1710
        $type = 'RowValues';
1711
 
1712
        $this->wheres[] = compact('type', 'columns', 'operator', 'values', 'boolean');
1713
 
1714
        $this->addBinding($this->cleanBindings($values));
1715
 
1716
        return $this;
1717
    }
1718
 
1719
    /**
1720
     * Adds an or where condition using row values.
1721
     *
1722
     * @param  array  $columns
1723
     * @param  string  $operator
1724
     * @param  array  $values
1725
     * @return $this
1726
     */
1727
    public function orWhereRowValues($columns, $operator, $values)
1728
    {
1729
        return $this->whereRowValues($columns, $operator, $values, 'or');
1730
    }
1731
 
1732
    /**
1733
     * Add a "where JSON contains" clause to the query.
1734
     *
1735
     * @param  string  $column
1736
     * @param  mixed  $value
1737
     * @param  string  $boolean
1738
     * @param  bool  $not
1739
     * @return $this
1740
     */
1741
    public function whereJsonContains($column, $value, $boolean = 'and', $not = false)
1742
    {
1743
        $type = 'JsonContains';
1744
 
1745
        $this->wheres[] = compact('type', 'column', 'value', 'boolean', 'not');
1746
 
1747
        if (! $value instanceof Expression) {
1748
            $this->addBinding($this->grammar->prepareBindingForJsonContains($value));
1749
        }
1750
 
1751
        return $this;
1752
    }
1753
 
1754
    /**
1755
     * Add an "or where JSON contains" clause to the query.
1756
     *
1757
     * @param  string  $column
1758
     * @param  mixed  $value
1759
     * @return $this
1760
     */
1761
    public function orWhereJsonContains($column, $value)
1762
    {
1763
        return $this->whereJsonContains($column, $value, 'or');
1764
    }
1765
 
1766
    /**
1767
     * Add a "where JSON not contains" clause to the query.
1768
     *
1769
     * @param  string  $column
1770
     * @param  mixed  $value
1771
     * @param  string  $boolean
1772
     * @return $this
1773
     */
1774
    public function whereJsonDoesntContain($column, $value, $boolean = 'and')
1775
    {
1776
        return $this->whereJsonContains($column, $value, $boolean, true);
1777
    }
1778
 
1779
    /**
1780
     * Add an "or where JSON not contains" clause to the query.
1781
     *
1782
     * @param  string  $column
1783
     * @param  mixed  $value
1784
     * @return $this
1785
     */
1786
    public function orWhereJsonDoesntContain($column, $value)
1787
    {
1788
        return $this->whereJsonDoesntContain($column, $value, 'or');
1789
    }
1790
 
1791
    /**
1792
     * Add a clause that determines if a JSON path exists to the query.
1793
     *
1794
     * @param  string  $column
1795
     * @param  string  $boolean
1796
     * @param  bool  $not
1797
     * @return $this
1798
     */
1799
    public function whereJsonContainsKey($column, $boolean = 'and', $not = false)
1800
    {
1801
        $type = 'JsonContainsKey';
1802
 
1803
        $this->wheres[] = compact('type', 'column', 'boolean', 'not');
1804
 
1805
        return $this;
1806
    }
1807
 
1808
    /**
1809
     * Add an "or" clause that determines if a JSON path exists to the query.
1810
     *
1811
     * @param  string  $column
1812
     * @return $this
1813
     */
1814
    public function orWhereJsonContainsKey($column)
1815
    {
1816
        return $this->whereJsonContainsKey($column, 'or');
1817
    }
1818
 
1819
    /**
1820
     * Add a clause that determines if a JSON path does not exist to the query.
1821
     *
1822
     * @param  string  $column
1823
     * @param  string  $boolean
1824
     * @return $this
1825
     */
1826
    public function whereJsonDoesntContainKey($column, $boolean = 'and')
1827
    {
1828
        return $this->whereJsonContainsKey($column, $boolean, true);
1829
    }
1830
 
1831
    /**
1832
     * Add an "or" clause that determines if a JSON path does not exist to the query.
1833
     *
1834
     * @param  string  $column
1835
     * @return $this
1836
     */
1837
    public function orWhereJsonDoesntContainKey($column)
1838
    {
1839
        return $this->whereJsonDoesntContainKey($column, 'or');
1840
    }
1841
 
1842
    /**
1843
     * Add a "where JSON length" clause to the query.
1844
     *
1845
     * @param  string  $column
1846
     * @param  mixed  $operator
1847
     * @param  mixed  $value
1848
     * @param  string  $boolean
1849
     * @return $this
1850
     */
1851
    public function whereJsonLength($column, $operator, $value = null, $boolean = 'and')
1852
    {
1853
        $type = 'JsonLength';
1854
 
1855
        [$value, $operator] = $this->prepareValueAndOperator(
1856
            $value, $operator, func_num_args() === 2
1857
        );
1858
 
1859
        $this->wheres[] = compact('type', 'column', 'operator', 'value', 'boolean');
1860
 
1861
        if (! $value instanceof Expression) {
1862
            $this->addBinding((int) $this->flattenValue($value));
1863
        }
1864
 
1865
        return $this;
1866
    }
1867
 
1868
    /**
1869
     * Add an "or where JSON length" clause to the query.
1870
     *
1871
     * @param  string  $column
1872
     * @param  mixed  $operator
1873
     * @param  mixed  $value
1874
     * @return $this
1875
     */
1876
    public function orWhereJsonLength($column, $operator, $value = null)
1877
    {
1878
        [$value, $operator] = $this->prepareValueAndOperator(
1879
            $value, $operator, func_num_args() === 2
1880
        );
1881
 
1882
        return $this->whereJsonLength($column, $operator, $value, 'or');
1883
    }
1884
 
1885
    /**
1886
     * Handles dynamic "where" clauses to the query.
1887
     *
1888
     * @param  string  $method
1889
     * @param  array  $parameters
1890
     * @return $this
1891
     */
1892
    public function dynamicWhere($method, $parameters)
1893
    {
1894
        $finder = substr($method, 5);
1895
 
1896
        $segments = preg_split(
1897
            '/(And|Or)(?=[A-Z])/', $finder, -1, PREG_SPLIT_DELIM_CAPTURE
1898
        );
1899
 
1900
        // The connector variable will determine which connector will be used for the
1901
        // query condition. We will change it as we come across new boolean values
1902
        // in the dynamic method strings, which could contain a number of these.
1903
        $connector = 'and';
1904
 
1905
        $index = 0;
1906
 
1907
        foreach ($segments as $segment) {
1908
            // If the segment is not a boolean connector, we can assume it is a column's name
1909
            // and we will add it to the query as a new constraint as a where clause, then
1910
            // we can keep iterating through the dynamic method string's segments again.
1911
            if ($segment !== 'And' && $segment !== 'Or') {
1912
                $this->addDynamic($segment, $connector, $parameters, $index);
1913
 
1914
                $index++;
1915
            }
1916
 
1917
            // Otherwise, we will store the connector so we know how the next where clause we
1918
            // find in the query should be connected to the previous ones, meaning we will
1919
            // have the proper boolean connector to connect the next where clause found.
1920
            else {
1921
                $connector = $segment;
1922
            }
1923
        }
1924
 
1925
        return $this;
1926
    }
1927
 
1928
    /**
1929
     * Add a single dynamic where clause statement to the query.
1930
     *
1931
     * @param  string  $segment
1932
     * @param  string  $connector
1933
     * @param  array  $parameters
1934
     * @param  int  $index
1935
     * @return void
1936
     */
1937
    protected function addDynamic($segment, $connector, $parameters, $index)
1938
    {
1939
        // Once we have parsed out the columns and formatted the boolean operators we
1940
        // are ready to add it to this query as a where clause just like any other
1941
        // clause on the query. Then we'll increment the parameter index values.
1942
        $bool = strtolower($connector);
1943
 
1944
        $this->where(Str::snake($segment), '=', $parameters[$index], $bool);
1945
    }
1946
 
1947
    /**
1948
     * Add a "where fulltext" clause to the query.
1949
     *
1950
     * @param  string|string[]  $columns
1951
     * @param  string  $value
1952
     * @param  string  $boolean
1953
     * @return $this
1954
     */
1955
    public function whereFullText($columns, $value, array $options = [], $boolean = 'and')
1956
    {
1957
        $type = 'Fulltext';
1958
 
1959
        $columns = (array) $columns;
1960
 
1961
        $this->wheres[] = compact('type', 'columns', 'value', 'options', 'boolean');
1962
 
1963
        $this->addBinding($value);
1964
 
1965
        return $this;
1966
    }
1967
 
1968
    /**
1969
     * Add a "or where fulltext" clause to the query.
1970
     *
1971
     * @param  string|string[]  $columns
1972
     * @param  string  $value
1973
     * @return $this
1974
     */
1975
    public function orWhereFullText($columns, $value, array $options = [])
1976
    {
1977
        return $this->whereFulltext($columns, $value, $options, 'or');
1978
    }
1979
 
1980
    /**
1981
     * Add a "group by" clause to the query.
1982
     *
1983
     * @param  array|string  ...$groups
1984
     * @return $this
1985
     */
1986
    public function groupBy(...$groups)
1987
    {
1988
        foreach ($groups as $group) {
1989
            $this->groups = array_merge(
1990
                (array) $this->groups,
1991
                Arr::wrap($group)
1992
            );
1993
        }
1994
 
1995
        return $this;
1996
    }
1997
 
1998
    /**
1999
     * Add a raw groupBy clause to the query.
2000
     *
2001
     * @param  string  $sql
2002
     * @param  array  $bindings
2003
     * @return $this
2004
     */
2005
    public function groupByRaw($sql, array $bindings = [])
2006
    {
2007
        $this->groups[] = new Expression($sql);
2008
 
2009
        $this->addBinding($bindings, 'groupBy');
2010
 
2011
        return $this;
2012
    }
2013
 
2014
    /**
2015
     * Add a "having" clause to the query.
2016
     *
2017
     * @param  \Closure|string  $column
2018
     * @param  string|int|float|null  $operator
2019
     * @param  string|int|float|null  $value
2020
     * @param  string  $boolean
2021
     * @return $this
2022
     */
2023
    public function having($column, $operator = null, $value = null, $boolean = 'and')
2024
    {
2025
        $type = 'Basic';
2026
 
2027
        // Here we will make some assumptions about the operator. If only 2 values are
2028
        // passed to the method, we will assume that the operator is an equals sign
2029
        // and keep going. Otherwise, we'll require the operator to be passed in.
2030
        [$value, $operator] = $this->prepareValueAndOperator(
2031
            $value, $operator, func_num_args() === 2
2032
        );
2033
 
2034
        if ($column instanceof Closure && is_null($operator)) {
2035
            return $this->havingNested($column, $boolean);
2036
        }
2037
 
2038
        // If the given operator is not found in the list of valid operators we will
2039
        // assume that the developer is just short-cutting the '=' operators and
2040
        // we will set the operators to '=' and set the values appropriately.
2041
        if ($this->invalidOperator($operator)) {
2042
            [$value, $operator] = [$operator, '='];
2043
        }
2044
 
2045
        if ($this->isBitwiseOperator($operator)) {
2046
            $type = 'Bitwise';
2047
        }
2048
 
2049
        $this->havings[] = compact('type', 'column', 'operator', 'value', 'boolean');
2050
 
2051
        if (! $value instanceof Expression) {
2052
            $this->addBinding($this->flattenValue($value), 'having');
2053
        }
2054
 
2055
        return $this;
2056
    }
2057
 
2058
    /**
2059
     * Add an "or having" clause to the query.
2060
     *
2061
     * @param  \Closure|string  $column
2062
     * @param  string|int|float|null  $operator
2063
     * @param  string|int|float|null  $value
2064
     * @return $this
2065
     */
2066
    public function orHaving($column, $operator = null, $value = null)
2067
    {
2068
        [$value, $operator] = $this->prepareValueAndOperator(
2069
            $value, $operator, func_num_args() === 2
2070
        );
2071
 
2072
        return $this->having($column, $operator, $value, 'or');
2073
    }
2074
 
2075
    /**
2076
     * Add a nested having statement to the query.
2077
     *
2078
     * @param  \Closure  $callback
2079
     * @param  string  $boolean
2080
     * @return $this
2081
     */
2082
    public function havingNested(Closure $callback, $boolean = 'and')
2083
    {
2084
        $callback($query = $this->forNestedWhere());
2085
 
2086
        return $this->addNestedHavingQuery($query, $boolean);
2087
    }
2088
 
2089
    /**
2090
     * Add another query builder as a nested having to the query builder.
2091
     *
2092
     * @param  \Illuminate\Database\Query\Builder  $query
2093
     * @param  string  $boolean
2094
     * @return $this
2095
     */
2096
    public function addNestedHavingQuery($query, $boolean = 'and')
2097
    {
2098
        if (count($query->havings)) {
2099
            $type = 'Nested';
2100
 
2101
            $this->havings[] = compact('type', 'query', 'boolean');
2102
 
2103
            $this->addBinding($query->getRawBindings()['having'], 'having');
2104
        }
2105
 
2106
        return $this;
2107
    }
2108
 
2109
    /**
2110
     * Add a "having null" clause to the query.
2111
     *
2112
     * @param  string|array  $columns
2113
     * @param  string  $boolean
2114
     * @param  bool  $not
2115
     * @return $this
2116
     */
2117
    public function havingNull($columns, $boolean = 'and', $not = false)
2118
    {
2119
        $type = $not ? 'NotNull' : 'Null';
2120
 
2121
        foreach (Arr::wrap($columns) as $column) {
2122
            $this->havings[] = compact('type', 'column', 'boolean');
2123
        }
2124
 
2125
        return $this;
2126
    }
2127
 
2128
    /**
2129
     * Add an "or having null" clause to the query.
2130
     *
2131
     * @param  string  $column
2132
     * @return $this
2133
     */
2134
    public function orHavingNull($column)
2135
    {
2136
        return $this->havingNull($column, 'or');
2137
    }
2138
 
2139
    /**
2140
     * Add a "having not null" clause to the query.
2141
     *
2142
     * @param  string|array  $columns
2143
     * @param  string  $boolean
2144
     * @return $this
2145
     */
2146
    public function havingNotNull($columns, $boolean = 'and')
2147
    {
2148
        return $this->havingNull($columns, $boolean, true);
2149
    }
2150
 
2151
    /**
2152
     * Add an "or having not null" clause to the query.
2153
     *
2154
     * @param  string  $column
2155
     * @return $this
2156
     */
2157
    public function orHavingNotNull($column)
2158
    {
2159
        return $this->havingNotNull($column, 'or');
2160
    }
2161
 
2162
    /**
2163
     * Add a "having between " clause to the query.
2164
     *
2165
     * @param  string  $column
2166
     * @param  array  $values
2167
     * @param  string  $boolean
2168
     * @param  bool  $not
2169
     * @return $this
2170
     */
2171
    public function havingBetween($column, array $values, $boolean = 'and', $not = false)
2172
    {
2173
        $type = 'between';
2174
 
2175
        $this->havings[] = compact('type', 'column', 'values', 'boolean', 'not');
2176
 
2177
        $this->addBinding(array_slice($this->cleanBindings(Arr::flatten($values)), 0, 2), 'having');
2178
 
2179
        return $this;
2180
    }
2181
 
2182
    /**
2183
     * Add a raw having clause to the query.
2184
     *
2185
     * @param  string  $sql
2186
     * @param  array  $bindings
2187
     * @param  string  $boolean
2188
     * @return $this
2189
     */
2190
    public function havingRaw($sql, array $bindings = [], $boolean = 'and')
2191
    {
2192
        $type = 'Raw';
2193
 
2194
        $this->havings[] = compact('type', 'sql', 'boolean');
2195
 
2196
        $this->addBinding($bindings, 'having');
2197
 
2198
        return $this;
2199
    }
2200
 
2201
    /**
2202
     * Add a raw or having clause to the query.
2203
     *
2204
     * @param  string  $sql
2205
     * @param  array  $bindings
2206
     * @return $this
2207
     */
2208
    public function orHavingRaw($sql, array $bindings = [])
2209
    {
2210
        return $this->havingRaw($sql, $bindings, 'or');
2211
    }
2212
 
2213
    /**
2214
     * Add an "order by" clause to the query.
2215
     *
2216
     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Expression|string  $column
2217
     * @param  string  $direction
2218
     * @return $this
2219
     *
2220
     * @throws \InvalidArgumentException
2221
     */
2222
    public function orderBy($column, $direction = 'asc')
2223
    {
2224
        if ($this->isQueryable($column)) {
2225
            [$query, $bindings] = $this->createSub($column);
2226
 
2227
            $column = new Expression('('.$query.')');
2228
 
2229
            $this->addBinding($bindings, $this->unions ? 'unionOrder' : 'order');
2230
        }
2231
 
2232
        $direction = strtolower($direction);
2233
 
2234
        if (! in_array($direction, ['asc', 'desc'], true)) {
2235
            throw new InvalidArgumentException('Order direction must be "asc" or "desc".');
2236
        }
2237
 
2238
        $this->{$this->unions ? 'unionOrders' : 'orders'}[] = [
2239
            'column' => $column,
2240
            'direction' => $direction,
2241
        ];
2242
 
2243
        return $this;
2244
    }
2245
 
2246
    /**
2247
     * Add a descending "order by" clause to the query.
2248
     *
2249
     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Expression|string  $column
2250
     * @return $this
2251
     */
2252
    public function orderByDesc($column)
2253
    {
2254
        return $this->orderBy($column, 'desc');
2255
    }
2256
 
2257
    /**
2258
     * Add an "order by" clause for a timestamp to the query.
2259
     *
2260
     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Query\Expression|string  $column
2261
     * @return $this
2262
     */
2263
    public function latest($column = 'created_at')
2264
    {
2265
        return $this->orderBy($column, 'desc');
2266
    }
2267
 
2268
    /**
2269
     * Add an "order by" clause for a timestamp to the query.
2270
     *
2271
     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Query\Expression|string  $column
2272
     * @return $this
2273
     */
2274
    public function oldest($column = 'created_at')
2275
    {
2276
        return $this->orderBy($column, 'asc');
2277
    }
2278
 
2279
    /**
2280
     * Put the query's results in random order.
2281
     *
2282
     * @param  string|int  $seed
2283
     * @return $this
2284
     */
2285
    public function inRandomOrder($seed = '')
2286
    {
2287
        return $this->orderByRaw($this->grammar->compileRandom($seed));
2288
    }
2289
 
2290
    /**
2291
     * Add a raw "order by" clause to the query.
2292
     *
2293
     * @param  string  $sql
2294
     * @param  array  $bindings
2295
     * @return $this
2296
     */
2297
    public function orderByRaw($sql, $bindings = [])
2298
    {
2299
        $type = 'Raw';
2300
 
2301
        $this->{$this->unions ? 'unionOrders' : 'orders'}[] = compact('type', 'sql');
2302
 
2303
        $this->addBinding($bindings, $this->unions ? 'unionOrder' : 'order');
2304
 
2305
        return $this;
2306
    }
2307
 
2308
    /**
2309
     * Alias to set the "offset" value of the query.
2310
     *
2311
     * @param  int  $value
2312
     * @return $this
2313
     */
2314
    public function skip($value)
2315
    {
2316
        return $this->offset($value);
2317
    }
2318
 
2319
    /**
2320
     * Set the "offset" value of the query.
2321
     *
2322
     * @param  int  $value
2323
     * @return $this
2324
     */
2325
    public function offset($value)
2326
    {
2327
        $property = $this->unions ? 'unionOffset' : 'offset';
2328
 
2329
        $this->$property = max(0, (int) $value);
2330
 
2331
        return $this;
2332
    }
2333
 
2334
    /**
2335
     * Alias to set the "limit" value of the query.
2336
     *
2337
     * @param  int  $value
2338
     * @return $this
2339
     */
2340
    public function take($value)
2341
    {
2342
        return $this->limit($value);
2343
    }
2344
 
2345
    /**
2346
     * Set the "limit" value of the query.
2347
     *
2348
     * @param  int  $value
2349
     * @return $this
2350
     */
2351
    public function limit($value)
2352
    {
2353
        $property = $this->unions ? 'unionLimit' : 'limit';
2354
 
2355
        if ($value >= 0) {
2356
            $this->$property = ! is_null($value) ? (int) $value : null;
2357
        }
2358
 
2359
        return $this;
2360
    }
2361
 
2362
    /**
2363
     * Set the limit and offset for a given page.
2364
     *
2365
     * @param  int  $page
2366
     * @param  int  $perPage
2367
     * @return $this
2368
     */
2369
    public function forPage($page, $perPage = 15)
2370
    {
2371
        return $this->offset(($page - 1) * $perPage)->limit($perPage);
2372
    }
2373
 
2374
    /**
2375
     * Constrain the query to the previous "page" of results before a given ID.
2376
     *
2377
     * @param  int  $perPage
2378
     * @param  int|null  $lastId
2379
     * @param  string  $column
2380
     * @return $this
2381
     */
2382
    public function forPageBeforeId($perPage = 15, $lastId = 0, $column = 'id')
2383
    {
2384
        $this->orders = $this->removeExistingOrdersFor($column);
2385
 
2386
        if (! is_null($lastId)) {
2387
            $this->where($column, '<', $lastId);
2388
        }
2389
 
2390
        return $this->orderBy($column, 'desc')
2391
                    ->limit($perPage);
2392
    }
2393
 
2394
    /**
2395
     * Constrain the query to the next "page" of results after a given ID.
2396
     *
2397
     * @param  int  $perPage
2398
     * @param  int|null  $lastId
2399
     * @param  string  $column
2400
     * @return $this
2401
     */
2402
    public function forPageAfterId($perPage = 15, $lastId = 0, $column = 'id')
2403
    {
2404
        $this->orders = $this->removeExistingOrdersFor($column);
2405
 
2406
        if (! is_null($lastId)) {
2407
            $this->where($column, '>', $lastId);
2408
        }
2409
 
2410
        return $this->orderBy($column, 'asc')
2411
                    ->limit($perPage);
2412
    }
2413
 
2414
    /**
2415
     * Remove all existing orders and optionally add a new order.
2416
     *
2417
     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Query\Expression|string|null  $column
2418
     * @param  string  $direction
2419
     * @return $this
2420
     */
2421
    public function reorder($column = null, $direction = 'asc')
2422
    {
2423
        $this->orders = null;
2424
        $this->unionOrders = null;
2425
        $this->bindings['order'] = [];
2426
        $this->bindings['unionOrder'] = [];
2427
 
2428
        if ($column) {
2429
            return $this->orderBy($column, $direction);
2430
        }
2431
 
2432
        return $this;
2433
    }
2434
 
2435
    /**
2436
     * Get an array with all orders with a given column removed.
2437
     *
2438
     * @param  string  $column
2439
     * @return array
2440
     */
2441
    protected function removeExistingOrdersFor($column)
2442
    {
2443
        return Collection::make($this->orders)
2444
                    ->reject(function ($order) use ($column) {
2445
                        return isset($order['column'])
2446
                               ? $order['column'] === $column : false;
2447
                    })->values()->all();
2448
    }
2449
 
2450
    /**
2451
     * Add a union statement to the query.
2452
     *
2453
     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder  $query
2454
     * @param  bool  $all
2455
     * @return $this
2456
     */
2457
    public function union($query, $all = false)
2458
    {
2459
        if ($query instanceof Closure) {
2460
            $query($query = $this->newQuery());
2461
        }
2462
 
2463
        $this->unions[] = compact('query', 'all');
2464
 
2465
        $this->addBinding($query->getBindings(), 'union');
2466
 
2467
        return $this;
2468
    }
2469
 
2470
    /**
2471
     * Add a union all statement to the query.
2472
     *
2473
     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder  $query
2474
     * @return $this
2475
     */
2476
    public function unionAll($query)
2477
    {
2478
        return $this->union($query, true);
2479
    }
2480
 
2481
    /**
2482
     * Lock the selected rows in the table.
2483
     *
2484
     * @param  string|bool  $value
2485
     * @return $this
2486
     */
2487
    public function lock($value = true)
2488
    {
2489
        $this->lock = $value;
2490
 
2491
        if (! is_null($this->lock)) {
2492
            $this->useWritePdo();
2493
        }
2494
 
2495
        return $this;
2496
    }
2497
 
2498
    /**
2499
     * Lock the selected rows in the table for updating.
2500
     *
399 lars 2501
     * @return $this
148 lars 2502
     */
2503
    public function lockForUpdate()
2504
    {
2505
        return $this->lock(true);
2506
    }
2507
 
2508
    /**
2509
     * Share lock the selected rows in the table.
2510
     *
399 lars 2511
     * @return $this
148 lars 2512
     */
2513
    public function sharedLock()
2514
    {
2515
        return $this->lock(false);
2516
    }
2517
 
2518
    /**
2519
     * Register a closure to be invoked before the query is executed.
2520
     *
2521
     * @param  callable  $callback
2522
     * @return $this
2523
     */
2524
    public function beforeQuery(callable $callback)
2525
    {
2526
        $this->beforeQueryCallbacks[] = $callback;
2527
 
2528
        return $this;
2529
    }
2530
 
2531
    /**
2532
     * Invoke the "before query" modification callbacks.
2533
     *
2534
     * @return void
2535
     */
2536
    public function applyBeforeQueryCallbacks()
2537
    {
2538
        foreach ($this->beforeQueryCallbacks as $callback) {
2539
            $callback($this);
2540
        }
2541
 
2542
        $this->beforeQueryCallbacks = [];
2543
    }
2544
 
2545
    /**
2546
     * Get the SQL representation of the query.
2547
     *
2548
     * @return string
2549
     */
2550
    public function toSql()
2551
    {
2552
        $this->applyBeforeQueryCallbacks();
2553
 
2554
        return $this->grammar->compileSelect($this);
2555
    }
2556
 
2557
    /**
2558
     * Execute a query for a single record by ID.
2559
     *
2560
     * @param  int|string  $id
2561
     * @param  array|string  $columns
2562
     * @return mixed|static
2563
     */
2564
    public function find($id, $columns = ['*'])
2565
    {
2566
        return $this->where('id', '=', $id)->first($columns);
2567
    }
2568
 
2569
    /**
2570
     * Execute a query for a single record by ID or call a callback.
2571
     *
2572
     * @param  mixed  $id
2573
     * @param  \Closure|array|string  $columns
2574
     * @param  \Closure|null  $callback
2575
     * @return mixed|static
2576
     */
2577
    public function findOr($id, $columns = ['*'], Closure $callback = null)
2578
    {
2579
        if ($columns instanceof Closure) {
2580
            $callback = $columns;
2581
 
2582
            $columns = ['*'];
2583
        }
2584
 
2585
        if (! is_null($data = $this->find($id, $columns))) {
2586
            return $data;
2587
        }
2588
 
2589
        return $callback();
2590
    }
2591
 
2592
    /**
2593
     * Get a single column's value from the first result of a query.
2594
     *
2595
     * @param  string  $column
2596
     * @return mixed
2597
     */
2598
    public function value($column)
2599
    {
2600
        $result = (array) $this->first([$column]);
2601
 
2602
        return count($result) > 0 ? reset($result) : null;
2603
    }
2604
 
2605
    /**
2606
     * Get a single expression value from the first result of a query.
2607
     *
2608
     * @param  string  $expression
2609
     * @param  array  $bindings
2610
     * @return mixed
2611
     */
2612
    public function rawValue(string $expression, array $bindings = [])
2613
    {
2614
        $result = (array) $this->selectRaw($expression, $bindings)->first();
2615
 
2616
        return count($result) > 0 ? reset($result) : null;
2617
    }
2618
 
2619
    /**
2620
     * Get a single column's value from the first result of a query if it's the sole matching record.
2621
     *
2622
     * @param  string  $column
2623
     * @return mixed
2624
     *
2625
     * @throws \Illuminate\Database\RecordsNotFoundException
2626
     * @throws \Illuminate\Database\MultipleRecordsFoundException
2627
     */
2628
    public function soleValue($column)
2629
    {
2630
        $result = (array) $this->sole([$column]);
2631
 
2632
        return reset($result);
2633
    }
2634
 
2635
    /**
2636
     * Execute the query as a "select" statement.
2637
     *
2638
     * @param  array|string  $columns
2639
     * @return \Illuminate\Support\Collection
2640
     */
2641
    public function get($columns = ['*'])
2642
    {
2643
        return collect($this->onceWithColumns(Arr::wrap($columns), function () {
2644
            return $this->processor->processSelect($this, $this->runSelect());
2645
        }));
2646
    }
2647
 
2648
    /**
2649
     * Run the query as a "select" statement against the connection.
2650
     *
2651
     * @return array
2652
     */
2653
    protected function runSelect()
2654
    {
2655
        return $this->connection->select(
2656
            $this->toSql(), $this->getBindings(), ! $this->useWritePdo
2657
        );
2658
    }
2659
 
2660
    /**
2661
     * Paginate the given query into a simple paginator.
2662
     *
2663
     * @param  int|\Closure  $perPage
2664
     * @param  array|string  $columns
2665
     * @param  string  $pageName
2666
     * @param  int|null  $page
2667
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
2668
     */
2669
    public function paginate($perPage = 15, $columns = ['*'], $pageName = 'page', $page = null)
2670
    {
2671
        $page = $page ?: Paginator::resolveCurrentPage($pageName);
2672
 
2673
        $total = $this->getCountForPagination();
2674
 
2675
        $perPage = $perPage instanceof Closure ? $perPage($total) : $perPage;
2676
 
2677
        $results = $total ? $this->forPage($page, $perPage)->get($columns) : collect();
2678
 
2679
        return $this->paginator($results, $total, $perPage, $page, [
2680
            'path' => Paginator::resolveCurrentPath(),
2681
            'pageName' => $pageName,
2682
        ]);
2683
    }
2684
 
2685
    /**
2686
     * Get a paginator only supporting simple next and previous links.
2687
     *
2688
     * This is more efficient on larger data-sets, etc.
2689
     *
2690
     * @param  int  $perPage
2691
     * @param  array|string  $columns
2692
     * @param  string  $pageName
2693
     * @param  int|null  $page
2694
     * @return \Illuminate\Contracts\Pagination\Paginator
2695
     */
2696
    public function simplePaginate($perPage = 15, $columns = ['*'], $pageName = 'page', $page = null)
2697
    {
2698
        $page = $page ?: Paginator::resolveCurrentPage($pageName);
2699
 
2700
        $this->offset(($page - 1) * $perPage)->limit($perPage + 1);
2701
 
2702
        return $this->simplePaginator($this->get($columns), $perPage, $page, [
2703
            'path' => Paginator::resolveCurrentPath(),
2704
            'pageName' => $pageName,
2705
        ]);
2706
    }
2707
 
2708
    /**
2709
     * Get a paginator only supporting simple next and previous links.
2710
     *
2711
     * This is more efficient on larger data-sets, etc.
2712
     *
2713
     * @param  int|null  $perPage
2714
     * @param  array|string  $columns
2715
     * @param  string  $cursorName
2716
     * @param  \Illuminate\Pagination\Cursor|string|null  $cursor
2717
     * @return \Illuminate\Contracts\Pagination\CursorPaginator
2718
     */
2719
    public function cursorPaginate($perPage = 15, $columns = ['*'], $cursorName = 'cursor', $cursor = null)
2720
    {
2721
        return $this->paginateUsingCursor($perPage, $columns, $cursorName, $cursor);
2722
    }
2723
 
2724
    /**
2725
     * Ensure the proper order by required for cursor pagination.
2726
     *
2727
     * @param  bool  $shouldReverse
2728
     * @return \Illuminate\Support\Collection
2729
     */
2730
    protected function ensureOrderForCursorPagination($shouldReverse = false)
2731
    {
2732
        $this->enforceOrderBy();
2733
 
2734
        return collect($this->orders ?? $this->unionOrders ?? [])->filter(function ($order) {
2735
            return Arr::has($order, 'direction');
2736
        })->when($shouldReverse, function (Collection $orders) {
2737
            return $orders->map(function ($order) {
2738
                $order['direction'] = $order['direction'] === 'asc' ? 'desc' : 'asc';
2739
 
2740
                return $order;
2741
            });
2742
        })->values();
2743
    }
2744
 
2745
    /**
2746
     * Get the count of the total records for the paginator.
2747
     *
2748
     * @param  array  $columns
2749
     * @return int
2750
     */
2751
    public function getCountForPagination($columns = ['*'])
2752
    {
2753
        $results = $this->runPaginationCountQuery($columns);
2754
 
2755
        // Once we have run the pagination count query, we will get the resulting count and
2756
        // take into account what type of query it was. When there is a group by we will
2757
        // just return the count of the entire results set since that will be correct.
2758
        if (! isset($results[0])) {
2759
            return 0;
2760
        } elseif (is_object($results[0])) {
2761
            return (int) $results[0]->aggregate;
2762
        }
2763
 
2764
        return (int) array_change_key_case((array) $results[0])['aggregate'];
2765
    }
2766
 
2767
    /**
2768
     * Run a pagination count query.
2769
     *
2770
     * @param  array  $columns
2771
     * @return array
2772
     */
2773
    protected function runPaginationCountQuery($columns = ['*'])
2774
    {
2775
        if ($this->groups || $this->havings) {
2776
            $clone = $this->cloneForPaginationCount();
2777
 
2778
            if (is_null($clone->columns) && ! empty($this->joins)) {
2779
                $clone->select($this->from.'.*');
2780
            }
2781
 
2782
            return $this->newQuery()
2783
                ->from(new Expression('('.$clone->toSql().') as '.$this->grammar->wrap('aggregate_table')))
2784
                ->mergeBindings($clone)
2785
                ->setAggregate('count', $this->withoutSelectAliases($columns))
2786
                ->get()->all();
2787
        }
2788
 
2789
        $without = $this->unions ? ['orders', 'limit', 'offset'] : ['columns', 'orders', 'limit', 'offset'];
2790
 
2791
        return $this->cloneWithout($without)
2792
                    ->cloneWithoutBindings($this->unions ? ['order'] : ['select', 'order'])
2793
                    ->setAggregate('count', $this->withoutSelectAliases($columns))
2794
                    ->get()->all();
2795
    }
2796
 
2797
    /**
2798
     * Clone the existing query instance for usage in a pagination subquery.
2799
     *
2800
     * @return self
2801
     */
2802
    protected function cloneForPaginationCount()
2803
    {
2804
        return $this->cloneWithout(['orders', 'limit', 'offset'])
2805
                    ->cloneWithoutBindings(['order']);
2806
    }
2807
 
2808
    /**
2809
     * Remove the column aliases since they will break count queries.
2810
     *
2811
     * @param  array  $columns
2812
     * @return array
2813
     */
2814
    protected function withoutSelectAliases(array $columns)
2815
    {
2816
        return array_map(function ($column) {
2817
            return is_string($column) && ($aliasPosition = stripos($column, ' as ')) !== false
2818
                    ? substr($column, 0, $aliasPosition) : $column;
2819
        }, $columns);
2820
    }
2821
 
2822
    /**
2823
     * Get a lazy collection for the given query.
2824
     *
2825
     * @return \Illuminate\Support\LazyCollection
2826
     */
2827
    public function cursor()
2828
    {
2829
        if (is_null($this->columns)) {
2830
            $this->columns = ['*'];
2831
        }
2832
 
2833
        return new LazyCollection(function () {
2834
            yield from $this->connection->cursor(
2835
                $this->toSql(), $this->getBindings(), ! $this->useWritePdo
2836
            );
2837
        });
2838
    }
2839
 
2840
    /**
2841
     * Throw an exception if the query doesn't have an orderBy clause.
2842
     *
2843
     * @return void
2844
     *
2845
     * @throws \RuntimeException
2846
     */
2847
    protected function enforceOrderBy()
2848
    {
2849
        if (empty($this->orders) && empty($this->unionOrders)) {
2850
            throw new RuntimeException('You must specify an orderBy clause when using this function.');
2851
        }
2852
    }
2853
 
2854
    /**
2855
     * Get a collection instance containing the values of a given column.
2856
     *
2857
     * @param  string  $column
2858
     * @param  string|null  $key
2859
     * @return \Illuminate\Support\Collection
2860
     */
2861
    public function pluck($column, $key = null)
2862
    {
2863
        // First, we will need to select the results of the query accounting for the
2864
        // given columns / key. Once we have the results, we will be able to take
2865
        // the results and get the exact data that was requested for the query.
2866
        $queryResult = $this->onceWithColumns(
2867
            is_null($key) ? [$column] : [$column, $key],
2868
            function () {
2869
                return $this->processor->processSelect(
2870
                    $this, $this->runSelect()
2871
                );
2872
            }
2873
        );
2874
 
2875
        if (empty($queryResult)) {
2876
            return collect();
2877
        }
2878
 
2879
        // If the columns are qualified with a table or have an alias, we cannot use
2880
        // those directly in the "pluck" operations since the results from the DB
2881
        // are only keyed by the column itself. We'll strip the table out here.
2882
        $column = $this->stripTableForPluck($column);
2883
 
2884
        $key = $this->stripTableForPluck($key);
2885
 
2886
        return is_array($queryResult[0])
2887
                    ? $this->pluckFromArrayColumn($queryResult, $column, $key)
2888
                    : $this->pluckFromObjectColumn($queryResult, $column, $key);
2889
    }
2890
 
2891
    /**
2892
     * Strip off the table name or alias from a column identifier.
2893
     *
2894
     * @param  string  $column
2895
     * @return string|null
2896
     */
2897
    protected function stripTableForPluck($column)
2898
    {
2899
        if (is_null($column)) {
2900
            return $column;
2901
        }
2902
 
2903
        $separator = str_contains(strtolower($column), ' as ') ? ' as ' : '\.';
2904
 
2905
        return last(preg_split('~'.$separator.'~i', $column));
2906
    }
2907
 
2908
    /**
2909
     * Retrieve column values from rows represented as objects.
2910
     *
2911
     * @param  array  $queryResult
2912
     * @param  string  $column
2913
     * @param  string  $key
2914
     * @return \Illuminate\Support\Collection
2915
     */
2916
    protected function pluckFromObjectColumn($queryResult, $column, $key)
2917
    {
2918
        $results = [];
2919
 
2920
        if (is_null($key)) {
2921
            foreach ($queryResult as $row) {
2922
                $results[] = $row->$column;
2923
            }
2924
        } else {
2925
            foreach ($queryResult as $row) {
2926
                $results[$row->$key] = $row->$column;
2927
            }
2928
        }
2929
 
2930
        return collect($results);
2931
    }
2932
 
2933
    /**
2934
     * Retrieve column values from rows represented as arrays.
2935
     *
2936
     * @param  array  $queryResult
2937
     * @param  string  $column
2938
     * @param  string  $key
2939
     * @return \Illuminate\Support\Collection
2940
     */
2941
    protected function pluckFromArrayColumn($queryResult, $column, $key)
2942
    {
2943
        $results = [];
2944
 
2945
        if (is_null($key)) {
2946
            foreach ($queryResult as $row) {
2947
                $results[] = $row[$column];
2948
            }
2949
        } else {
2950
            foreach ($queryResult as $row) {
2951
                $results[$row[$key]] = $row[$column];
2952
            }
2953
        }
2954
 
2955
        return collect($results);
2956
    }
2957
 
2958
    /**
2959
     * Concatenate values of a given column as a string.
2960
     *
2961
     * @param  string  $column
2962
     * @param  string  $glue
2963
     * @return string
2964
     */
2965
    public function implode($column, $glue = '')
2966
    {
2967
        return $this->pluck($column)->implode($glue);
2968
    }
2969
 
2970
    /**
2971
     * Determine if any rows exist for the current query.
2972
     *
2973
     * @return bool
2974
     */
2975
    public function exists()
2976
    {
2977
        $this->applyBeforeQueryCallbacks();
2978
 
2979
        $results = $this->connection->select(
2980
            $this->grammar->compileExists($this), $this->getBindings(), ! $this->useWritePdo
2981
        );
2982
 
2983
        // If the results have rows, we will get the row and see if the exists column is a
2984
        // boolean true. If there are no results for this query we will return false as
2985
        // there are no rows for this query at all, and we can return that info here.
2986
        if (isset($results[0])) {
2987
            $results = (array) $results[0];
2988
 
2989
            return (bool) $results['exists'];
2990
        }
2991
 
2992
        return false;
2993
    }
2994
 
2995
    /**
2996
     * Determine if no rows exist for the current query.
2997
     *
2998
     * @return bool
2999
     */
3000
    public function doesntExist()
3001
    {
3002
        return ! $this->exists();
3003
    }
3004
 
3005
    /**
3006
     * Execute the given callback if no rows exist for the current query.
3007
     *
3008
     * @param  \Closure  $callback
3009
     * @return mixed
3010
     */
3011
    public function existsOr(Closure $callback)
3012
    {
3013
        return $this->exists() ? true : $callback();
3014
    }
3015
 
3016
    /**
3017
     * Execute the given callback if rows exist for the current query.
3018
     *
3019
     * @param  \Closure  $callback
3020
     * @return mixed
3021
     */
3022
    public function doesntExistOr(Closure $callback)
3023
    {
3024
        return $this->doesntExist() ? true : $callback();
3025
    }
3026
 
3027
    /**
3028
     * Retrieve the "count" result of the query.
3029
     *
3030
     * @param  string  $columns
3031
     * @return int
3032
     */
3033
    public function count($columns = '*')
3034
    {
3035
        return (int) $this->aggregate(__FUNCTION__, Arr::wrap($columns));
3036
    }
3037
 
3038
    /**
3039
     * Retrieve the minimum value of a given column.
3040
     *
3041
     * @param  string  $column
3042
     * @return mixed
3043
     */
3044
    public function min($column)
3045
    {
3046
        return $this->aggregate(__FUNCTION__, [$column]);
3047
    }
3048
 
3049
    /**
3050
     * Retrieve the maximum value of a given column.
3051
     *
3052
     * @param  string  $column
3053
     * @return mixed
3054
     */
3055
    public function max($column)
3056
    {
3057
        return $this->aggregate(__FUNCTION__, [$column]);
3058
    }
3059
 
3060
    /**
3061
     * Retrieve the sum of the values of a given column.
3062
     *
3063
     * @param  string  $column
3064
     * @return mixed
3065
     */
3066
    public function sum($column)
3067
    {
3068
        $result = $this->aggregate(__FUNCTION__, [$column]);
3069
 
3070
        return $result ?: 0;
3071
    }
3072
 
3073
    /**
3074
     * Retrieve the average of the values of a given column.
3075
     *
3076
     * @param  string  $column
3077
     * @return mixed
3078
     */
3079
    public function avg($column)
3080
    {
3081
        return $this->aggregate(__FUNCTION__, [$column]);
3082
    }
3083
 
3084
    /**
3085
     * Alias for the "avg" method.
3086
     *
3087
     * @param  string  $column
3088
     * @return mixed
3089
     */
3090
    public function average($column)
3091
    {
3092
        return $this->avg($column);
3093
    }
3094
 
3095
    /**
3096
     * Execute an aggregate function on the database.
3097
     *
3098
     * @param  string  $function
3099
     * @param  array  $columns
3100
     * @return mixed
3101
     */
3102
    public function aggregate($function, $columns = ['*'])
3103
    {
3104
        $results = $this->cloneWithout($this->unions || $this->havings ? [] : ['columns'])
3105
                        ->cloneWithoutBindings($this->unions || $this->havings ? [] : ['select'])
3106
                        ->setAggregate($function, $columns)
3107
                        ->get($columns);
3108
 
3109
        if (! $results->isEmpty()) {
3110
            return array_change_key_case((array) $results[0])['aggregate'];
3111
        }
3112
    }
3113
 
3114
    /**
3115
     * Execute a numeric aggregate function on the database.
3116
     *
3117
     * @param  string  $function
3118
     * @param  array  $columns
3119
     * @return float|int
3120
     */
3121
    public function numericAggregate($function, $columns = ['*'])
3122
    {
3123
        $result = $this->aggregate($function, $columns);
3124
 
3125
        // If there is no result, we can obviously just return 0 here. Next, we will check
3126
        // if the result is an integer or float. If it is already one of these two data
3127
        // types we can just return the result as-is, otherwise we will convert this.
3128
        if (! $result) {
3129
            return 0;
3130
        }
3131
 
3132
        if (is_int($result) || is_float($result)) {
3133
            return $result;
3134
        }
3135
 
3136
        // If the result doesn't contain a decimal place, we will assume it is an int then
3137
        // cast it to one. When it does we will cast it to a float since it needs to be
3138
        // cast to the expected data type for the developers out of pure convenience.
3139
        return ! str_contains((string) $result, '.')
3140
                ? (int) $result : (float) $result;
3141
    }
3142
 
3143
    /**
3144
     * Set the aggregate property without running the query.
3145
     *
3146
     * @param  string  $function
3147
     * @param  array  $columns
3148
     * @return $this
3149
     */
3150
    protected function setAggregate($function, $columns)
3151
    {
3152
        $this->aggregate = compact('function', 'columns');
3153
 
3154
        if (empty($this->groups)) {
3155
            $this->orders = null;
3156
 
3157
            $this->bindings['order'] = [];
3158
        }
3159
 
3160
        return $this;
3161
    }
3162
 
3163
    /**
3164
     * Execute the given callback while selecting the given columns.
3165
     *
3166
     * After running the callback, the columns are reset to the original value.
3167
     *
3168
     * @param  array  $columns
3169
     * @param  callable  $callback
3170
     * @return mixed
3171
     */
3172
    protected function onceWithColumns($columns, $callback)
3173
    {
3174
        $original = $this->columns;
3175
 
3176
        if (is_null($original)) {
3177
            $this->columns = $columns;
3178
        }
3179
 
3180
        $result = $callback();
3181
 
3182
        $this->columns = $original;
3183
 
3184
        return $result;
3185
    }
3186
 
3187
    /**
3188
     * Insert new records into the database.
3189
     *
3190
     * @param  array  $values
3191
     * @return bool
3192
     */
3193
    public function insert(array $values)
3194
    {
3195
        // Since every insert gets treated like a batch insert, we will make sure the
3196
        // bindings are structured in a way that is convenient when building these
3197
        // inserts statements by verifying these elements are actually an array.
3198
        if (empty($values)) {
3199
            return true;
3200
        }
3201
 
3202
        if (! is_array(reset($values))) {
3203
            $values = [$values];
3204
        }
3205
 
3206
        // Here, we will sort the insert keys for every record so that each insert is
3207
        // in the same order for the record. We need to make sure this is the case
3208
        // so there are not any errors or problems when inserting these records.
3209
        else {
3210
            foreach ($values as $key => $value) {
3211
                ksort($value);
3212
 
3213
                $values[$key] = $value;
3214
            }
3215
        }
3216
 
3217
        $this->applyBeforeQueryCallbacks();
3218
 
3219
        // Finally, we will run this query against the database connection and return
3220
        // the results. We will need to also flatten these bindings before running
3221
        // the query so they are all in one huge, flattened array for execution.
3222
        return $this->connection->insert(
3223
            $this->grammar->compileInsert($this, $values),
3224
            $this->cleanBindings(Arr::flatten($values, 1))
3225
        );
3226
    }
3227
 
3228
    /**
3229
     * Insert new records into the database while ignoring errors.
3230
     *
3231
     * @param  array  $values
3232
     * @return int
3233
     */
3234
    public function insertOrIgnore(array $values)
3235
    {
3236
        if (empty($values)) {
3237
            return 0;
3238
        }
3239
 
3240
        if (! is_array(reset($values))) {
3241
            $values = [$values];
3242
        } else {
3243
            foreach ($values as $key => $value) {
3244
                ksort($value);
3245
                $values[$key] = $value;
3246
            }
3247
        }
3248
 
3249
        $this->applyBeforeQueryCallbacks();
3250
 
3251
        return $this->connection->affectingStatement(
3252
            $this->grammar->compileInsertOrIgnore($this, $values),
3253
            $this->cleanBindings(Arr::flatten($values, 1))
3254
        );
3255
    }
3256
 
3257
    /**
3258
     * Insert a new record and get the value of the primary key.
3259
     *
3260
     * @param  array  $values
3261
     * @param  string|null  $sequence
3262
     * @return int
3263
     */
3264
    public function insertGetId(array $values, $sequence = null)
3265
    {
3266
        $this->applyBeforeQueryCallbacks();
3267
 
3268
        $sql = $this->grammar->compileInsertGetId($this, $values, $sequence);
3269
 
3270
        $values = $this->cleanBindings($values);
3271
 
3272
        return $this->processor->processInsertGetId($this, $sql, $values, $sequence);
3273
    }
3274
 
3275
    /**
3276
     * Insert new records into the table using a subquery.
3277
     *
3278
     * @param  array  $columns
3279
     * @param  \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string  $query
3280
     * @return int
3281
     */
3282
    public function insertUsing(array $columns, $query)
3283
    {
3284
        $this->applyBeforeQueryCallbacks();
3285
 
3286
        [$sql, $bindings] = $this->createSub($query);
3287
 
3288
        return $this->connection->affectingStatement(
3289
            $this->grammar->compileInsertUsing($this, $columns, $sql),
3290
            $this->cleanBindings($bindings)
3291
        );
3292
    }
3293
 
3294
    /**
3295
     * Update records in the database.
3296
     *
3297
     * @param  array  $values
3298
     * @return int
3299
     */
3300
    public function update(array $values)
3301
    {
3302
        $this->applyBeforeQueryCallbacks();
3303
 
3304
        $sql = $this->grammar->compileUpdate($this, $values);
3305
 
3306
        return $this->connection->update($sql, $this->cleanBindings(
3307
            $this->grammar->prepareBindingsForUpdate($this->bindings, $values)
3308
        ));
3309
    }
3310
 
3311
    /**
3312
     * Update records in a PostgreSQL database using the update from syntax.
3313
     *
3314
     * @param  array  $values
3315
     * @return int
3316
     */
3317
    public function updateFrom(array $values)
3318
    {
3319
        if (! method_exists($this->grammar, 'compileUpdateFrom')) {
3320
            throw new LogicException('This database engine does not support the updateFrom method.');
3321
        }
3322
 
3323
        $this->applyBeforeQueryCallbacks();
3324
 
3325
        $sql = $this->grammar->compileUpdateFrom($this, $values);
3326
 
3327
        return $this->connection->update($sql, $this->cleanBindings(
3328
            $this->grammar->prepareBindingsForUpdateFrom($this->bindings, $values)
3329
        ));
3330
    }
3331
 
3332
    /**
3333
     * Insert or update a record matching the attributes, and fill it with values.
3334
     *
3335
     * @param  array  $attributes
3336
     * @param  array  $values
3337
     * @return bool
3338
     */
3339
    public function updateOrInsert(array $attributes, array $values = [])
3340
    {
3341
        if (! $this->where($attributes)->exists()) {
3342
            return $this->insert(array_merge($attributes, $values));
3343
        }
3344
 
3345
        if (empty($values)) {
3346
            return true;
3347
        }
3348
 
3349
        return (bool) $this->limit(1)->update($values);
3350
    }
3351
 
3352
    /**
3353
     * Insert new records or update the existing ones.
3354
     *
3355
     * @param  array  $values
3356
     * @param  array|string  $uniqueBy
3357
     * @param  array|null  $update
3358
     * @return int
3359
     */
3360
    public function upsert(array $values, $uniqueBy, $update = null)
3361
    {
3362
        if (empty($values)) {
3363
            return 0;
3364
        } elseif ($update === []) {
3365
            return (int) $this->insert($values);
3366
        }
3367
 
3368
        if (! is_array(reset($values))) {
3369
            $values = [$values];
3370
        } else {
3371
            foreach ($values as $key => $value) {
3372
                ksort($value);
3373
 
3374
                $values[$key] = $value;
3375
            }
3376
        }
3377
 
3378
        if (is_null($update)) {
3379
            $update = array_keys(reset($values));
3380
        }
3381
 
3382
        $this->applyBeforeQueryCallbacks();
3383
 
3384
        $bindings = $this->cleanBindings(array_merge(
3385
            Arr::flatten($values, 1),
3386
            collect($update)->reject(function ($value, $key) {
3387
                return is_int($key);
3388
            })->all()
3389
        ));
3390
 
3391
        return $this->connection->affectingStatement(
3392
            $this->grammar->compileUpsert($this, $values, (array) $uniqueBy, $update),
3393
            $bindings
3394
        );
3395
    }
3396
 
3397
    /**
3398
     * Increment a column's value by a given amount.
3399
     *
3400
     * @param  string  $column
3401
     * @param  float|int  $amount
3402
     * @param  array  $extra
3403
     * @return int
3404
     *
3405
     * @throws \InvalidArgumentException
3406
     */
3407
    public function increment($column, $amount = 1, array $extra = [])
3408
    {
3409
        if (! is_numeric($amount)) {
3410
            throw new InvalidArgumentException('Non-numeric value passed to increment method.');
3411
        }
3412
 
150 lars 3413
        return $this->incrementEach([$column => $amount], $extra);
3414
    }
148 lars 3415
 
150 lars 3416
    /**
3417
     * Increment the given column's values by the given amounts.
3418
     *
3419
     * @param  array<string, float|int|numeric-string>  $columns
3420
     * @param  array<string, mixed>  $extra
3421
     * @return int
3422
     *
3423
     * @throws \InvalidArgumentException
3424
     */
3425
    public function incrementEach(array $columns, array $extra = [])
3426
    {
3427
        foreach ($columns as $column => $amount) {
3428
            if (! is_numeric($amount)) {
3429
                throw new InvalidArgumentException("Non-numeric value passed as increment amount for column: '$column'.");
3430
            } elseif (! is_string($column)) {
3431
                throw new InvalidArgumentException('Non-associative array passed to incrementEach method.');
3432
            }
148 lars 3433
 
150 lars 3434
            $columns[$column] = $this->raw("{$this->grammar->wrap($column)} + $amount");
3435
        }
3436
 
3437
        return $this->update(array_merge($columns, $extra));
148 lars 3438
    }
3439
 
3440
    /**
3441
     * Decrement a column's value by a given amount.
3442
     *
3443
     * @param  string  $column
3444
     * @param  float|int  $amount
3445
     * @param  array  $extra
3446
     * @return int
3447
     *
3448
     * @throws \InvalidArgumentException
3449
     */
3450
    public function decrement($column, $amount = 1, array $extra = [])
3451
    {
3452
        if (! is_numeric($amount)) {
3453
            throw new InvalidArgumentException('Non-numeric value passed to decrement method.');
3454
        }
3455
 
150 lars 3456
        return $this->decrementEach([$column => $amount], $extra);
3457
    }
148 lars 3458
 
150 lars 3459
    /**
3460
     * Decrement the given column's values by the given amounts.
3461
     *
3462
     * @param  array<string, float|int|numeric-string>  $columns
3463
     * @param  array<string, mixed>  $extra
3464
     * @return int
3465
     *
3466
     * @throws \InvalidArgumentException
3467
     */
3468
    public function decrementEach(array $columns, array $extra = [])
3469
    {
3470
        foreach ($columns as $column => $amount) {
3471
            if (! is_numeric($amount)) {
3472
                throw new InvalidArgumentException("Non-numeric value passed as decrement amount for column: '$column'.");
3473
            } elseif (! is_string($column)) {
3474
                throw new InvalidArgumentException('Non-associative array passed to decrementEach method.');
3475
            }
148 lars 3476
 
150 lars 3477
            $columns[$column] = $this->raw("{$this->grammar->wrap($column)} - $amount");
3478
        }
3479
 
3480
        return $this->update(array_merge($columns, $extra));
148 lars 3481
    }
3482
 
3483
    /**
3484
     * Delete records from the database.
3485
     *
3486
     * @param  mixed  $id
3487
     * @return int
3488
     */
3489
    public function delete($id = null)
3490
    {
3491
        // If an ID is passed to the method, we will set the where clause to check the
3492
        // ID to let developers to simply and quickly remove a single row from this
3493
        // database without manually specifying the "where" clauses on the query.
3494
        if (! is_null($id)) {
3495
            $this->where($this->from.'.id', '=', $id);
3496
        }
3497
 
3498
        $this->applyBeforeQueryCallbacks();
3499
 
3500
        return $this->connection->delete(
3501
            $this->grammar->compileDelete($this), $this->cleanBindings(
3502
                $this->grammar->prepareBindingsForDelete($this->bindings)
3503
            )
3504
        );
3505
    }
3506
 
3507
    /**
3508
     * Run a truncate statement on the table.
3509
     *
3510
     * @return void
3511
     */
3512
    public function truncate()
3513
    {
3514
        $this->applyBeforeQueryCallbacks();
3515
 
3516
        foreach ($this->grammar->compileTruncate($this) as $sql => $bindings) {
3517
            $this->connection->statement($sql, $bindings);
3518
        }
3519
    }
3520
 
3521
    /**
3522
     * Get a new instance of the query builder.
3523
     *
3524
     * @return \Illuminate\Database\Query\Builder
3525
     */
3526
    public function newQuery()
3527
    {
3528
        return new static($this->connection, $this->grammar, $this->processor);
3529
    }
3530
 
3531
    /**
3532
     * Create a new query instance for a sub-query.
3533
     *
3534
     * @return \Illuminate\Database\Query\Builder
3535
     */
3536
    protected function forSubQuery()
3537
    {
3538
        return $this->newQuery();
3539
    }
3540
 
3541
    /**
3542
     * Create a raw database expression.
3543
     *
3544
     * @param  mixed  $value
3545
     * @return \Illuminate\Database\Query\Expression
3546
     */
3547
    public function raw($value)
3548
    {
3549
        return $this->connection->raw($value);
3550
    }
3551
 
3552
    /**
3553
     * Get the current query value bindings in a flattened array.
3554
     *
3555
     * @return array
3556
     */
3557
    public function getBindings()
3558
    {
3559
        return Arr::flatten($this->bindings);
3560
    }
3561
 
3562
    /**
3563
     * Get the raw array of bindings.
3564
     *
3565
     * @return array
3566
     */
3567
    public function getRawBindings()
3568
    {
3569
        return $this->bindings;
3570
    }
3571
 
3572
    /**
3573
     * Set the bindings on the query builder.
3574
     *
3575
     * @param  array  $bindings
3576
     * @param  string  $type
3577
     * @return $this
3578
     *
3579
     * @throws \InvalidArgumentException
3580
     */
3581
    public function setBindings(array $bindings, $type = 'where')
3582
    {
3583
        if (! array_key_exists($type, $this->bindings)) {
3584
            throw new InvalidArgumentException("Invalid binding type: {$type}.");
3585
        }
3586
 
3587
        $this->bindings[$type] = $bindings;
3588
 
3589
        return $this;
3590
    }
3591
 
3592
    /**
3593
     * Add a binding to the query.
3594
     *
3595
     * @param  mixed  $value
3596
     * @param  string  $type
3597
     * @return $this
3598
     *
3599
     * @throws \InvalidArgumentException
3600
     */
3601
    public function addBinding($value, $type = 'where')
3602
    {
3603
        if (! array_key_exists($type, $this->bindings)) {
3604
            throw new InvalidArgumentException("Invalid binding type: {$type}.");
3605
        }
3606
 
3607
        if (is_array($value)) {
3608
            $this->bindings[$type] = array_values(array_map(
3609
                [$this, 'castBinding'],
3610
                array_merge($this->bindings[$type], $value),
3611
            ));
3612
        } else {
3613
            $this->bindings[$type][] = $this->castBinding($value);
3614
        }
3615
 
3616
        return $this;
3617
    }
3618
 
3619
    /**
3620
     * Cast the given binding value.
3621
     *
3622
     * @param  mixed  $value
3623
     * @return mixed
3624
     */
3625
    public function castBinding($value)
3626
    {
3627
        if (function_exists('enum_exists') && $value instanceof BackedEnum) {
3628
            return $value->value;
3629
        }
3630
 
3631
        return $value;
3632
    }
3633
 
3634
    /**
3635
     * Merge an array of bindings into our bindings.
3636
     *
3637
     * @param  \Illuminate\Database\Query\Builder  $query
3638
     * @return $this
3639
     */
3640
    public function mergeBindings(self $query)
3641
    {
3642
        $this->bindings = array_merge_recursive($this->bindings, $query->bindings);
3643
 
3644
        return $this;
3645
    }
3646
 
3647
    /**
3648
     * Remove all of the expressions from a list of bindings.
3649
     *
3650
     * @param  array  $bindings
3651
     * @return array
3652
     */
3653
    public function cleanBindings(array $bindings)
3654
    {
3655
        return collect($bindings)
3656
                    ->reject(function ($binding) {
3657
                        return $binding instanceof Expression;
3658
                    })
3659
                    ->map([$this, 'castBinding'])
3660
                    ->values()
3661
                    ->all();
3662
    }
3663
 
3664
    /**
3665
     * Get a scalar type value from an unknown type of input.
3666
     *
3667
     * @param  mixed  $value
3668
     * @return mixed
3669
     */
3670
    protected function flattenValue($value)
3671
    {
3672
        return is_array($value) ? head(Arr::flatten($value)) : $value;
3673
    }
3674
 
3675
    /**
3676
     * Get the default key name of the table.
3677
     *
3678
     * @return string
3679
     */
3680
    protected function defaultKeyName()
3681
    {
3682
        return 'id';
3683
    }
3684
 
3685
    /**
3686
     * Get the database connection instance.
3687
     *
3688
     * @return \Illuminate\Database\ConnectionInterface
3689
     */
3690
    public function getConnection()
3691
    {
3692
        return $this->connection;
3693
    }
3694
 
3695
    /**
3696
     * Get the database query processor instance.
3697
     *
3698
     * @return \Illuminate\Database\Query\Processors\Processor
3699
     */
3700
    public function getProcessor()
3701
    {
3702
        return $this->processor;
3703
    }
3704
 
3705
    /**
3706
     * Get the query grammar instance.
3707
     *
3708
     * @return \Illuminate\Database\Query\Grammars\Grammar
3709
     */
3710
    public function getGrammar()
3711
    {
3712
        return $this->grammar;
3713
    }
3714
 
3715
    /**
3716
     * Use the "write" PDO connection when executing the query.
3717
     *
3718
     * @return $this
3719
     */
3720
    public function useWritePdo()
3721
    {
3722
        $this->useWritePdo = true;
3723
 
3724
        return $this;
3725
    }
3726
 
3727
    /**
3728
     * Determine if the value is a query builder instance or a Closure.
3729
     *
3730
     * @param  mixed  $value
3731
     * @return bool
3732
     */
3733
    protected function isQueryable($value)
3734
    {
3735
        return $value instanceof self ||
3736
               $value instanceof EloquentBuilder ||
3737
               $value instanceof Relation ||
3738
               $value instanceof Closure;
3739
    }
3740
 
3741
    /**
3742
     * Clone the query.
3743
     *
3744
     * @return static
3745
     */
3746
    public function clone()
3747
    {
3748
        return clone $this;
3749
    }
3750
 
3751
    /**
3752
     * Clone the query without the given properties.
3753
     *
3754
     * @param  array  $properties
3755
     * @return static
3756
     */
3757
    public function cloneWithout(array $properties)
3758
    {
3759
        return tap($this->clone(), function ($clone) use ($properties) {
3760
            foreach ($properties as $property) {
3761
                $clone->{$property} = null;
3762
            }
3763
        });
3764
    }
3765
 
3766
    /**
3767
     * Clone the query without the given bindings.
3768
     *
3769
     * @param  array  $except
3770
     * @return static
3771
     */
3772
    public function cloneWithoutBindings(array $except)
3773
    {
3774
        return tap($this->clone(), function ($clone) use ($except) {
3775
            foreach ($except as $type) {
3776
                $clone->bindings[$type] = [];
3777
            }
3778
        });
3779
    }
3780
 
3781
    /**
3782
     * Dump the current SQL and bindings.
3783
     *
3784
     * @return $this
3785
     */
3786
    public function dump()
3787
    {
3788
        dump($this->toSql(), $this->getBindings());
3789
 
3790
        return $this;
3791
    }
3792
 
3793
    /**
3794
     * Die and dump the current SQL and bindings.
3795
     *
3796
     * @return never
3797
     */
3798
    public function dd()
3799
    {
3800
        dd($this->toSql(), $this->getBindings());
3801
    }
3802
 
3803
    /**
3804
     * Handle dynamic method calls into the method.
3805
     *
3806
     * @param  string  $method
3807
     * @param  array  $parameters
3808
     * @return mixed
3809
     *
3810
     * @throws \BadMethodCallException
3811
     */
3812
    public function __call($method, $parameters)
3813
    {
3814
        if (static::hasMacro($method)) {
3815
            return $this->macroCall($method, $parameters);
3816
        }
3817
 
3818
        if (str_starts_with($method, 'where')) {
3819
            return $this->dynamicWhere($method, $parameters);
3820
        }
3821
 
3822
        static::throwBadMethodCallException($method);
3823
    }
3824
}