Completed
Push — master ( 818c2f...233141 )
by Arjay
05:20
created

BaseEngine::withTrashed()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Yajra\Datatables\Engines;
4
5
use Illuminate\Http\JsonResponse;
6
use Illuminate\Support\Facades\Config;
7
use Illuminate\Support\Str;
8
use League\Fractal\Resource\Collection;
9
use Yajra\Datatables\Contracts\DataTableEngineContract;
10
use Yajra\Datatables\Helper;
11
use Yajra\Datatables\Processors\DataProcessor;
12
13
/**
14
 * Class BaseEngine.
15
 *
16
 * @package Yajra\Datatables\Engines
17
 * @author  Arjay Angeles <[email protected]>
18
 */
19
abstract class BaseEngine implements DataTableEngineContract
20
{
21
    /**
22
     * Datatables Request object.
23
     *
24
     * @var \Yajra\Datatables\Request
25
     */
26
    public $request;
27
28
    /**
29
     * Database connection used.
30
     *
31
     * @var \Illuminate\Database\Connection
32
     */
33
    protected $connection;
34
35
    /**
36
     * Builder object.
37
     *
38
     * @var \Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder
39
     */
40
    protected $query;
41
42
    /**
43
     * Query builder object.
44
     *
45
     * @var \Illuminate\Database\Query\Builder
46
     */
47
    protected $builder;
48
49
    /**
50
     * Array of result columns/fields.
51
     *
52
     * @var array
53
     */
54
    protected $columns = [];
55
56
    /**
57
     * DT columns definitions container (add/edit/remove/filter/order/escape).
58
     *
59
     * @var array
60
     */
61
    protected $columnDef = [
62
        'index'     => false,
63
        'append'    => [],
64
        'edit'      => [],
65
        'excess'    => ['rn', 'row_num'],
66
        'filter'    => [],
67
        'order'     => [],
68
        'escape'    => [],
69
        'blacklist' => ['password', 'remember_token'],
70
        'whitelist' => '*',
71
    ];
72
73
    /**
74
     * Query type.
75
     *
76
     * @var string
77
     */
78
    protected $query_type;
79
80
    /**
81
     * Extra/Added columns.
82
     *
83
     * @var array
84
     */
85
    protected $extraColumns = [];
86
87
    /**
88
     * Total records.
89
     *
90
     * @var int
91
     */
92
    protected $totalRecords = 0;
93
94
    /**
95
     * Total filtered records.
96
     *
97
     * @var int
98
     */
99
    protected $filteredRecords = 0;
100
101
    /**
102
     * Auto-filter flag.
103
     *
104
     * @var bool
105
     */
106
    protected $autoFilter = true;
107
    
108
    /**
109
     * Select trashed records in count function for models with soft deletes trait.
110
     * By default we do not select soft deleted records
111
     *
112
     * @var bool
113
     */
114
    protected $withTrashed = false;
115
116
    /**
117
     * Callback to override global search.
118
     *
119
     * @var \Closure
120
     */
121
    protected $filterCallback;
122
123
    /**
124
     * Parameters to passed on filterCallback.
125
     *
126
     * @var mixed
127
     */
128
    protected $filterCallbackParameters;
129
130
    /**
131
     * DT row templates container.
132
     *
133
     * @var array
134
     */
135
    protected $templates = [
136
        'DT_RowId'    => '',
137
        'DT_RowClass' => '',
138
        'DT_RowData'  => [],
139
        'DT_RowAttr'  => [],
140
    ];
141
142
    /**
143
     * Output transformer.
144
     *
145
     * @var \League\Fractal\TransformerAbstract
146
     */
147
    protected $transformer = null;
148
149
    /**
150
     * Database prefix
151
     *
152
     * @var string
153
     */
154
    protected $prefix;
155
156
    /**
157
     * Database driver used.
158
     *
159
     * @var string
160
     */
161
    protected $database;
162
163
    /**
164
     * [internal] Track if any filter was applied for at least one column
165
     *
166
     * @var boolean
167
     */
168
    protected $isFilterApplied = false;
169
170
    /**
171
     * Fractal serializer class.
172
     *
173
     * @var string|null
174
     */
175
    protected $serializer = null;
176
177
    /**
178
     * Custom ordering callback.
179
     *
180
     * @var \Closure
181
     */
182
    protected $orderCallback;
183
184
    /**
185
     * Skip paginate as needed.
186
     *
187
     * @var bool
188
     */
189
    protected $skipPaging = false;
190
191
    /**
192
     * Array of data to append on json response.
193
     *
194
     * @var array
195
     */
196
    private $appends = [];
197
198
    /**
199
     * Setup search keyword.
200
     *
201
     * @param  string $value
202
     * @return string
203
     */
204
    public function setupKeyword($value)
205
    {
206
        if ($this->isSmartSearch()) {
207
            $keyword = '%' . $value . '%';
208
            if ($this->isWildcard()) {
209
                $keyword = $this->wildcardLikeString($value);
210
            }
211
            // remove escaping slash added on js script request
212
            $keyword = str_replace('\\', '%', $keyword);
213
214
            return $keyword;
215
        }
216
217
        return $value;
218
    }
219
220
    /**
221
     * Check if DataTables uses smart search.
222
     *
223
     * @return bool
224
     */
225
    protected function isSmartSearch()
226
    {
227
        return Config::get('datatables.search.smart', true);
228
    }
229
230
    /**
231
     * Get config use wild card status.
232
     *
233
     * @return bool
234
     */
235
    public function isWildcard()
236
    {
237
        return Config::get('datatables.search.use_wildcards', false);
238
    }
239
240
    /**
241
     * Adds % wildcards to the given string.
242
     *
243
     * @param string $str
244
     * @param bool $lowercase
245
     * @return string
246
     */
247
    public function wildcardLikeString($str, $lowercase = true)
248
    {
249
        $wild   = '%';
250
        $length = Str::length($str);
251
        if ($length) {
252
            for ($i = 0; $i < $length; $i++) {
253
                $wild .= $str[$i] . '%';
254
            }
255
        }
256
        if ($lowercase) {
257
            $wild = Str::lower($wild);
258
        }
259
260
        return $wild;
261
    }
262
263
    /**
264
     * Will prefix column if needed.
265
     *
266
     * @param string $column
267
     * @return string
268
     */
269
    public function prefixColumn($column)
270
    {
271
        $table_names = $this->tableNames();
272
        if (count(
273
            array_filter($table_names, function ($value) use (&$column) {
274
                return strpos($column, $value . '.') === 0;
275
            })
276
        )) {
277
            // the column starts with one of the table names
278
            $column = $this->prefix . $column;
279
        }
280
281
        return $column;
282
    }
283
284
    /**
285
     * Will look through the query and all it's joins to determine the table names.
286
     *
287
     * @return array
288
     */
289
    public function tableNames()
290
    {
291
        $names          = [];
292
        $query          = $this->getQueryBuilder();
293
        $names[]        = $query->from;
294
        $joins          = $query->joins ?: [];
295
        $databasePrefix = $this->prefix;
296
        foreach ($joins as $join) {
297
            $table   = preg_split('/ as /i', $join->table);
298
            $names[] = $table[0];
299
            if (isset($table[1]) && ! empty($databasePrefix) && strpos($table[1], $databasePrefix) == 0) {
300
                $names[] = preg_replace('/^' . $databasePrefix . '/', '', $table[1]);
301
            }
302
        }
303
304
        return $names;
305
    }
306
307
    /**
308
     * Get Query Builder object.
309
     *
310
     * @param mixed $instance
311
     * @return mixed
312
     */
313
    public function getQueryBuilder($instance = null)
314
    {
315
        if (! $instance) {
316
            $instance = $this->query;
317
        }
318
319
        if ($this->isQueryBuilder()) {
320
            return $instance;
321
        }
322
323
        return $instance->getQuery();
324
    }
325
326
    /**
327
     * Check query type is a builder.
328
     *
329
     * @return bool
330
     */
331
    public function isQueryBuilder()
332
    {
333
        return $this->query_type == 'builder';
334
    }
335
336
    /**
337
     * Add column in collection.
338
     *
339
     * @param string $name
340
     * @param string|callable $content
341
     * @param bool|int $order
342
     * @return $this
343
     */
344
    public function addColumn($name, $content, $order = false)
345
    {
346
        $this->extraColumns[] = $name;
347
348
        $this->columnDef['append'][] = ['name' => $name, 'content' => $content, 'order' => $order];
349
350
        return $this;
351
    }
352
353
    /**
354
     * Add DT row index column on response.
355
     *
356
     * @return $this
357
     */
358
    public function addIndexColumn()
359
    {
360
        $this->columnDef['index'] = true;
361
362
        return $this;
363
    }
364
365
    /**
366
     * Edit column's content.
367
     *
368
     * @param string $name
369
     * @param string|callable $content
370
     * @return $this
371
     */
372
    public function editColumn($name, $content)
373
    {
374
        $this->columnDef['edit'][] = ['name' => $name, 'content' => $content];
375
376
        return $this;
377
    }
378
379
    /**
380
     * Remove column from collection.
381
     *
382
     * @return $this
383
     */
384
    public function removeColumn()
385
    {
386
        $names                     = func_get_args();
387
        $this->columnDef['excess'] = array_merge($this->columnDef['excess'], $names);
388
389
        return $this;
390
    }
391
392
    /**
393
     * Declare columns to escape values.
394
     *
395
     * @param string|array $columns
396
     * @return $this
397
     */
398
    public function escapeColumns($columns = '*')
399
    {
400
        $this->columnDef['escape'] = $columns;
401
402
        return $this;
403
    }
404
    
405
    /**
406
     * Change withTrashed flag value.
407
     *
408
     * @param bool $withTrashed
409
     * @return $this
410
     */
411
    public function withTrashed($withTrashed = true)
412
    {
413
        $this->withTrashed = $withTrashed;
414
        
415
        return $this;
416
    }
417
418
    /**
419
     * Allows previous API calls where the methods were snake_case.
420
     * Will convert a camelCase API call to a snake_case call.
421
     * Allow query builder method to be used by the engine.
422
     *
423
     * @param  string $name
424
     * @param  array $arguments
425
     * @return mixed
426
     */
427
    public function __call($name, $arguments)
428
    {
429
        $name = Str::camel(Str::lower($name));
430
        if (method_exists($this, $name)) {
431
            return call_user_func_array([$this, $name], $arguments);
432
        } elseif (method_exists($this->getQueryBuilder(), $name)) {
433
            call_user_func_array([$this->getQueryBuilder(), $name], $arguments);
434
        } else {
435
            trigger_error('Call to undefined method ' . __CLASS__ . '::' . $name . '()', E_USER_ERROR);
436
        }
437
438
        return $this;
439
    }
440
441
    /**
442
     * Sets DT_RowClass template.
443
     * result: <tr class="output_from_your_template">.
444
     *
445
     * @param string|callable $content
446
     * @return $this
447
     */
448
    public function setRowClass($content)
449
    {
450
        $this->templates['DT_RowClass'] = $content;
451
452
        return $this;
453
    }
454
455
    /**
456
     * Sets DT_RowId template.
457
     * result: <tr id="output_from_your_template">.
458
     *
459
     * @param string|callable $content
460
     * @return $this
461
     */
462
    public function setRowId($content)
463
    {
464
        $this->templates['DT_RowId'] = $content;
465
466
        return $this;
467
    }
468
469
    /**
470
     * Set DT_RowData templates.
471
     *
472
     * @param array $data
473
     * @return $this
474
     */
475
    public function setRowData(array $data)
476
    {
477
        $this->templates['DT_RowData'] = $data;
478
479
        return $this;
480
    }
481
482
    /**
483
     * Add DT_RowData template.
484
     *
485
     * @param string $key
486
     * @param string|callable $value
487
     * @return $this
488
     */
489
    public function addRowData($key, $value)
490
    {
491
        $this->templates['DT_RowData'][$key] = $value;
492
493
        return $this;
494
    }
495
496
    /**
497
     * Set DT_RowAttr templates.
498
     * result: <tr attr1="attr1" attr2="attr2">.
499
     *
500
     * @param array $data
501
     * @return $this
502
     */
503
    public function setRowAttr(array $data)
504
    {
505
        $this->templates['DT_RowAttr'] = $data;
506
507
        return $this;
508
    }
509
510
    /**
511
     * Add DT_RowAttr template.
512
     *
513
     * @param string $key
514
     * @param string|callable $value
515
     * @return $this
516
     */
517
    public function addRowAttr($key, $value)
518
    {
519
        $this->templates['DT_RowAttr'][$key] = $value;
520
521
        return $this;
522
    }
523
524
    /**
525
     * Override default column filter search.
526
     *
527
     * @param string $column
528
     * @param string|Closure $method
529
     * @return $this
530
     * @internal param $mixed ...,... All the individual parameters required for specified $method
531
     * @internal string $1 Special variable that returns the requested search keyword.
532
     */
533
    public function filterColumn($column, $method)
534
    {
535
        $params                             = func_get_args();
536
        $this->columnDef['filter'][$column] = ['method' => $method, 'parameters' => array_splice($params, 2)];
537
538
        return $this;
539
    }
540
541
    /**
542
     * Order each given columns versus the given custom sql.
543
     *
544
     * @param array $columns
545
     * @param string $sql
546
     * @param array $bindings
547
     * @return $this
548
     */
549
    public function orderColumns(array $columns, $sql, $bindings = [])
550
    {
551
        foreach ($columns as $column) {
552
            $this->orderColumn($column, str_replace(':column', $column, $sql), $bindings);
553
        }
554
555
        return $this;
556
    }
557
558
    /**
559
     * Override default column ordering.
560
     *
561
     * @param string $column
562
     * @param string $sql
563
     * @param array $bindings
564
     * @return $this
565
     * @internal string $1 Special variable that returns the requested order direction of the column.
566
     */
567
    public function orderColumn($column, $sql, $bindings = [])
568
    {
569
        $this->columnDef['order'][$column] = ['method' => 'orderByRaw', 'parameters' => [$sql, $bindings]];
570
571
        return $this;
572
    }
573
574
    /**
575
     * Set data output transformer.
576
     *
577
     * @param \League\Fractal\TransformerAbstract $transformer
578
     * @return $this
579
     */
580
    public function setTransformer($transformer)
581
    {
582
        $this->transformer = $transformer;
583
584
        return $this;
585
    }
586
587
    /**
588
     * Set fractal serializer class.
589
     *
590
     * @param string $serializer
591
     * @return $this
592
     */
593
    public function setSerializer($serializer)
594
    {
595
        $this->serializer = $serializer;
596
597
        return $this;
598
    }
599
600
    /**
601
     * Organizes works.
602
     *
603
     * @param bool $mDataSupport
604
     * @param bool $orderFirst
605
     * @return \Illuminate\Http\JsonResponse
606
     */
607
    public function make($mDataSupport = false, $orderFirst = false)
608
    {
609
        $this->totalRecords = $this->totalCount();
610
611
        if ($this->totalRecords) {
612
            $this->orderRecords(! $orderFirst);
613
            $this->filterRecords();
614
            $this->orderRecords($orderFirst);
615
            $this->paginate();
616
        }
617
618
        return $this->render($mDataSupport);
619
    }
620
621
    /**
622
     * Sort records.
623
     *
624
     * @param  boolean $skip
625
     * @return void
626
     */
627
    public function orderRecords($skip)
628
    {
629
        if (! $skip) {
630
            $this->ordering();
631
        }
632
    }
633
634
    /**
635
     * Perform necessary filters.
636
     *
637
     * @return void
638
     */
639
    public function filterRecords()
640
    {
641
        if ($this->autoFilter && $this->request->isSearchable()) {
642
            $this->filtering();
643
        }
644
645
        if (is_callable($this->filterCallback)) {
646
            call_user_func($this->filterCallback, $this->filterCallbackParameters);
647
        }
648
649
        $this->columnSearch();
650
        $this->filteredRecords = $this->isFilterApplied ? $this->count() : $this->totalRecords;
651
    }
652
653
    /**
654
     * Apply pagination.
655
     *
656
     * @return void
657
     */
658
    public function paginate()
659
    {
660
        if ($this->request->isPaginationable() && ! $this->skipPaging) {
661
            $this->paging();
662
        }
663
    }
664
665
    /**
666
     * Render json response.
667
     *
668
     * @param bool $object
669
     * @return \Illuminate\Http\JsonResponse
670
     */
671
    public function render($object = false)
672
    {
673
        $output = array_merge([
674
            'draw'            => (int) $this->request['draw'],
675
            'recordsTotal'    => $this->totalRecords,
676
            'recordsFiltered' => $this->filteredRecords,
677
        ], $this->appends);
678
679
        if (isset($this->transformer)) {
680
            $fractal = app('datatables.fractal');
681
682
            if ($this->serializer) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->serializer of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
683
                $fractal->setSerializer(new $this->serializer);
684
            }
685
686
            //Get transformer reflection
687
            //Firs method parameter should be data/object to transform
688
            $reflection = new \ReflectionMethod($this->transformer, 'transform');
689
            $parameter  = $reflection->getParameters()[0];
690
691
            //If parameter is class assuming it requires object
692
            //Else just pass array by default
693
            if ($parameter->getClass()) {
694
                $resource = new Collection($this->results(), $this->createTransformer());
695
            } else {
696
                $resource = new Collection(
697
                    $this->getProcessedData($object),
698
                    $this->createTransformer()
699
                );
700
            }
701
702
            $collection     = $fractal->createData($resource)->toArray();
703
            $output['data'] = $collection['data'];
704
        } else {
705
            $output['data'] = Helper::transform($this->getProcessedData($object));
706
        }
707
708
        if ($this->isDebugging()) {
709
            $output = $this->showDebugger($output);
710
        }
711
712
        return new JsonResponse($output);
713
    }
