Completed
Push — master ( 34cd5e...81adef )
by Arjay
02:27
created

BaseEngine::rawColumns()   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
        'raw'       => ['action'],
70
        'blacklist' => ['password', 'remember_token'],
71
        'whitelist' => '*',
72
    ];
73
74
    /**
75
     * Query type.
76
     *
77
     * @var string
78
     */
79
    protected $query_type;
80
81
    /**
82
     * Extra/Added columns.
83
     *
84
     * @var array
85
     */
86
    protected $extraColumns = [];
87
88
    /**
89
     * Total records.
90
     *
91
     * @var int
92
     */
93
    protected $totalRecords = 0;
94
95
    /**
96
     * Total filtered records.
97
     *
98
     * @var int
99
     */
100
    protected $filteredRecords = 0;
101
102
    /**
103
     * Auto-filter flag.
104
     *
105
     * @var bool
106
     */
107
    protected $autoFilter = true;
108
109
    /**
110
     * Callback to override global search.
111
     *
112
     * @var callable
113
     */
114
    protected $filterCallback;
115
116
    /**
117
     * Parameters to passed on filterCallback.
118
     *
119
     * @var mixed
120
     */
121
    protected $filterCallbackParameters;
122
123
    /**
124
     * DT row templates container.
125
     *
126
     * @var array
127
     */
128
    protected $templates = [
129
        'DT_RowId'    => '',
130
        'DT_RowClass' => '',
131
        'DT_RowData'  => [],
132
        'DT_RowAttr'  => [],
133
    ];
134
135
    /**
136
     * Output transformer.
137
     *
138
     * @var \League\Fractal\TransformerAbstract
139
     */
140
    protected $transformer = null;
141
142
    /**
143
     * Database prefix
144
     *
145
     * @var string
146
     */
147
    protected $prefix;
148
149
    /**
150
     * Database driver used.
151
     *
152
     * @var string
153
     */
154
    protected $database;
155
156
    /**
157
     * [internal] Track if any filter was applied for at least one column
158
     *
159
     * @var boolean
160
     */
161
    protected $isFilterApplied = false;
162
163
    /**
164
     * Fractal serializer class.
165
     *
166
     * @var string|null
167
     */
168
    protected $serializer = null;
169
170
    /**
171
     * Custom ordering callback.
172
     *
173
     * @var callable
174
     */
175
    protected $orderCallback;
176
177
    /**
178
     * Skip paginate as needed.
179
     *
180
     * @var bool
181
     */
182
    protected $skipPaging = false;
183
184
    /**
185
     * Array of data to append on json response.
186
     *
187
     * @var array
188
     */
189
    protected $appends = [];
190
191
    /**
192
     * Flag for ordering NULLS LAST option.
193
     *
194
     * @var bool
195
     */
196
    protected $nullsLast = false;
197
198
    /**
199
     * Add column in collection.
200
     *
201
     * @param string $name
202
     * @param string|callable $content
203
     * @param bool|int $order
204
     * @return $this
205
     */
206
    public function addColumn($name, $content, $order = false)
207
    {
208
        $this->extraColumns[] = $name;
209
210
        $this->columnDef['append'][] = ['name' => $name, 'content' => $content, 'order' => $order];
211
212
        return $this;
213
    }
214
215
    /**
216
     * Add DT row index column on response.
217
     *
218
     * @return $this
219
     */
220
    public function addIndexColumn()
221
    {
222
        $this->columnDef['index'] = true;
223
224
        return $this;
225
    }
226
227
    /**
228
     * Edit column's content.
229
     *
230
     * @param string $name
231
     * @param string|callable $content
232
     * @return $this
233
     */
234
    public function editColumn($name, $content)
235
    {
236
        $this->columnDef['edit'][] = ['name' => $name, 'content' => $content];
237
238
        return $this;
239
    }
240
241
    /**
242
     * Remove column from collection.
243
     *
244
     * @return $this
245
     */
246
    public function removeColumn()
247
    {
248
        $names                     = func_get_args();
249
        $this->columnDef['excess'] = array_merge($this->columnDef['excess'], $names);
250
251
        return $this;
252
    }
253
254
    /**
255
     * Declare columns to escape values.
256
     *
257
     * @param string|array $columns
258
     * @return $this
259
     */
260
    public function escapeColumns($columns = '*')
261
    {
262
        $this->columnDef['escape'] = $columns;
263
264
        return $this;
265
    }
266
267
    /**
268
     * Set columns that should not be escaped.
269
     *
270
     * @param array $columns
271
     * @return $this
272
     */
273
    public function rawColumns(array $columns)
