Completed
Push — master ( 01f863...e04cdd )
by Arjay
11:27
created

BaseEngine::pushToBlacklist()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
709
710
        return $output;
711
    }
712
713
    /**
714
     * Get monolog/logger instance.
715
     *
716
     * @return \Illuminate\Contracts\Logging\Log
717
     */
718
    public function getLogger()
719
    {
720
        $this->logger = $this->logger ?: resolve(Log::class);
721
722
        return $this->logger;
723
    }
724
725
    /**
726
     * Set monolog/logger instance.
727
     *
728
     * @param \Illuminate\Contracts\Logging\Log $logger
729
     * @return $this
730
     */
731
    public function setLogger(Log $logger)
732
    {
733
        $this->logger = $logger;
734
735
        return $this;
736
    }
737
738
    /**
739
     * Get config is case insensitive status.
740
     *
741
     * @return bool
742
     */
743
    public function isCaseInsensitive()
744
    {
745
        return config('datatables.search.case_insensitive', false);
746
    }
747
748
    /**
749
     * Append data on json response.
750
     *
751
     * @param mixed $key
752
     * @param mixed $value
753
     * @return $this
754
     */
755
    public function with($key, $value = '')
756
    {
757
        if (is_array($key)) {
758
            $this->appends = $key;
759
        } elseif (is_callable($value)) {
760
            $this->appends[$key] = value($value);
761
        } else {
762
            $this->appends[$key] = value($value);
763
        }
764
765
        return $this;
766
    }
767
768
    /**
769
     * Override default ordering method with a closure callback.
770
     *
771
     * @param callable $closure
772
     * @return $this
773
     */
774
    public function order(callable $closure)
775
    {
776
        $this->orderCallback = $closure;
777
778
        return $this;
779
    }
780
781
    /**
782
     * Update list of columns that is not allowed for search/sort.
783
     *
784
     * @param  array $blacklist
785
     * @return $this
786
     */
787
    public function blacklist(array $blacklist)
788
    {
789
        $this->columnDef['blacklist'] = $blacklist;
790
791
        return $this;
792
    }
793
794
    /**
795
     * Update list of columns that is allowed for search/sort.
796
     *
797
     * @param  string|array $whitelist
798
     * @return $this
799
     */
800
    public function whitelist($whitelist = '*')
801
    {
802
        $this->columnDef['whitelist'] = $whitelist;
803
804
        return $this;
805
    }
806
807
    /**
808
     * Set smart search config at runtime.
809
     *
810
     * @param bool $bool
811
     * @return $this
812
     */
813
    public function smart($bool = true)
814
    {
815
        config(['datatables.search.smart' => $bool]);
816
817
        return $this;
818
    }
819
820
    /**
821
     * Set total records manually.
822
     *
823
     * @param int $total
824
     * @return $this
825
     */
826
    public function setTotalRecords($total)
827
    {
828
        $this->totalRecords = $total;
829
830
        return $this;
831
    }
832
833
    /**
834
     * Skip pagination as needed.
835
     *
836
     * @return $this
837
     */
838
    public function skipPaging()
839
    {
840
        $this->skipPaging = true;
841
842
        return $this;
843
    }
844
845
    /**
846
     * Check if the current sql language is based on oracle syntax.
847
     *
848
     * @return bool
849
     */
850
    public function isOracleSql()
851
    {
852
        return in_array($this->database, ['oracle', 'oci8']);
853
    }
854
855
    /**
856
     * Set datatables to do ordering with NULLS LAST option.
857
     *
858
     * @return $this
859
     */
860
    public function orderByNullsLast()
861
    {
862
        $this->nullsLast = true;
863
864
        return $this;
865
    }
866
867
    /**
868
     * Push a new column name to blacklist
869
     *
870
     * @param string $column
871
     * @return $this
872
     */
873
    public function pushToBlacklist($column)
874
    {
875
        if (! $this->isBlacklisted($column)) {
876
            array_push($this->columnDef['blacklist'], $column);
877
        }
878
879
        return $this;
880
    }
881
882
    /**
883
     * Check if column is blacklisted.
884
     *
885
     * @param string $column
886
     * @return bool
887
     */
888
    protected function isBlacklisted($column)
889
    {
890
        if (in_array($column, $this->columnDef['blacklist'])) {
891
            return true;
892
        }
893
894
        if ($this->columnDef['whitelist'] === '*' || in_array($column, $this->columnDef['whitelist'])) {
895
            return false;
896
        }
897
898
        return true;
899
    }
900
901
    /**
902
     * Setup search keyword.
903
     *
904
     * @param  string $value
905
     * @return string
906
     */