714
715
    /**
716
     * Get or create transformer instance.
717
     *
718
     * @return \League\Fractal\TransformerAbstract
719
     */
720
    protected function createTransformer()
721
    {
722
        if ($this->transformer instanceof \League\Fractal\TransformerAbstract) {
723
            return $this->transformer;
724
        }
725
726
        return new $this->transformer();
727
    }
728
729
    /**
730
     * Get processed data
731
     *
732
     * @param bool|false $object
733
     * @return array
734
     */
735
    private function getProcessedData($object = false)
736
    {
737
        $processor = new DataProcessor(
738
            $this->results(),
739
            $this->columnDef,
740
            $this->templates,
741
            $this->request['start']
742
        );
743
744
        return $processor->process($object);
745
    }
746
747
    /**
748
     * Check if app is in debug mode.
749
     *
750
     * @return bool
751
     */
752
    public function isDebugging()
753
    {
754
        return Config::get('app.debug', false);
755
    }
756
757
    /**
758
     * Append debug parameters on output.
759
     *
760
     * @param  array $output
761
     * @return array
762
     */
763
    public function showDebugger(array $output)
764
    {
765
        $output['queries'] = $this->connection->getQueryLog();
766
        $output['input']   = $this->request->all();
767
768
        return $output;
769
    }