274
    {
275
        $this->columnDef['raw'] = $columns;
276
277
        return $this;
278
    }
279
280
    /**
281
     * Allows previous API calls where the methods were snake_case.
282
     * Will convert a camelCase API call to a snake_case call.
283
     * Allow query builder method to be used by the engine.
284
     *
285
     * @param  string $name
286
     * @param  array $arguments
287
     * @return mixed
288
     */
289
    public function __call($name, $arguments)
290
    {
291
        $name = Str::camel(Str::lower($name));
292
        if (method_exists($this, $name)) {
293
            return call_user_func_array([$this, $name], $arguments);
294
        } elseif (method_exists($this->getQueryBuilder(), $name)) {
295
            call_user_func_array([$this->getQueryBuilder(), $name], $arguments);
296
        } else {
297
            trigger_error('Call to undefined method ' . __CLASS__ . '::' . $name . '()', E_USER_ERROR);
298
        }
299
300
        return $this;
301
    }
302
303
    /**
304
     * Get Query Builder object.
305
     *
306
     * @param mixed $instance
307
     * @return mixed
308
     */
309
    public function getQueryBuilder($instance = null)
310
    {
311
        if (! $instance) {
312
            $instance = $this->query;
313
        }
314
315
        if ($this->isQueryBuilder()) {
316
            return $instance;
317
        }
318
319
        return $instance->getQuery();
320
    }
321
322
    /**
323
     * Check query type is a builder.
324
     *
325
     * @return bool
326
     */
327
    public function isQueryBuilder()
328
    {
329
        return $this->query_type == 'builder';
330
    }
331
332
    /**
333
     * Sets DT_RowClass template.
334
     * result: <tr class="output_from_your_template">.
335
     *
336
     * @param string|callable $content
337
     * @return $this
338
     */
339
    public function setRowClass($content)
340
    {
341
        $this->templates['DT_RowClass'] = $content;
342
343
        return $this;
344
    }
345
346
    /**
347
     * Sets DT_RowId template.
348
     * result: <tr id="output_from_your_template">.
349
     *
350
     * @param string|callable $content
351
     * @return $this
352
     */
353
    public function setRowId($content)
354
    {
355
        $this->templates['DT_RowId'] = $content;
356
357
        return $this;
358
    }
359
360
    /**
361
     * Set DT_RowData templates.
362
     *
363
     * @param array $data
364
     * @return $this
365
     */
366
    public function setRowData(array $data)
367
    {
368
        $this->templates['DT_RowData'] = $data;
369
370
        return $this;
371
    }
372
373
    /**
374
     * Add DT_RowData template.
375
     *
376
     * @param string $key
377
     * @param string|callable $value
378
     * @return $this
379
     */
380
    public function addRowData($key, $value)
381
    {
382
        $this->templates['DT_RowData'][$key] = $value;
383
384
        return $this;
385
    }
386
387
    /**
388
     * Set DT_RowAttr templates.
389
     * result: <tr attr1="attr1" attr2="attr2">.
390
     *
391
     * @param array $data
392
     * @return $this
393
     */
394
    public function setRowAttr(array $data)
395
    {
396
        $this->templates['DT_RowAttr'] = $data;
397
398
        return $this;
399
    }
400
401
    /**
402
     * Add DT_RowAttr template.
403
     *
404
     * @param string $key
405
     * @param string|callable $value
406
     * @return $this
407
     */
408
    public function addRowAttr($key, $value)
409
    {
410
        $this->templates['DT_RowAttr'][$key] = $value;
411
412
        return $this;
413
    }
414
415
    /**
416
     * Override default column filter search.
417
     *
418
     * @param string $column
419
     * @param string|callable $method
420
     * @return $this
421
     * @internal param $mixed ...,... All the individual parameters required for specified $method
422
     * @internal string $1 Special variable that returns the requested search keyword.
423
     */
424
    public function filterColumn($column, $method)
425
    {
426
        $params                             = func_get_args();
427
        $this->columnDef['filter'][$column] = ['method' => $method, 'parameters' => array_splice($params, 2)];
428
429
        return $this;
430
    }
431
432
    /**
433
     * Order each given columns versus the given custom sql.
434
     *
435
     * @param array $columns
436
     * @param string $sql
437
     * @param array $bindings
438
     * @return $this
439
     */
440
    public function orderColumns(array $columns, $sql, $bindings = [])
441
    {
442
        foreach ($columns as $column) {
443
            $this->orderColumn($column, str_replace(':column', $column, $sql), $bindings);
444
        }
445
446
        return $this;
447
    }
