Builder::raw()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Framy Framework
4
 *
5
 * @copyright Copyright Framy
6
 * @Author  Marco Bier <[email protected]>
7
 */
8
9
namespace app\framework\Component\Database\Query;
10
11
use app\framework\Component\StdLib\StdObject\ArrayObject\ArrayObject;
12
use app\framework\Component\Database\Connection\Connection;
13
use app\framework\Component\Database\Model\Model;
14
use app\framework\Component\Database\Query\Grammars\Grammar;
15
use Closure;
16
use Exception;
17
use InvalidArgumentException;
18
19
/**
20
 * Class Builder
21
 * Build Query based on grammar.
22
 *
23
 * @package app\framework\Component\Database\Query
24
 */
25
class Builder
26
{
27
    /**
28
     * @var Connection
29
     */
30
    private $connection;
31
32
    /**
33
     * The database query grammar instance.
34
     *
35
     * @var Grammar
36
     */
37
    private $grammar;
38
39
    /**
40
     * The columns that should be returned.
41
     *
42
     * @var array
43
     */
44
    public $columns;
45
46
    /**
47
     * The current query value bindings.
48
     *
49
     * @var array
50
     */
51
    protected $bindings = [
52
        'select' => [],
53
        'join'   => [],
54
        'where'  => [],
55
        'having' => [],
56
        'order'  => [],
57
        'union'  => [],
58
    ];
59
60
    /**
61
     * An aggregate function and column to be run.
62
     *
63
     * @var array
64
     */
65
    public $aggregate;
66
67
    /**
68
     * Indicates if the query returns distinct results.
69
     *
70
     * @var bool
71
     */
72
    public $distinct = false;
73
74
    /**
75
     * The table which the query is targeting.
76
     *
77
     * @var string
78
     */
79
    public $from;
80
81
    /**
82
     * The where constraints for the query.
83
     *
84
     * @var array
85
     */
86
    public $wheres;
87
88
    /**
89
     * The orderings for the query.
90
     *
91
     * @var array
92
     */
93
    public $orders;
94
95
    /**
96
     * The maximum number of records to return.
97
     *
98
     * @var int
99
     */
100
    public $limit;
101
102
    /**
103
     * An offset to define how manny entries should be skipped
104
     *
105
     * @var int
106
     */
107
    public $offset;
108
109
    /**
110
     * All of the available clause operators.
111
     *
112
     * @var array
113
     */
114
    protected $operators = [
115
        '=', '<', '>', '<=', '>=', '<>', '!=',
116
        'like', 'like binary', 'not like', 'between', 'ilike',
117
        '&', '|', '^', '<<', '>>',
118
        'rlike', 'regexp', 'not regexp',
119
        '~', '~*', '!~', '!~*', 'similar to',
120
        'not similar to', 'not ilike', '~~*', '!~~*',
121
    ];
122
123
    /**
124
     * Builder constructor.
125
     *
126
     * @param Connection $connection
127
     */
128
    public function __construct(Connection $connection)
129
    {
130
        $this->connection = $connection;
131
132
        $this->grammar = new Grammar();
133
    }
134
135
    /**
136
     * Add an array of where clauses to the query.
137
     *
138
     * @param  array  $column
139
     * @param  string  $boolean
140
     * @return $this
141
     */
142
    protected function addArrayOfWheres($column, string $boolean)
143
    {
144
        return $this->whereNested(function ($query) use ($column) {
145
146
            /** @var Builder $query */
147
            foreach ($column as $key => $value) {
148
                if (is_numeric($key) && is_array($value)) {
149
                    call_user_func_array([$query, 'where'], $value);
150
                } else {
151
                    $query->where($key, '=', $value);
152
                }
153
            }
154
        }, $boolean);
155
    }
156
157
    /**
158
     * Add an "or where" clause to the query.
159
     *
160
     * @param  string  $column
161
     * @param  string  $operator
162
     * @param  mixed   $value
163
     * @return Builder|static
164
     */
165
    public function orWhere($column, $operator = null, $value = null)
166
    {
167
        return $this->where($column, $operator, $value, 'or');
168
    }
169
170
    /**
171
     * Add an "and where" clause to the query.
172
     *
173
     * @param  string  $column
174
     * @param  string  $operator
175
     * @param  mixed   $value
176
     * @return Builder|static
177
     */
178
    public function andWhere($column, $operator = null, $value = null)
179
    {
180
        return $this->where($column, $operator, $value, 'and');
181
    }
182
183
    /**
184
     * Determine if the given operator and value combination is legal.
185
     *
186
     * @param  string  $operator
187
     * @param  mixed  $value
188
     * @return bool
189
     */
190
    protected function invalidOperatorAndValue($operator, $value)
191
    {
192
        $isOperator = in_array($operator, $this->operators);
193
194
        return $isOperator && $operator != '=' && is_null($value);
195
    }
196
197
    /**
198
     * Add a binding to the query.
199
     *
200
     * @param  mixed   $value
201
     * @param  string  $type
202
     * @return $this
203
     *
204
     * @throws InvalidArgumentException
205
     */
206
    public function addBinding($value, $type = 'where')
207
    {
208
        if (! array_key_exists($type, $this->bindings)) {
209
            throw new InvalidArgumentException("Invalid binding type: {$type}.");
210
        }
211
212
        if (is_array($value)) {
213
            $this->bindings[$type] = array_values(array_merge($this->bindings[$type], $value));
214
        } else {
215
            $this->bindings[$type][] = $value;
216
        }
217
218
        return $this;
219
    }
220
221
    /**
222
     * Create a raw database expression.
223
     *
224
     * @param  mixed  $value
225
     * @return Expression
226
     */
227
    public function raw($value)
228
    {
229
        return $this->connection->raw($value);
230
    }
231
232
    /**
233
     * Get the current query value bindings in a flattened array.
234
     *
235
     * @return array
236
     */
237
    public function getBindings()
238
    {
239
        return arr($this->bindings)->flatten();
240
    }
241
242
    /**
243
     * Set the columns to be selected.
244
     *
245
     * @param  array|mixed  $columns
246
     * @return $this
247
     */
248
    public function select($columns = ['*'])
249
    {
250
        $this->columns = is_array($columns) ? $columns : func_get_args();
251
252
        return $this;
253
    }
254
255
    /**
256
     * @param string $query
257
     * @param array $bindings
258
     * @return mixed
259
     */
260
    public function selectRaw(string $query, array $bindings = [])
261
    {
262
        return $this->connection->select($query, $bindings);
263
    }
264
265
    /**
266
     * @param array $values
267
     * @return bool
268
     */
269
    public function insert(array $values)
270
    {
271
        if (empty($values)) {
272
            return true;
273
        }
274
275
        if (! is_array(reset($values))) {
276
            $values = [$values];
277
        }
278
279
        // Here, we will sort the insert keys for every record so that each insert is
280
        // in the same order for the record. We need to make sure this is the case
281
        // so there are not any errors or problems when inserting these records.
282
        else {
283
            foreach ($values as $key => $value) {
284
                ksort($value);
285
                $values[$key] = $value;
286
            }
287
        }
288
289
        // We'll treat every insert like a batch insert so we can easily insert each
290
        // of the records into the database consistently. This will make it much
291
        // easier on the grammars to just handle one type of record insertion.
292
        $bindings = [];
293
294
        foreach ($values as $record) {
295
            foreach ($record as $value) {
296
                $bindings[] = $value;
297
            }
298
        }
299
300
        $sql = $this->grammar->compileInsert($this, $values);
301
302
        // Once we have compiled the insert statement's SQL we can execute it on the
303
        // connection and return a result as a boolean success indicator as that
304
        // is the same type of result returned by the raw connection instance.
305
        $bindings = $this->cleanBindings($bindings);
306
307
        return $this->connection->insert($sql, $bindings);
308
    }
309
310
    /**
311
     * Insert a new record and get the value of the primary key.
312
     *
313
     * @param array $values
314
     * @return int
315
     */
316
    public function insertGetId(array $values)
317
    {
318
        $sql = $this->grammar->compileInsertGetId($this, $values);
319
320
        $values = $this->cleanBindings($values);
321
322
        return $this->connection->processInsertGetId($sql, $values);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->connection...ertGetId($sql, $values) also could return the type string which is incompatible with the documented return type integer.
Loading history...
323
    }
324
325
    /**
326
     * Update a record in the database.
327
     *
328
     * @param array $values
329
     * @return int
330
     */
331
    public function update(array $values)
332
    {
333
        $bindings = array_values(array_merge($values, $this->getBindings()));
334
335
        $sql = $this->grammar->compileUpdate($this, $values);
336
337
        return $this->connection->update($sql, $this->cleanBindings($bindings));
338
    }
339
340
    /**
341
     * Increment a column's value by a given amount.
342
     *
343
     * @param string $column
344
     * @param int $amount
345
     * @return int
346
     */
347
    public function increment(string $column, int $amount = 1)
348
    {
349
        $wrapped = $this->grammar->wrap($column);
350
351
        $columns = array_merge([$column => $this->raw("$wrapped + $amount")]);
352
353
        return $this->update($columns);
354
    }
355
356
    /**
357
     * Decrement a column's value by a given amount.
358
     *
359
     * @param string $column
360
     * @param int $amount
361
     * @return int
362
     */
363
    public function decrement(string $column, int $amount = 1)
364
    {
365
        $wrapped = $this->grammar->wrap($column);
366
367
        $columns = array_merge([$column => $this->raw("$wrapped - $amount")]);
368
369
        return $this->update($columns);
370
    }
371
372
    /**
373
     * Delete a record from the database.
374
     *
375
     * @param  mixed  $id
376
     * @return int
377
     */
378
    public function delete($id = null)
379
    {
380
        // If an ID is passed to the method, we will set the where clause to check
381
        // the ID to allow developers to simply and quickly remove a single row
382
        // from their database without manually specifying the where clauses.
383
        if (! is_null($id)) {
384
            $this->where('id', '=', $id);
385
        }
386
387
        $sql = $this->grammar->compileDelete($this);
388
389
        return $this->connection->delete($sql, $this->getBindings());
390
    }
391
392
    /**
393
     * Run a truncate statement on the table.
394
     *
395
     * @return void
396
     */
397
    public function truncate()
398
    {
399
        foreach ($this->grammar->compileTruncate($this) as $sql => $bindings) {
400
            $this->connection->statement($sql, $bindings);
401
        }
402
    }
403
404
    /**
405
     * @param string $query
406
     * @param array $bindings
407
     * @return bool
408
     */
409
    public function insertRaw(string $query, array $bindings = [])
410
    {
411
        return $this->connection->insert($query, $bindings);
412
    }
413
414
    /**
415
     * @param string $query
416
     * @param array $bindings
417
     * @return int
418
     */
419
    public function updateRaw(string $query, array $bindings = [])
420
    {
421
        return $this->connection->update($query, $bindings);
422
    }
423
424
    /**
425
     * @param string $query
426
     * @param array $bindings
427
     * @return int
428
     */
429
    public function deleteRaw(string $query, array $bindings = [])
430
    {
431
        return $this->connection->delete($query, $bindings);
432
    }
433
434
    /**
435
     * Set the table which the query is targeting.
436
     *
437
     * @param  string  $table
438
     * @return $this
439
     */
440
    public function from($table)
441
    {
442
        $this->from = $table;
443
444
        return $this;
445
    }
446
447
    /**
448
     * Add a basic where clause to the query.
449
     *
450
     * @param  string|array  $column
451
     * @param  string  $operator
452
     * @param  mixed   $value
453
     * @param  string  $boolean
454
     * @return $this
455
     */
456
    public function where($column, $operator = "=", $value = null, $boolean = 'and')
457
    {
458
        // If the column is an array, we will assume it is an array of key-value pairs
459
        // and can add them each as a where clause. We will maintain the boolean we
460
        // received when the method was called and pass it into the nested where.
461
        if (is_array($column)) {
462
            return $this->addArrayOfWheres($column, $boolean);
463
        }
464
465
        // Here we will make some assumptions about the operator. If only 2 values are
466
        // passed to the method, we will assume that the operator is an equals sign
467
        // and keep going. Otherwise, we'll require the operator to be passed in.
468
        if (func_num_args() == 2) {
469
            list($value, $operator) = [$operator, '='];
470
        } elseif ($this->invalidOperatorAndValue($operator, $value)) {
471
            throw new InvalidArgumentException('Illegal operator and value combination.');
472
        }
473
474
        // If the given operator is not found in the list of valid operators we will
475
        // assume that the developer is just short-cutting the '=' operators and
476
        // we will set the operators to '=' and set the values appropriately.
477
        if (! in_array(strtolower($operator), $this->operators, true)) {
478
            list($value, $operator) = [$operator, '='];
479
        }
480
481
        // If the value is a Closure, it means the developer is performing an entire
482
        // sub-select within the query and we will need to compile the sub-select
483
        // within the where clause to get the appropriate query record results.
484
        if ($value instanceof Closure) {
485
            return $this->whereSub($column, $operator, $value, $boolean);
486
        }
487
488
        // If the value is "null", we will just assume the developer wants to add a
489
        // where null clause to the query. So, we will allow a short-cut here to
490
        // that method for convenience so the developer doesn't have to check.
491
        if (is_null($value)) {
492
            return $this->whereNull($column, $boolean, $operator != '=');
493
        }
494
495
        // Now that we are working with just a simple query we can put the elements
496
        // in our array and add the query binding to our array of bindings that
497
        // will be bound to each SQL statements when it is finally executed.
498
        $type = 'Basic';
499
500
        $this->wheres[] = compact('type', 'column', 'operator', 'value', 'boolean');
501
502
        if (! $value instanceof Expression) {
503
            $this->addBinding($value, 'where');
504
        }
505
506
        return $this;
507
    }
508
509
    /**
510
     * Add a where in statement to the query.
511
     *
512
     * @param $column
513
     * @param array $values
514
     * @param string $boolean
515
     * @param bool $not
516
     * @return $this
517
     */
518
    public function whereIn($column, array $values, $boolean = 'and', $not = false)
519
    {
520
        $type = 'in';
521
522
        $this->wheres[] = compact('column', 'type', 'boolean', 'not');
523
524
        $this->addBinding($values, 'where');
525
526
        return $this;
527
    }
528
529
    /**
530
     * Add a where not in statement to the query.
531
     *
532
     * @param $column
533
     * @param array $values
534
     * @param string $boolean
535
     * @return Builder
536
     */
537
    public function whereNotIn($column, array $values, $boolean = 'and')
538
    {
539
        return $this->whereIn($column, $values, $boolean, true);
540
    }
541
542
    /**
543
     * Add a or where in statement to the query.
544
     *
545
     * @param $column
546
     * @param array $values
547
     * @return Builder
548
     */
549
    public function orWhereIn($column, array $values)
550
    {
551
        return $this->whereIn($column, $values, 'or');
552
    }
553
554
    /**
555
     * Add a or where not in statement to the query.
556
     *
557
     * @param $column
558
     * @param array $values
559
     * @return Builder
560
     */
561
    public function orWhereNotIn($column, array $values)
562
    {
563
        return $this->whereIn($column, $values, 'or', true);
564
    }
565
566
    /**
567
     * Add a where between statement to the query.
568
     *
569
     * @param  string  $column
570
     * @param  array   $values
571
     * @param  string  $boolean
572
     * @param  bool  $not
573
     * @return $this
574
     */
575
    public function whereBetween($column, array $values, $boolean = 'and', $not = false)
576
    {
577
        $type = 'between';
578
579
        $this->wheres[] = compact('column', 'type', 'boolean', 'not');
580
581
        $this->addBinding($values, 'where');
582
583
        return $this;
584
    }
585
586
    /**
587
     * Add a or where between statement to the query.
588
     *
589
     * @param $column
590
     * @param array $values
591
     * @return Builder
592
     */
593
    public function orWhereBetween($column, array $values)
594
    {
595
        return $this->whereBetween($column, $values, 'or');
596
    }
597
598
    /**
599
     * Add a where not between statement to the query.
600
     *
601
     * @param $column
602
     * @param array $values
603
     * @param string $boolean
604
     * @return Builder
605
     */
606
    public function whereNotBetween($column, array $values, $boolean = 'and')
607
    {
608
        return $this->whereBetween($column, $values, $boolean, true);
609
    }
610
611
    /**
612
     * Add a or where not between statement to the query.
613
     *
614
     * @param $column
615
     * @param array $values
616
     * @return Builder
617
     */
618
    public function orWhereNotBetween($column, array $values)
619
    {
620
        return $this->whereBetween($column, $values, 'or', true);
621
    }
622
623
    /**
624
     * Add a nested where statement to the query.
625
     *
626
     * @param  Closure $callback
627
     * @param  string   $boolean
628
     * @return Builder|static
629
     */
630
    public function whereNested(Closure $callback, $boolean = 'and')
631
    {
632
        $query = $this->forNestedWhere();
633
634
        call_user_func($callback, $query);
635
636
        return $this->addNestedWhereQuery($query, $boolean);
637
    }
638
639
    /**
640
     * Create a new query instance for nested where condition.
641
     *
642
     * @return Builder
643
     */
644
    public function forNestedWhere()
645
    {
646
        $query = $this->newQuery();
647
648
        return $query->from($this->from);
649
    }
650
651
    /**
652
     * Add another query builder as a nested where to the query builder.
653
     *
654
     * @param  Builder|static $query
655
     * @param  string  $boolean
656
     * @return $this
657
     */
658
    public function addNestedWhereQuery($query, $boolean = 'and')
659
    {
660
        if (count($query->wheres)) {
661
            $type = 'Nested';
662
663
            $this->wheres[] = compact('type', 'query', 'boolean');
664
665
            $this->addBinding($query->getBindings(), 'where');
666
        }
667
668
        return $this;
669
    }
670
671
    /**
672
     * Add a full sub-select to the query.
673
     *
674
     * @param  string   $column
675
     * @param  string   $operator
676
     * @param  Closure $callback
677
     * @param  string   $boolean
678
     * @return $this
679
     */
680
    protected function whereSub($column, $operator, Closure $callback, $boolean)
681
    {
682
        $type = 'Sub';
683
684
        $query = $this->newQuery();
685
686
        // Once we have the query instance we can simply execute it so it can add all
687
        // of the sub-select's conditions to itself, and then we can cache it off
688
        // in the array of where clauses for the "main" parent query instance.
689
        call_user_func($callback, $query);
690
691
        $this->wheres[] = compact('type', 'column', 'operator', 'query', 'boolean');
692
693
        $this->addBinding($query->getBindings(), 'where');
694
695
        return $this;
696
    }
697
698
    /**
699
     * Add a "where null" clause to the query.
700
     *
701
     * @param  string  $column
702
     * @param  string  $boolean
703
     * @param  bool    $not
704
     * @return $this
705
     */
706
    public function whereNull($column, $boolean = 'and', $not = false)
707
    {
708
        $type = 'Null';
709
710
        $this->wheres[] = compact('type', 'column', 'boolean', 'not');
711
712
        return $this;
713
    }
714
715
    /**
716
     * Add an "or where null" clause to the query.
717
     *
718
     * @param  string  $column
719
     * @return Builder|static
720
     */
721
    public function orWhereNull($column)
722
    {
723
        return $this->whereNull($column, 'or');
724
    }
725
726
    /**
727
     * Add a "where not null" clause to the query.
728
     *
729
     * @param  string  $column
730
     * @param  string  $boolean
731
     * @return Builder|static
732
     */
733
    public function whereNotNull($column, $boolean = 'and')
734
    {
735
        return $this->whereNull($column, $boolean, true);
736
    }
737
738
    /**
739
     * Add an "or where not null" clause to the query.
740
     *
741
     * @param  string  $column
742
     * @return Builder|static
743
     */
744
    public function orWhereNotNull($column)
745
    {
746
        return $this->whereNull($column, 'or', true);
747
    }
748
749
    /**
750
     * Add a "where date" statement to the query.
751
     *
752
     * @param  string  $column
753
     * @param  string   $operator
754
     * @param  int   $value
755
     * @param  string   $boolean
756
     * @return Builder|static
757
     */
758
    public function whereDate($column, $operator, $value, $boolean = 'and')
759
    {
760
        return $this->addDateBasedWhere('Date', $column, $operator, $value, $boolean);
761
    }
762
763
    /**
764
     * Add an "or where date" statement to the query.
765
     *
766
     * @param  string  $column
767
     * @param  string   $operator
768
     * @param  int   $value
769
     * @return Builder|static
770
     */
771
    public function orWhereDate($column, $operator, $value)
772
    {
773
        return $this->whereDate($column, $operator, $value, 'or');
774
    }
775
776
    /**
777
     * Add a "where day" statement to the query.
778
     *
779
     * @param  string  $column
780
     * @param  string   $operator
781
     * @param  int   $value
782
     * @param  string   $boolean
783
     * @return Builder|static
784
     */
785
    public function whereDay($column, $operator, $value, $boolean = 'and')
786
    {
787
        return $this->addDateBasedWhere('Day', $column, $operator, $value, $boolean);
788
    }
789
790
    /**
791
     * Add a "where month" statement to the query.
792
     *
793
     * @param  string  $column
794
     * @param  string   $operator
795
     * @param  int   $value
796
     * @param  string   $boolean
797
     * @return Builder|static
798
     */
799
    public function whereMonth($column, $operator, $value, $boolean = 'and')
800
    {
801
        return $this->addDateBasedWhere('Month', $column, $operator, $value, $boolean);
802
    }
803
804
    /**
805
     * Add a "where year" statement to the query.
806
     *
807
     * @param  string  $column
808
     * @param  string   $operator
809
     * @param  int   $value
810
     * @param  string   $boolean
811
     * @return Builder|static
812
     */
813
    public function whereYear($column, $operator, $value, $boolean = 'and')
814
    {
815
        return $this->addDateBasedWhere('Year', $column, $operator, $value, $boolean);
816
    }
817
818
    /**
819
     * Add a date based (year, month, day) statement to the query.
820
     *
821
     * @param  string  $type
822
     * @param  string  $column
823
     * @param  string  $operator
824
     * @param  int  $value
825
     * @param  string  $boolean
826
     * @return $this
827
     */
828
    protected function addDateBasedWhere($type, $column, $operator, $value, $boolean = 'and')
829
    {
830
        $this->wheres[] = compact('column', 'type', 'boolean', 'operator', 'value');
831
832
        $this->addBinding($value, 'where');
833
834
        return $this;
835
    }
836
837
    /**
838
     * Add an "order by" clause to the query.
839
     *
840
     * @param  array|string  $column
841
     * @param  string        $direction asc or desc
842
     * @return $this
843
     */
844
    public function orderBy($column, $direction = 'asc')
845
    {
846
        if(!($direction == 'asc' or $direction == 'desc')) {
847
            handle(new Exception("Order by direction invalid: '".$direction."'"));
848
        }
849
850
        $column = is_array($column) ? $column : [$column];
851
852
        $this->orders = [
853
            $column,
854
            $direction
855
        ];
856
857
        return $this;
858
    }
859
860
    /**
861
     * @param $column
862
     * @return Builder
863
     */
864
    public function latest($column)
865
    {
866
        return $this->orderBy($column, "desc");
867
    }
868
869
    /**
870
     * @param string $column
871
     * @return Builder
872
     */
873
    public function oldest($column = "created_at")
874
    {
875
        return $this->orderBy($column, "desc");
876
    }
877
878
    /**
879
     * Retrieve the results in random order
880
     *
881
     * @return Builder
882
     */
883
    public function inRandomOrder()
884
    {
885
        return $this->orderBy("rand()");
886
    }
887
888
    /**
889
     * Execute the query as a "select" statement.
890
     *
891
     * @param array $columns
892
     * @return ArrayObject|null
893
     */
894
    public function get(array $columns = ['*'], $executeSQLWithoutSeprateBindings = false)
895
    {
896
        $this->columns = $columns;
897
898
        $sql = $executeSQLWithoutSeprateBindings
899
            ? $this->prepareRawQuery($this->toSql(), $this->getBindings())
900
            : $this->toSql();
901
902
        $result = $this->connection->select(
903
            $sql,
904
            $this->getBindings(),
905
            $this->from
906
        );
907
908
        // To fix issue #62
909
        // @link https://github.com/FramyFramework/Framy/issues/62
910
        $result->map(function ($item) {
911
            $item->exists = true;
912
        });
913
914
        return $result;
915
    }
916
917
    /**
918
     * Add an "limit" to the query
919
     *
920
     * @param int $limit
921
     * @return $this
922
     */
923
    public function limit(int $limit)
924
    {
925
        $this->limit = $limit;
926
927
        return $this;
928
    }
929
930
    /**
931
     * @param int $limit
932
     * @return $this
933
     */
934
    public function take(int $limit)
935
    {
936
        $this->limit($limit);
937
938
        return $this;
939
    }
940
941
    /**
942
     * Add an "offset" clause to the query.
943
     *
944
     * @param int $offset
945
     * @return $this
946
     */
947
    public function offset(int $offset)
948
    {
949
        $this->offset = $offset;
950
951
        return $this;
952
    }
953
954
    /**
955
     * @param int $offset
956
     * @return $this
957
     */
958
    public function skip(int $offset)
959
    {
960
        $this->offset($offset);
961
962
        return $this;
963
    }
964
965
    /**
966
     * Return only first result from database
967
     *
968
     * @param array $columns
969
     * @return Model
970
     */
971
    public function first(array $columns = ['*'])
972
    {
973
        $this->limit(1);
974
975
        return $this->get($columns)[0];
976
    }
977
978
    /**
979
     * To only get a single value
980
     *
981
     * @param $val
982
     * @return mixed
983
     */
984
    public function value(string $val)
985
    {
986
        return $this->first([$val])->$val;
987
    }
988
989
    /**
990
     * Retrieve the "count" result of the query.
991
     *
992
     * @param  string  $columns
993
     * @return int
994
     */
995
    public function count($columns = '*')
996
    {
997
        if (! is_array($columns)) {
0 ignored issues
show
introduced by
The condition is_array($columns) is always false.
Loading history...
998
            $columns = [$columns];
999
        }
1000
1001
        return (int) $this->aggregate(__FUNCTION__, $columns);
1002
    }
1003
1004
    /**
1005
     * Retrieve the minimum value of a given column.
1006
     *
1007
     * @param  string  $column
1008
     * @return float|int
1009
     */
1010
    public function min(string $column)
1011
    {
1012
        return $this->aggregate(__FUNCTION__, [$column]);
1013
    }
1014
1015
    /**
1016
     * Retrieve the maximum value of a given column.
1017
     *
1018
     * @param  string  $column
1019
     * @return float|int
1020
     */
1021
    public function max($column)
1022
    {
1023
        return $this->aggregate(__FUNCTION__, [$column]);
1024
    }
1025
1026
    /**
1027
     * Retrieve the sum of the values of a given column.
1028
     *
1029
     * @param  string  $column
1030
     * @return float|int
1031
     */
1032
    public function sum($column)
1033
    {
1034
        $result = $this->aggregate(__FUNCTION__, [$column]);
1035
1036
        return $result ?: 0;
1037
    }
1038
1039
    /**
1040
     * Retrieve the average of the values of a given column.
1041
     *
1042
     * @param  string  $column
1043
     * @return float|int
1044
     */
1045
    public function avg($column)
1046
    {
1047
        return $this->aggregate(__FUNCTION__, [$column]);
1048
    }
1049
1050
    /**
1051
     * Alias for the "avg" method.
1052
     *
1053
     * @param  string  $column
1054
     * @return float|int
1055
     */
1056
    public function average($column)
1057
    {
1058
        return $this->avg($column);
1059
    }
1060
1061
    /**
1062
     * Get a new instance of the query builder.
1063
     *
1064
     * @return Builder
1065
     */
1066
    public function newQuery()
1067
    {
1068
        return new static($this->connection);
1069
    }
1070
1071
    /**
1072
     * Determine if any rows exist for the current query.
1073
     *
1074
     * @return bool
1075
     */
1076
    public function exists()
1077
    {
1078
        $sql = $this->grammar->compileExists($this);
1079
1080
        $results = $this->connection->select($sql);
1081
1082
        if (isset($results[0])) {
1083
            $results = (array) $results[0];
1084
1085
            return (bool) $results['exists'];
1086
        }
1087
1088
        return false;
1089
    }
1090
1091
    /**
1092
     * Determine if any rows does not exist for the current query.
1093
     *
1094
     * @return bool
1095
     */
1096
    public function doesntExist()
1097
    {
1098
        return ! $this->exists();
1099
    }
1100
1101
    /**
1102
     * Execute an aggregate function on the database.
1103
     *
1104
     * @param  string  $function
1105
     * @param  array   $columns
1106
     * @return float|int
1107
     */
1108
    public function aggregate($function, $columns = ['*'])
1109
    {
1110
        $this->aggregate = compact('function', 'columns');
1111
1112
        $previousColumns = $this->columns;
1113
1114
        $results = $this->get($columns);
1115
1116
        // Once we have executed the query, we will reset the aggregate property so
1117
        // that more select queries can be executed against the database without
1118
        // the aggregate value getting in the way when the grammar builds it.
1119
        $this->aggregate = null;
1120
1121
        $this->columns = $previousColumns;
1122
1123
        if (isset($results[0])) {
1124
            $result = array_change_key_case((array) $results[0]);
1125
1126
            return $result['aggregate'];
1127
        }
1128
    }
1129
1130
    /**
1131
     * Check if the operator is in the list of valid operators.
1132
     * Returns true if it is.
1133
     *
1134
     * @deprecated Will be removed with next major
1135
     * @param $operatorToCheck
1136
     * @return bool
1137
     */
1138
    private function isOperatorValid($operatorToCheck)
0 ignored issues
show
Unused Code introduced by
The method isOperatorValid() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
1139
    {
1140
        foreach ($this->operators as $operator) {
1141
            if($operatorToCheck === $operator) {
1142
                return true;
1143
            }
1144
        }
1145
1146
        return false;
1147
    }
1148
1149
    /**
1150
     * Get the SQL representation of the query.
1151
     *
1152
     * @return string
1153
     */
1154
    private function toSql()
1155
    {
1156
        return $this->grammar->compileSelect($this);
1157
    }
1158
1159
    /**
1160
     * Remove all of the expressions from a list of bindings.
1161
     *
1162
     * @param  array  $bindings
1163
     * @return array
1164
     */
1165
    protected function cleanBindings(array $bindings)
1166
    {
1167
        return array_values(array_filter($bindings, function ($binding) {
1168
            return ! $binding instanceof Expression;
1169
        }));
1170
    }
1171
1172
    /**
1173
     * Switch values in query with value fitting in binding
1174
     *
1175
     * @param string $query    The query to prepare
1176
     * @param array $bindings  Array of the bindings
1177
     * @return string          The run ready query
1178
     */
1179
    protected function prepareRawQuery(string $query, array $bindings): string
1180
    {
1181
        // search for values in query
1182
        preg_match_all("/:([^ ]*)/", $query,$values);
1183
1184
        // replace values with bindings
1185
        foreach ($values[1] as $value) {
1186
            // check if fitting binding exists
1187
            if(array_key_exists($value, $bindings)) {
1188
                $query = str_replace(":".$value, $bindings[$value], $query);
1189
            } else {
1190
                handle(new Exception("Could not find fitting value '$value' in bindings for query: '$query'"));
1191
            }
1192
        }
1193
1194
        // to handle "?" markers in query
1195
        foreach ($bindings as $binding) {
1196
            $query = (preg_replace('/'.preg_quote("?", '/').'/', $binding, $query, 1));
1197
        }
1198
1199
        return $query;
1200
    }
1201
}
1202