770
771
    /**
772
     * Update flags to disable global search
773
     *
774
     * @param  \Closure $callback
775
     * @param  mixed $parameters
776
     * @param  bool $autoFilter
777
     */
778
    public function overrideGlobalSearch(\Closure $callback, $parameters, $autoFilter = false)
779
    {
780
        $this->autoFilter               = $autoFilter;
781
        $this->isFilterApplied          = true;
782
        $this->filterCallback           = $callback;
783
        $this->filterCallbackParameters = $parameters;
784
    }
785
786
    /**
787
     * Get config is case insensitive status.
788
     *
789
     * @return bool
790
     */
791
    public function isCaseInsensitive()
792
    {
793
        return Config::get('datatables.search.case_insensitive', false);
794
    }
795
796
    /**
797
     * Append data on json response.
798
     *
799
     * @param mixed $key
800
     * @param mixed $value
801
     * @return $this
802
     */
803
    public function with($key, $value = '')
804
    {
805
        if (is_array($key)) {
806
            $this->appends = $key;
807
        } elseif (is_callable($value)) {
808
            $this->appends[$key] = value($value);
809
        } else {
810
            $this->appends[$key] = value($value);
811
        }
812
813
        return $this;
814
    }
815
816
    /**
817
     * Override default ordering method with a closure callback.
818
     *
819
     * @param \Closure $closure
820
     * @return $this
821
     */
