Completed
Push — master ( 1f1519...e513b7 )
by Arjay
02:02
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
     * Callback to override global search.
110
     *
111
     * @var callable
112
     */
113
    protected $filterCallback;
114
115
    /**
116
     * Parameters to passed on filterCallback.
117
     *
118
     * @var mixed
119
     */
120
    protected $filterCallbackParameters;
121
122
    /**
123
     * DT row templates container.
124
     *
125
     * @var array
126
     */
127
    protected $templates = [
128
        'DT_RowId'    => '',
129
        'DT_RowClass' => '',
130
        'DT_RowData'  => [],
131
        'DT_RowAttr'  => [],
132
    ];
133
134
    /**
135
     * Output transformer.
136
     *
137
     * @var \League\Fractal\TransformerAbstract
138
     */
139
    protected $transformer = null;
140
141
    /**
142
     * Database prefix
143
     *
144
     * @var string
145
     */
146
    protected $prefix;
147
148
    /**
149
     * Database driver used.
150
     *
151
     * @var string
152
     */
153
    protected $database;
154
155
    /**
156
     * [internal] Track if any filter was applied for at least one column
157
     *
158
     * @var boolean
159
     */
160
    protected $isFilterApplied = false;
161
162
    /**
163
     * Fractal serializer class.
164
     *
165
     * @var string|null
166
     */
167
    protected $serializer = null;
168
169
    /**
170
     * Custom ordering callback.
171
     *
172
     * @var callable
173
     */
174
    protected $orderCallback;
175
176
    /**
177
     * Skip paginate as needed.
178
     *
179
     * @var bool
180
     */
181
    protected $skipPaging = false;
182
183
    /**
184
     * Array of data to append on json response.
185
     *
186
     * @var array
187
     */
188
    protected $appends = [];
189
190
    /**
191
     * Flag for ordering NULLS LAST option.
192
     *
193
     * @var bool
194
     */
195
    protected $nullsLast = false;
196
197
    /**
198
     * Add column in collection.
199
     *
200
     * @param string $name
201
     * @param string|callable $content
202
     * @param bool|int $order
203
     * @return $this
204
     */
205
    public function addColumn($name, $content, $order = false)
206
    {
207
        $this->extraColumns[] = $name;
208
209
        $this->columnDef['append'][] = ['name' => $name, 'content' => $content, 'order' => $order];
210
211
        return $this;
212
    }
213
214
    /**
215
     * Add DT row index column on response.
216
     *
217
     * @return $this
218
     */
219
    public function addIndexColumn()
220
    {
221
        $this->columnDef['index'] = true;
222
223
        return $this;
224
    }
225
226
    /**
227
     * Edit column's content.
228
     *
229
     * @param string $name
230
     * @param string|callable $content
231
     * @return $this
232
     */
233
    public function editColumn($name, $content)
234
    {
235
        $this->columnDef['edit'][] = ['name' => $name, 'content' => $content];
236
237
        return $this;
238
    }
239
240
    /**
241
     * Remove column from collection.
242
     *
243
     * @return $this
244
     */
245
    public function removeColumn()
246
    {
247
        $names                     = func_get_args();
248
        $this->columnDef['excess'] = array_merge($this->columnDef['excess'], $names);
249
250
        return $this;
251
    }
252
253
    /**
254
     * Declare columns to escape values.
255
     *
256
     * @param string|array $columns
257
     * @return $this
258
     */
259
    public function escapeColumns($columns = '*')
260
    {
261
        $this->columnDef['escape'] = $columns;
262
263
        return $this;
264
    }
265
266
    /**
267
     * Allows previous API calls where the methods were snake_case.
268
     * Will convert a camelCase API call to a snake_case call.
269
     * Allow query builder method to be used by the engine.
270
     *
271
     * @param  string $name
272
     * @param  array $arguments
273
     * @return mixed
274
     */
275
    public function __call($name, $arguments)
276
    {
277
        $name = Str::camel(Str::lower($name));
278
        if (method_exists($this, $name)) {
279
            return call_user_func_array([$this, $name], $arguments);
280
        } elseif (method_exists($this->getQueryBuilder(), $name)) {
281
            call_user_func_array([$this->getQueryBuilder(), $name], $arguments);
282
        } else {
283
            trigger_error('Call to undefined method ' . __CLASS__ . '::' . $name . '()', E_USER_ERROR);
284
        }
285
286
        return $this;
287
    }
