Completed
Push — master ( 80ba42...c75345 )
by Arjay
01:44
created

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

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

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