822
    public function order(\Closure $closure)
823
    {
824
        $this->orderCallback = $closure;
825
826
        return $this;
827
    }
828
829
    /**
830
     * Update list of columns that is not allowed for search/sort.
831
     *
832
     * @param  array $blacklist
833
     * @return $this
834
     */
835
    public function blacklist(array $blacklist)
836
    {
837
        $this->columnDef['blacklist'] = $blacklist;
838
839
        return $this;
840
    }
841
842
    /**
843
     * Update list of columns that is not allowed for search/sort.
844
     *
845
     * @param  string|array $whitelist
846
     * @return $this
847
     */
848
    public function whitelist($whitelist = '*')
849
    {
850
        $this->columnDef['whitelist'] = $whitelist;
851
852
        return $this;
853
    }
854
855
    /**
856
     * Set smart search config at runtime.
857
     *
858
     * @param bool $bool
859
     * @return $this
860
     */
861
    public function smart($bool = true)
862
    {
863
        Config::set('datatables.search.smart', $bool);
864
865
        return $this;
866
    }
867
868
    /**
869
     * Set total records manually.
870
     *
871
     * @param int $total
872
     * @return $this
873
     */
874
    public function setTotalRecords($total)
875
    {
876
        $this->totalRecords = $total;
877
878
        return $this;
879
    }