288
289
    /**
290
     * Get Query Builder object.
291
     *
292
     * @param mixed $instance
293
     * @return mixed
294
     */
295
    public function getQueryBuilder($instance = null)
296
    {
297
        if (! $instance) {
298
            $instance = $this->query;
299
        }
300
301
        if ($this->isQueryBuilder()) {
302
            return $instance;
303
        }
304
305
        return $instance->getQuery();
306
    }
307
308
    /**
309
     * Check query type is a builder.
310
     *
311
     * @return bool
312
     */
313
    public function isQueryBuilder()
314
    {
315
        return $this->query_type == 'builder';
316
    }
317
318
    /**
319
     * Sets DT_RowClass template.
320
     * result: <tr class="output_from_your_template">.
321
     *
322
     * @param string|callable $content
323
     * @return $this
324
     */
325
    public function setRowClass($content)
326
    {
327
        $this->templates['DT_RowClass'] = $content;
328
329
        return $this;
330
    }
331
332
    /**
333
     * Sets DT_RowId template.
334
     * result: <tr id="output_from_your_template">.
335
     *
336
     * @param string|callable $content
337
     * @return $this
338
     */
339
    public function setRowId($content)
340
    {
341
        $this->templates['DT_RowId'] = $content;
342
343
        return $this;
344
    }
345
346
    /**
347
     * Set DT_RowData templates.
348
     *
349
     * @param array $data
350
     * @return $this
351
     */
352
    public function setRowData(array $data)
353
    {
354
        $this->templates['DT_RowData'] = $data;
355
356
        return $this;
357
    }
358
359
    /**
360
     * Add DT_RowData template.
361
     *
362
     * @param string $key
363
     * @param string|callable $value
364
     * @return $this
365
     */
366
    public function addRowData($key, $value)
367
    {
368
        $this->templates['DT_RowData'][$key] = $value;
369
370
        return $this;
371
    }
372
373
    /**
374
     * Set DT_RowAttr templates.
375
     * result: <tr attr1="attr1" attr2="attr2">.
376
     *
377
     * @param array $data
378
     * @return $this
379
     */
380
    public function setRowAttr(array $data)
381
    {
382
        $this->templates['DT_RowAttr'] = $data;
383
384
        return $this;
385
    }
386
387
    /**
388
     * Add DT_RowAttr template.
389
     *
390
     * @param string $key
391
     * @param string|callable $value
392
     * @return $this
393
     */
394
    public function addRowAttr($key, $value)
395
    {
396
        $this->templates['DT_RowAttr'][$key] = $value;
397
398
        return $this;
399
    }
400
401
    /**
402
     * Override default column filter search.
403
     *
404
     * @param string $column
405
     * @param string|callable $method
406
     * @return $this
407
     * @internal param $mixed ...,... All the individual parameters required for specified $method
408
     * @internal string $1 Special variable that returns the requested search keyword.
409
     */
410
    public function filterColumn($column, $method)
411
    {
412
        $params                             = func_get_args();
413
        $this->columnDef['filter'][$column] = ['method' => $method, 'parameters' => array_splice($params, 2)];
414
415
        return $this;
416
    }
417
418
    /**
419
     * Order each given columns versus the given custom sql.
420
     *
421
     * @param array $columns
422
     * @param string $sql
423
     * @param array $bindings
424
     * @return $this
425
     */
426
    public function orderColumns(array $columns, $sql, $bindings = [])
427
    {
428
        foreach ($columns as $column) {
429
            $this->orderColumn($column, str_replace(':column', $column, $sql), $bindings);
430
        }
431
432
        return $this;
433
    }
434
435
    /**
436
     * Override default column ordering.
437
     *
438
     * @param string $column
439
     * @param string $sql
440
     * @param array $bindings
441
     * @return $this
442
     * @internal string $1 Special variable that returns the requested order direction of the column.
443
     */
444
    public function orderColumn($column, $sql, $bindings = [])
445
    {
446
        $this->columnDef['order'][$column] = ['method' => 'orderByRaw', 'parameters' => [$sql, $bindings]];
447
448
        return $this;
449
    }
450
451
    /**
452
     * Set data output transformer.
453
     *
454
     * @param \League\Fractal\TransformerAbstract $transformer
455
     * @return $this
456
     */
457
    public function setTransformer($transformer)
458
    {
459
        $this->transformer = $transformer;
460
461
        return $this;
462
    }