448
449
    /**
450
     * Override default column ordering.
451
     *
452
     * @param string $column
453
     * @param string $sql
454
     * @param array $bindings
455
     * @return $this
456
     * @internal string $1 Special variable that returns the requested order direction of the column.
457
     */
458
    public function orderColumn($column, $sql, $bindings = [])
459
    {
460
        $this->columnDef['order'][$column] = ['method' => 'orderByRaw', 'parameters' => [$sql, $bindings]];
461
462
        return $this;
463
    }
464
465
    /**
466
     * Set data output transformer.
467
     *
468
     * @param \League\Fractal\TransformerAbstract $transformer
469
     * @return $this
470
     */
471
    public function setTransformer($transformer)
472
    {
473
        $this->transformer = $transformer;
474
475
        return $this;
476
    }
477
478
    /**
479
     * Set fractal serializer class.
480
     *
481
     * @param string $serializer
482
     * @return $this
483
     */
484
    public function setSerializer($serializer)
485
    {
486
        $this->serializer = $serializer;
487
488
        return $this;
489
    }
490
491
    /**
492
     * Organizes works.
493
     *
494
     * @param bool $mDataSupport
495
     * @param bool $orderFirst
496
     * @return \Illuminate\Http\JsonResponse
497
     */
498
    public function make($mDataSupport = false, $orderFirst = false)
499
    {
500
        $this->totalRecords = $this->totalCount();
501
502
        if ($this->totalRecords) {
503
            $this->orderRecords(! $orderFirst);
504
            $this->filterRecords();
505
            $this->orderRecords($orderFirst);
506
            $this->paginate();
507
        }
508
509
        return $this->render($mDataSupport);
510
    }
511
512
    /**
513
     * Sort records.
514
     *
515
     * @param  boolean $skip
516
     * @return void
517
     */
518
    protected function orderRecords($skip)
519
    {
520
        if (! $skip) {
521
            $this->ordering();
522
        }
523
    }
524
525
    /**
526
     * Perform necessary filters.
527
     *
528
     * @return void
529
     */
530
    protected function filterRecords()
531
    {
532
        if ($this->autoFilter && $this->request->isSearchable()) {
533
            $this->filtering();
534
        }
535
536
        if (is_callable($this->filterCallback)) {
537
            call_user_func($this->filterCallback, $this->filterCallbackParameters);
538
        }
539
540
        $this->columnSearch();
541
        $this->filteredRecords = $this->isFilterApplied ? $this->count() : $this->totalRecords;
542
    }
543
544
    /**
545
     * Apply pagination.
546
     *
547
     * @return void
548
     */
549
    protected function paginate()
550
    {
551
        if ($this->request->isPaginationable() && ! $this->skipPaging) {
552
            $this->paging();
553
        }
554
    }
555
556
    /**
557
     * Render json response.
558
     *
559
     * @param bool $object
560
     * @return \Illuminate\Http\JsonResponse
561
     */
562
    protected function render($object = false)
563
    {
564
        $output = array_merge([
565
            'draw'            => (int) $this->request['draw'],
566
            'recordsTotal'    => $this->totalRecords,
567
            'recordsFiltered' => $this->filteredRecords,
568
        ], $this->appends);
569
570
        if (isset($this->transformer)) {
571
            $fractal = app('datatables.fractal');
572
573
            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...
574
                $fractal->setSerializer($this->createSerializer());
575
            }
576
577
            //Get transformer reflection
578
            //Firs method parameter should be data/object to transform
579
            $reflection = new \ReflectionMethod($this->transformer, 'transform');
580
            $parameter  = $reflection->getParameters()[0];
581
582
            //If parameter is class assuming it requires object
583
            //Else just pass array by default
584
            if ($parameter->getClass()) {
585
                $resource = new Collection($this->results(), $this->createTransformer());
586
            } else {
587
                $resource = new Collection(
588
                    $this->getProcessedData($object),
589
                    $this->createTransformer()
590
                );
591
            }
592
593
            $collection     = $fractal->createData($resource)->toArray();
594
            $output['data'] = $collection['data'];
595
        } else {
596
            $output['data'] = Helper::transform($this->getProcessedData($object));
597
        }
598
599
        if ($this->isDebugging()) {
600
            $output = $this->showDebugger($output);
601
        }
602
603
        return new JsonResponse($output);
604
    }
605
606
    /**
607
     * Get or create transformer serializer instance.
608
     *
609
     * @return \League\Fractal\Serializer\SerializerAbstract
610
     */
611
    protected function createSerializer()
612
    {
613
        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...
614
            return $this->serializer;
615
        }
