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