463
464
    /**
465
     * Set fractal serializer class.
466
     *
467
     * @param string $serializer
468
     * @return $this
469
     */
470
    public function setSerializer($serializer)
471
    {
472
        $this->serializer = $serializer;
473
474
        return $this;
475
    }
476
477
    /**
478
     * Organizes works.
479
     *
480
     * @param bool $mDataSupport
481
     * @param bool $orderFirst
482
     * @return \Illuminate\Http\JsonResponse
483
     */
484
    public function make($mDataSupport = false, $orderFirst = false)
485
    {
486
        $this->totalRecords = $this->totalCount();
487
488
        if ($this->totalRecords) {
489
            $this->orderRecords(! $orderFirst);
490
            $this->filterRecords();
491
            $this->orderRecords($orderFirst);
492
            $this->paginate();
493
        }
494
495
        return $this->render($mDataSupport);
496
    }
497
498
    /**
499
     * Sort records.
500
     *
501
     * @param  boolean $skip
502
     * @return void
503
     */
504
    protected function orderRecords($skip)
505
    {
506
        if (! $skip) {
507
            $this->ordering();
508
        }
509
    }
510
511
    /**
512
     * Perform necessary filters.
513
     *
514
     * @return void
515
     */
516
    protected function filterRecords()
517
    {
518
        if ($this->autoFilter && $this->request->isSearchable()) {
519
            $this->filtering();
520
        }
521
522
        if (is_callable($this->filterCallback)) {
523
            call_user_func($this->filterCallback, $this->filterCallbackParameters);
524
        }
525
526
        $this->columnSearch();
527
        $this->filteredRecords = $this->isFilterApplied ? $this->count() : $this->totalRecords;
528
    }
529
530
    /**
531
     * Apply pagination.
532
     *
533
     * @return void
534
     */
535
    protected function paginate()
536
    {
537
        if ($this->request->isPaginationable() && ! $this->skipPaging) {
538
            $this->paging();
539
        }
540
    }
541
542
    /**
543
     * Render json response.
544
     *
545
     * @param bool $object
546
     * @return \Illuminate\Http\JsonResponse
547
     */
548
    protected function render($object = false)
549
    {
550
        $output = array_merge([
551
            'draw'            => (int) $this->request['draw'],
552
            'recordsTotal'    => $this->totalRecords,
553
            'recordsFiltered' => $this->filteredRecords,
554
        ], $this->appends);
555
556
        if (isset($this->transformer)) {
557
            $fractal = app('datatables.fractal');
558
559
            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...
560
                $fractal->setSerializer($this->createSerializer());
561
            }
562
563
            //Get transformer reflection
564
            //Firs method parameter should be data/object to transform
565
            $reflection = new \ReflectionMethod($this->transformer, 'transform');
566
            $parameter  = $reflection->getParameters()[0];
567
568
            //If parameter is class assuming it requires object
569
            //Else just pass array by default
570
            if ($parameter->getClass()) {
571
                $resource = new Collection($this->results(), $this->createTransformer());
572
            } else {
573
                $resource = new Collection(
574
                    $this->getProcessedData($object),
575
                    $this->createTransformer()
576
                );
577
            }
578
579
            $collection     = $fractal->createData($resource)->toArray();
580
            $output['data'] = $collection['data'];
581
        } else {
582
            $output['data'] = Helper::transform($this->getProcessedData($object));
583
        }
584
585
        if ($this->isDebugging()) {
586
            $output = $this->showDebugger($output);
587
        }
588
589
        return new JsonResponse($output);
590
    }
591
592
    /**
593
     * Get or create transformer serializer instance.
594
     *
595
     * @return \League\Fractal\Serializer\SerializerAbstract
596
     */
597
    protected function createSerializer()
598
    {
599
        if ($this->serializer instanceof \League\Fractal\Serializer\SerializerAbstract) {
0 ignored issues
show
Bug introduced by
The class League\Fractal\Serializer\SerializerAbstract does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
600
            return $this->serializer;
601
        }
602
603
        return new $this->serializer();
604
    }
605
606
    /**
607
     * Get or create transformer instance.
608
     *
609
     * @return \League\Fractal\TransformerAbstract
610
     */
611
    protected function createTransformer()
612
    {
613
        if ($this->transformer instanceof \League\Fractal\TransformerAbstract) {
0 ignored issues
show
Bug introduced by
The class League\Fractal\TransformerAbstract does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
614
            return $this->transformer;
615
        }
616
617
        return new $this->transformer();
618
    }