616
617
        return new $this->serializer();
618
    }
619
620
    /**
621
     * Get or create transformer instance.
622
     *
623
     * @return \League\Fractal\TransformerAbstract
624
     */
625
    protected function createTransformer()
626
    {
627
        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...
628
            return $this->transformer;
629
        }
630
631
        return new $this->transformer();
632
    }
633
634
    /**
635
     * Get processed data
636
     *
637
     * @param bool|false $object
638
     * @return array
639
     */
640
    protected function getProcessedData($object = false)
641
    {
642
        $processor = new DataProcessor(
643
            $this->results(),
644
            $this->columnDef,
645
            $this->templates,
646
            $this->request['start']
647
        );
648
649
        return $processor->process($object);
650
    }
651
652
    /**
653
     * Check if app is in debug mode.
654
     *
655
     * @return bool
656
     */
657
    public function isDebugging()
658
    {
659
        return Config::get('app.debug', false);
660
    }
661
662
    /**
663
     * Append debug parameters on output.
664
     *
665
     * @param  array $output
666
     * @return array
667
     */
668
    protected function showDebugger(array $output)
669
    {
670
        $output['queries'] = $this->connection->getQueryLog();
671
        $output['input']   = $this->request->all();
672
673
        return $output;
674
    }
675
676
    /**
677
     * Get config is case insensitive status.
678
     *
679
     * @return bool
680
     */
681
    public function isCaseInsensitive()
682
    {
683
        return Config::get('datatables.search.case_insensitive', false);
684
    }
685
686
    /**
687
     * Append data on json response.
688
     *
689
     * @param mixed $key
690
     * @param mixed $value
691
     * @return $this
692
     */
693
    public function with($key, $value = '')
694
    {
695
        if (is_array($key)) {
696
            $this->appends = $key;
697
        } elseif (is_callable($value)) {
698
            $this->appends[$key] = value($value);
699
        } else {
700
            $this->appends[$key] = value($value);
701
        }
702
703
        return $this;
704
    }
705
706
    /**
707
     * Override default ordering method with a closure callback.
708
     *
709
     * @param callable $closure
710
     * @return $this
711
     */
712
    public function order(callable $closure)
713
    {
714
        $this->orderCallback = $closure;
715
716
        return $this;
717
    }
718
719
    /**
720
     * Update list of columns that is not allowed for search/sort.
721
     *
722
     * @param  array $blacklist
723
     * @return $this
724
     */
725
    public function blacklist(array $blacklist)
726
    {
727
        $this->columnDef['blacklist'] = $blacklist;
728
729
        return $this;
730
    }
731
732
    /**
733
     * Update list of columns that is not allowed for search/sort.
734
     *
735
     * @param  string|array $whitelist
736
     * @return $this
737
     */
738
    public function whitelist($whitelist = '*')
739
    {
740
        $this->columnDef['whitelist'] = $whitelist;
741
742
        return $this;
743
    }
744
745
    /**
746
     * Set smart search config at runtime.
747
     *
748
     * @param bool $bool
749
     * @return $this
750
     */
751
    public function smart($bool = true)
752
    {
753
        Config::set('datatables.search.smart', $bool);
754
755
        return $this;
756
    }
757
758
    /**
759
     * Set total records manually.
760
     *
761
     * @param int $total
762
     * @return $this
763
     */
764
    public function setTotalRecords($total)
765
    {
766
        $this->totalRecords = $total;
767
768
        return $this;
769
    }
770
771
    /**
772
     * Skip pagination as needed.
773
     *
774
     * @return $this
775
     */
776
    public function skipPaging()
777
    {
778
        $this->skipPaging = true;
779
780
        return $this;
781
    }
782
783
    /**
784
     * Check if the current sql language is based on oracle syntax.
785
     *
786
     * @return bool
787
     */
788
    public function isOracleSql()
789
    {
790
        return in_array($this->database, ['oracle', 'oci8']);
791
    }
792
793
    /**
794
     * Set datatables to do ordering with NULLS LAST option.
795
     *
796
     * @return $this
797
     */
798
    public function orderByNullsLast()
799
    {
800
        $this->nullsLast = true;
801
802
        return $this;
803
    }
804
805
    /**
806
     * Setup search keyword.
807
     *
808
     * @param  string $value
809
     * @return string
810
     */
811
    protected function setupKeyword($value)
812
    {
813
        if ($this->isSmartSearch()) {
814
            $keyword = '%' . $value . '%';
815
            if ($this->isWildcard()) {
816
                $keyword = $this->wildcardLikeString($value);
817
            }
818
            // remove escaping slash added on js script request
819
            $keyword = str_replace('\\', '%', $keyword);
820
821
            return $keyword;
822
        }
823
824
        return $value;
825
    }