880
881
    /**
882
     * Skip pagination as needed.
883
     */
884
    public function skipPaging()
885
    {
886
        $this->skipPaging = true;
887
888
        return $this;
889
    }
890
891
    /**
892
     * Check if column is blacklisted.
893
     *
894
     * @param string $column
895
     * @return bool
896
     */
897
    protected function isBlacklisted($column)
898
    {
899
        if (in_array($column, $this->columnDef['blacklist'])) {
900
            return true;
901
        }
902
903
        if ($this->columnDef['whitelist'] === '*' || in_array($column, $this->columnDef['whitelist'])) {
904
            return false;
905
        }
906
907
        return true;
908
    }
909
910
    /**
911
     * Get column name to be use for filtering and sorting.
912
     *
913
     * @param integer $index
914
     * @param bool $wantsAlias
915
     * @return string
916
     */
917
    protected function getColumnName($index, $wantsAlias = false)
918
    {
919
        $column = $this->request->columnName($index);
920
921
        // DataTables is using make(false)
922
        if (is_numeric($column)) {
923
            $column = $this->getColumnNameByIndex($index);
924
        }
925
926
        if (Str::contains(Str::upper($column), ' AS ')) {
927
            $column = $this->extractColumnName($column, $wantsAlias);
928
        }
929
930
        return $column;
931
    }