619
620
    /**
621
     * Get processed data
622
     *
623
     * @param bool|false $object
624
     * @return array
625
     */
626
    protected function getProcessedData($object = false)
627
    {
628
        $processor = new DataProcessor(
629
            $this->results(),
630
            $this->columnDef,
631
            $this->templates,
632
            $this->request['start']
633
        );
634
635
        return $processor->process($object);
636
    }
637
638
    /**
639
     * Check if app is in debug mode.
640
     *
641
     * @return bool
642
     */
643
    public function isDebugging()
644
    {
645
        return Config::get('app.debug', false);
646
    }
647
648
    /**
649
     * Append debug parameters on output.
650
     *
651
     * @param  array $output
652
     * @return array
653
     */
654
    protected function showDebugger(array $output)
655
    {
656
        $output['queries'] = $this->connection->getQueryLog();
657
        $output['input']   = $this->request->all();
658
659
        return $output;
660
    }
661
662
    /**
663
     * Get config is case insensitive status.
664
     *
665
     * @return bool
666
     */
667
    public function isCaseInsensitive()
668
    {
669
        return Config::get('datatables.search.case_insensitive', false);
670
    }
671
672
    /**
673
     * Append data on json response.
674
     *
675
     * @param mixed $key
676
     * @param mixed $value
677
     * @return $this
678
     */
679
    public function with($key, $value = '')
680
    {
681
        if (is_array($key)) {
682
            $this->appends = $key;
683
        } elseif (is_callable($value)) {
684
            $this->appends[$key] = value($value);
685
        } else {
686
            $this->appends[$key] = value($value);
687
        }
688
689
        return $this;
690
    }
691
692
    /**
693
     * Override default ordering method with a closure callback.
694
     *
695
     * @param callable $closure
696
     * @return $this
697
     */
698
    public function order(callable $closure)
699
    {
700
        $this->orderCallback = $closure;
701
702
        return $this;
703
    }
704
705
    /**
706
     * Update list of columns that is not allowed for search/sort.
707
     *
708
     * @param  array $blacklist
709
     * @return $this
710
     */
711
    public function blacklist(array $blacklist)
712
    {
713
        $this->columnDef['blacklist'] = $blacklist;
714
715
        return $this;
716
    }
717
718
    /**
719
     * Update list of columns that is not allowed for search/sort.
720
     *
721
     * @param  string|array $whitelist
722
     * @return $this
723
     */
724
    public function whitelist($whitelist = '*')
725
    {
726
        $this->columnDef['whitelist'] = $whitelist;
727
728
        return $this;
729
    }
730
731
    /**
732
     * Set smart search config at runtime.
733
     *
734
     * @param bool $bool
735
     * @return $this
736
     */
737
    public function smart($bool = true)
738
    {
739
        Config::set('datatables.search.smart', $bool);
740
741
        return $this;
742
    }
743
744
    /**
745
     * Set total records manually.
746
     *
747
     * @param int $total
748
     * @return $this
749
     */
750
    public function setTotalRecords($total)
751
    {
752
        $this->totalRecords = $total;
753
754
        return $this;
755
    }
756
757
    /**
758
     * Skip pagination as needed.
759
     *
760
     * @return $this
761
     */
762
    public function skipPaging()
763
    {
764
        $this->skipPaging = true;
765
766
        return $this;
767
    }
768
769
    /**
770
     * Check if the current sql language is based on oracle syntax.
771
     *
772
     * @return bool
773
     */
774
    public function isOracleSql()
775
    {
776
        return in_array($this->database, ['oracle', 'oci8']);
777
    }
778
779
    /**
780
     * Set datatables to do ordering with NULLS LAST option.
781
     *
782
     * @return $this
783
     */
784
    public function orderByNullsLast()
785
    {
786
        $this->nullsLast = true;
787
788
        return $this;
789
    }
790
791
    /**
792
     * Setup search keyword.
793
     *
794
     * @param  string $value
795
     * @return string
796
     */
797
    protected function setupKeyword($value)
798
    {
799
        if ($this->isSmartSearch()) {
800
            $keyword = '%' . $value . '%';
801
            if ($this->isWildcard()) {
802
                $keyword = $this->wildcardLikeString($value);
803
            }
804
            // remove escaping slash added on js script request
805
            $keyword = str_replace('\\', '%', $keyword);
806
807
            return $keyword;
808
        }
809
810
        return $value;
811
    }