826
827
    /**
828
     * Check if DataTables uses smart search.
829
     *
830
     * @return bool
831
     */
832
    public function isSmartSearch()
833
    {
834
        return Config::get('datatables.search.smart', true);
835
    }
836
837
    /**
838
     * Get config use wild card status.
839
     *
840
     * @return bool
841
     */
842
    public function isWildcard()
843
    {
844
        return Config::get('datatables.search.use_wildcards', false);
845
    }
846
847
    /**
848
     * Adds % wildcards to the given string.
849
     *
850
     * @param string $str
851
     * @param bool $lowercase
852
     * @return string
853
     */
854
    protected function wildcardLikeString($str, $lowercase = true)
855
    {
856
        $wild  = '%';
857
        $chars = preg_split('//u', $str, -1, PREG_SPLIT_NO_EMPTY);
858
859
        if (count($chars) > 0) {
860
            foreach ($chars as $char) {
861
                $wild .= $char . '%';
862
            }
863
        }
864
865
        if ($lowercase) {
866
            $wild = Str::lower($wild);
867
        }
868
869
        return $wild;
870
    }
871
872
    /**
873
     * Update flags to disable global search
874
     *
875
     * @param  callable $callback
876
     * @param  mixed $parameters
877
     * @param  bool $autoFilter
878
     */
879
    protected function overrideGlobalSearch(callable $callback, $parameters, $autoFilter = false)
880
    {
881
        $this->autoFilter               = $autoFilter;
882
        $this->isFilterApplied          = true;
883
        $this->filterCallback           = $callback;
884
        $this->filterCallbackParameters = $parameters;
885
    }
886
887
    /**
888
     * Check if column is blacklisted.
889
     *
890
     * @param string $column
891
     * @return bool
892
     */
893
    protected function isBlacklisted($column)
894
    {
895
        if (in_array($column, $this->columnDef['blacklist'])) {
896
            return true;
897
        }
898
899
        if ($this->columnDef['whitelist'] === '*' || in_array($column, $this->columnDef['whitelist'])) {
900
            return false;
901
        }
902
903
        return true;
904
    }
905
906
    /**
907
     * Get column name to be use for filtering and sorting.
908
     *
909
     * @param integer $index
910
     * @param bool $wantsAlias
911
     * @return string
912
     */
913
    protected function getColumnName($index, $wantsAlias = false)
914
    {
915
        $column = $this->request->columnName($index);
916
917
        // DataTables is using make(false)
918
        if (is_numeric($column)) {
919
            $column = $this->getColumnNameByIndex($index);
920
        }
921
922
        if (Str::contains(Str::upper($column), ' AS ')) {
923
            $column = $this->extractColumnName($column, $wantsAlias);
924
        }
925
926
        return $column;
927
    }
928
929
    /**
930
     * Get column name by order column index.
931
     *
932
     * @param int $index
933
     * @return mixed
934
     */
935
    protected function getColumnNameByIndex($index)
936
    {
937
        $name = isset($this->columns[$index]) && $this->columns[$index] <> '*' ? $this->columns[$index] : $this->getPrimaryKeyName();
938
939
        return in_array($name, $this->extraColumns, true) ? $this->getPrimaryKeyName() : $name;
940
    }
941
942
    /**
943
     * If column name could not be resolved then use primary key.
944
     *
945
     * @return string
946
     */
947
    protected function getPrimaryKeyName()
948
    {
949
        if ($this->isEloquent()) {
950
            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...
951
        }
952
953
        return 'id';
954
    }
955
956
    /**
957
     * Check if the engine used was eloquent.
958
     *
959
     * @return bool
960
     */
961
    public function isEloquent()
962
    {
963
        return $this->query_type === 'eloquent';
964
    }
965
966
    /**
967
     * Get column name from string.
968
     *
969
     * @param string $str
970
     * @param bool $wantsAlias
971
     * @return string
972
     */
973
    protected function extractColumnName($str, $wantsAlias)
974
    {
975
        $matches = explode(' as ', Str::lower($str));
976
977
        if (! empty($matches)) {
978
            if ($wantsAlias) {
979
                return array_pop($matches);
980
            } else {
981
                return array_shift($matches);
982
            }
983
        } elseif (strpos($str, '.')) {
984
            $array = explode('.', $str);
985
986
            return array_pop($array);
987
        }
988
989
        return $str;
990
    }
991
}
992