907
    protected function setupKeyword($value)
908
    {
909
        if ($this->isSmartSearch()) {
910
            $keyword = '%' . $value . '%';
911
            if ($this->isWildcard()) {
912
                $keyword = $this->wildcardLikeString($value);
913
            }
914
            // remove escaping slash added on js script request
915
            $keyword = str_replace('\\', '%', $keyword);
916
917
            return $keyword;
918
        }
919
920
        return $value;
921
    }
922
923
    /**
924
     * Check if DataTables uses smart search.
925
     *
926
     * @return bool
927
     */
928
    public function isSmartSearch()
929
    {
930
        return config('datatables.search.smart', true);
931
    }
932
933
    /**
934
     * Get config use wild card status.
935
     *
936
     * @return bool
937
     */
938
    public function isWildcard()
939
    {
940
        return config('datatables.search.use_wildcards', false);
941
    }
942
943
    /**
944
     * Adds % wildcards to the given string.
945
     *
946
     * @param string $str
947
     * @param bool $lowercase
948
     * @return string
949
     */
950
    protected function wildcardLikeString($str, $lowercase = true)
951
    {
952
        $wild  = '%';
953
        $chars = preg_split('//u', $str, -1, PREG_SPLIT_NO_EMPTY);
954
955
        if (count($chars) > 0) {
956
            foreach ($chars as $char) {
957
                $wild .= $char . '%';
958
            }
959
        }
960
961
        if ($lowercase) {
962
            $wild = Str::lower($wild);
963
        }
964
965
        return $wild;
966
    }
967
968
    /**
969
     * Update flags to disable global search
970
     *
971
     * @param  callable $callback
972
     * @param  mixed $parameters
973
     * @param  bool $autoFilter
974
     */
975
    protected function overrideGlobalSearch(callable $callback, $parameters, $autoFilter = false)
976
    {
977
        $this->autoFilter               = $autoFilter;
978
        $this->isFilterApplied          = true;
979
        $this->filterCallback           = $callback;
980
        $this->filterCallbackParameters = $parameters;
981
    }
982
983
    /**
984
     * Get column name to be use for filtering and sorting.
985
     *
986
     * @param integer $index
987
     * @param bool $wantsAlias
988
     * @return string
989
     */
990
    protected function getColumnName($index, $wantsAlias = false)
991
    {
992
        $column = $this->request->columnName($index);
993
994
        // DataTables is using make(false)
995
        if (is_numeric($column)) {
996
            $column = $this->getColumnNameByIndex($index);
997
        }
998
999
        if (Str::contains(Str::upper($column), ' AS ')) {
1000
            $column = $this->extractColumnName($column, $wantsAlias);
1001
        }
1002
1003
        return $column;
1004
    }
1005
1006
    /**
1007
     * Get column name by order column index.
1008
     *
1009
     * @param int $index
1010
     * @return mixed
1011
     */
1012
    protected function getColumnNameByIndex($index)
1013
    {
1014
        $name = isset($this->columns[$index]) && $this->columns[$index] != '*' ? $this->columns[$index] : $this->getPrimaryKeyName();
1015
1016
        return in_array($name, $this->extraColumns, true) ? $this->getPrimaryKeyName() : $name;
1017
    }
1018
1019
    /**
1020
     * If column name could not be resolved then use primary key.
1021
     *
1022
     * @return string
1023
     */
1024
    protected function getPrimaryKeyName()
1025
    {
1026
        if ($this->isEloquent()) {
1027
            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...
1028
        }
1029
1030
        return 'id';
1031
    }
1032
1033
    /**
1034
     * Check if the engine used was eloquent.
1035
     *
1036
     * @return bool
1037
     */
1038
    public function isEloquent()
1039
    {
1040
        return $this->query_type === 'eloquent';
1041
    }
1042
1043
    /**
1044
     * Get column name from string.
1045
     *
1046
     * @param string $str
1047
     * @param bool $wantsAlias
1048
     * @return string
1049
     */
1050
    protected function extractColumnName($str, $wantsAlias)
1051
    {
1052
        $matches = explode(' as ', Str::lower($str));
1053
1054
        if (! empty($matches)) {
1055
            if ($wantsAlias) {
1056
                return array_pop($matches);
1057
            } else {
1058
                return array_shift($matches);
1059
            }
1060
        } elseif (strpos($str, '.')) {
1061
            $array = explode('.', $str);
1062
1063
            return array_pop($array);
1064
        }
1065
1066
        return $str;
1067
    }
1068
}
1069