932
933
    /**
934
     * Get column name by order column index.
935
     *
936
     * @param int $index
937
     * @return mixed
938
     */
939
    protected function getColumnNameByIndex($index)
940
    {
941
        $name = isset($this->columns[$index]) && $this->columns[$index] <> '*' ? $this->columns[$index] : $this->getPrimaryKeyName();
942
943
        return in_array($name, $this->extraColumns, true) ? $this->getPrimaryKeyName() : $name;
944
    }
945
946
    /**
947
     * If column name could not be resolved then use primary key.
948
     *
949
     * @return string
950
     */
951
    protected function getPrimaryKeyName()
952
    {
953
        if ($this->isEloquent()) {
954
            return $this->query->getModel()->getKeyName();
0 ignored issues
show
Bug introduced by
The method getModel does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Query\Builder.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
955
        }
956
957
        return 'id';
958
    }
959
960
    /**
961
     * Check if the engine used was eloquent.
962
     *
963
     * @return bool
964
     */
965
    protected function isEloquent()
966
    {
967
        return $this->query_type === 'eloquent';
968
    }
969
970
    /**
971
     * Get column name from string.
972
     *
973
     * @param string $str
974
     * @param bool $wantsAlias
975
     * @return string
976
     */
977
    protected function extractColumnName($str, $wantsAlias)
978
    {
979
        $matches = explode(' as ', Str::lower($str));
980
981
        if (! empty($matches)) {
982
            if ($wantsAlias) {
983
                return array_pop($matches);
984
            } else {
985
                return array_shift($matches);
986
            }
987
        } elseif (strpos($str, '.')) {
988
            $array = explode('.', $str);
989
990
            return array_pop($array);
991
        }
992
993
        return $str;
994
    }
995
996
    /**
997
     * Check if the current sql language is based on oracle syntax.
998
     *
999
     * @return bool
1000
     */
1001
    protected function isOracleSql()
1002
    {
1003
        return in_array($this->database, ['oracle', 'oci8']);
1004
    }
1005
}
1006