812
813
    /**
814
     * Check if DataTables uses smart search.
815
     *
816
     * @return bool
817
     */
818
    public function isSmartSearch()
819
    {
820
        return Config::get('datatables.search.smart', true);
821
    }
822
823
    /**
824
     * Get config use wild card status.
825
     *
826
     * @return bool
827
     */
828
    public function isWildcard()
829
    {
830
        return Config::get('datatables.search.use_wildcards', false);
831
    }
832
833
    /**
834
     * Adds % wildcards to the given string.
835
     *
836
     * @param string $str
837
     * @param bool $lowercase
838
     * @return string
839
     */
840
    protected function wildcardLikeString($str, $lowercase = true)
841
    {
842
        $wild   = '%';
843
        $chars = preg_split('//u', $str, -1, PREG_SPLIT_NO_EMPTY);
844
845
        if (count($chars) > 0) {
846
            foreach ($chars as $char) {
847
                $wild .= $char . '%';
848
            }
849
        }
850
851
        if ($lowercase) {
852
            $wild = Str::lower($wild);
853
        }
854
855
        return $wild;
856
    }
857
858
    /**
859
     * Update flags to disable global search
860
     *
861
     * @param  callable $callback
862
     * @param  mixed $parameters
863
     * @param  bool $autoFilter
864
     */
865
    protected function overrideGlobalSearch(callable $callback, $parameters, $autoFilter = false)
866
    {
867
        $this->autoFilter               = $autoFilter;
868
        $this->isFilterApplied          = true;
869
        $this->filterCallback           = $callback;
870
        $this->filterCallbackParameters = $parameters;
871
    }
872
873
    /**
874
     * Check if column is blacklisted.
875
     *
876
     * @param string $column
877
     * @return bool
878
     */
879
    protected function isBlacklisted($column)
880
    {
881
        if (in_array($column, $this->columnDef['blacklist'])) {
882
            return true;
883
        }
884
885
        if ($this->columnDef['whitelist'] === '*' || in_array($column, $this->columnDef['whitelist'])) {
886
            return false;
887
        }
888
889
        return true;
890
    }
891
892
    /**
893
     * Get column name to be use for filtering and sorting.
894
     *
895
     * @param integer $index
896
     * @param bool $wantsAlias
897
     * @return string
898
     */
899
    protected function getColumnName($index, $wantsAlias = false)
900
    {
901
        $column = $this->request->columnName($index);
902
903
        // DataTables is using make(false)
904
        if (is_numeric($column)) {
905
            $column = $this->getColumnNameByIndex($index);
906
        }
907
908
        if (Str::contains(Str::upper($column), ' AS ')) {
909
            $column = $this->extractColumnName($column, $wantsAlias);
910
        }
911
912
        return $column;
913
    }
914
915
    /**
916
     * Get column name by order column index.
917
     *
918
     * @param int $index
919
     * @return mixed
920
     */
921
    protected function getColumnNameByIndex($index)
922
    {
923
        $name = isset($this->columns[$index]) && $this->columns[$index] <> '*' ? $this->columns[$index] : $this->getPrimaryKeyName();
924
925
        return in_array($name, $this->extraColumns, true) ? $this->getPrimaryKeyName() : $name;
926
    }
927
928
    /**
929
     * If column name could not be resolved then use primary key.
930
     *
931
     * @return string
932
     */
933
    protected function getPrimaryKeyName()
934
    {
935
        if ($this->isEloquent()) {
936
            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...
937
        }
938
939
        return 'id';
940
    }
941
942
    /**
943
     * Check if the engine used was eloquent.
944
     *
945
     * @return bool
946
     */
947
    public function isEloquent()
948
    {
949
        return $this->query_type === 'eloquent';
950
    }
951
952
    /**
953
     * Get column name from string.
954
     *
955
     * @param string $str
956
     * @param bool $wantsAlias
957
     * @return string
958
     */
959
    protected function extractColumnName($str, $wantsAlias)
960
    {
961
        $matches = explode(' as ', Str::lower($str));
962
963
        if (! empty($matches)) {
964
            if ($wantsAlias) {
965
                return array_pop($matches);
966
            } else {
967
                return array_shift($matches);
968
            }
969
        } elseif (strpos($str, '.')) {
970
            $array = explode('.', $str);
971
972
            return array_pop($array);
973
        }
974
975
        return $str;
976
    }
977
}
978