Completed
Push — master ( 356601...b2b7ae )
by Arjay
08:01
created

BaseEngine::blacklist()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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