DataTable   C
last analyzed

Complexity

Total Complexity 57

Size/Duplication

Total Lines 618
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 8

Importance

Changes 0
Metric Value
wmc 57
lcom 2
cbo 8
dl 0
loc 618
rs 5.022
c 0
b 0
f 0

35 Methods

Rating   Name   Duplication   Size   Complexity  
A render() 0 16 6
A request() 0 4 2
A ajax() 0 23 4
A printPreview() 0 6 1
A getDataForPrint() 0 6 1
A printColumns() 0 4 2
A getPrintColumnsFromBuilder() 0 4 1
A getExportColumnsFromBuilder() 0 4 1
A getColumnsFromBuilder() 0 4 1
A html() 0 4 1
A builder() 0 12 3
A mapResponseToColumns() 0 8 1
A getAjaxResponseData() 0 9 1
A getHtmlBuilder() 0 9 2
A withHtml() 0 6 1
A before() 0 6 1
A response() 0 6 1
A excel() 0 6 1
A buildExcelFile() 0 6 1
A getFilename() 0 4 2
A setFilename() 0 6 1
A filename() 0 4 1
A getDataForExport() 0 6 1
A exportColumns() 0 4 2
A toColumnsCollection() 0 17 3
A csv() 0 6 1
A pdf() 0 8 2
A snappyPdf() 0 11 1
A addScope() 0 6 1
A addScopes() 0 6 1
A with() 0 10 2
A __get() 0 6 2
A applyScopes() 0 8 2
A hasScopes() 0 8 2
A getBuilderParameters() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like DataTable often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DataTable, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Yajra\DataTables\Services;
4
5
use Illuminate\Http\JsonResponse;
6
use Illuminate\Support\Collection;
7
use Yajra\DataTables\Contracts\DataTableButtons;
8
use Yajra\DataTables\Contracts\DataTableScope;
9
use Yajra\DataTables\Html\Column;
10
use Yajra\DataTables\Transformers\DataArrayTransformer;
11
12
abstract class DataTable implements DataTableButtons
13
{
14
    /**
15
     * DataTables print preview view.
16
     *
17
     * @var string
18
     */
19
    protected $printPreview = 'datatables::print';
20
21
    /**
22
     * Name of the dataTable variable.
23
     *
24
     * @var string
25
     */
26
    protected $dataTableVariable = 'dataTable';
27
28
    /**
29
     * List of columns to be excluded from export.
30
     *
31
     * @var string|array
32
     */
33
    protected $excludeFromExport = [];
34
35
    /**
36
     * List of columns to be excluded from printing.
37
     *
38
     * @var string|array
39
     */
40
    protected $excludeFromPrint = [];
41
42
    /**
43
     * List of columns to be exported.
44
     *
45
     * @var string|array
46
     */
47
    protected $exportColumns = '*';
48
49
    /**
50
     * List of columns to be printed.
51
     *
52
     * @var string|array
53
     */
54
    protected $printColumns = '*';
55
56
    /**
57
     * Query scopes.
58
     *
59
     * @var \Yajra\DataTables\Contracts\DataTableScope[]
60
     */
61
    protected $scopes = [];
62
63
    /**
64
     * Html builder.
65
     *
66
     * @var \Yajra\DataTables\Html\Builder
67
     */
68
    protected $htmlBuilder;
69
70
    /**
71
     * Html builder extension callback.
72
     *
73
     * @var callable
74
     */
75
    protected $htmlCallback;
76
77
    /**
78
     * Export filename.
79
     *
80
     * @var string
81
     */
82
    protected $filename = '';
83
84
    /**
85
     * Custom attributes set on the class.
86
     *
87
     * @var array
88
     */
89
    protected $attributes = [];
90
91
    /**
92
     * Callback before sending the response.
93
     *
94
     * @var callable
95
     */
96
    protected $beforeCallback;
97
98
    /**
99
     * Callback after processing the response.
100
     *
101
     * @var callable
102
     */
103
    protected $responseCallback;
104
105
    /**
106
     * Available button actions. When calling an action, the value will be used
107
     * as the function name (so it should be available)
108
     * If you want to add or disable an action, overload and modify this property.
109
     *
110
     * @var array
111
     */
112
    protected $actions = ['print', 'csv', 'excel', 'pdf'];
113
114
    /**
115
     * @var \Yajra\DataTables\Utilities\Request
116
     */
117
    protected $request;
118
119
    /**
120
     * Export class handler.
121
     *
122
     * @var string
123
     */
124
    protected $exportClass = DataTablesExportHandler::class;
125
126
    /**
127
     * CSV export type writer.
128
     *
129
     * @var string
130
     */
131
    protected $csvWriter = 'Csv';
132
133
    /**
134
     * Excel export type writer.
135
     *
136
     * @var string
137
     */
138
    protected $excelWriter = 'Xlsx';
139
140
    /**
141
     * PDF export type writer.
142
     *
143
     * @var string
144
     */
145
    protected $pdfWriter = 'Dompdf';
146
147
    /**
148
     * Process dataTables needed render output.
149
     *
150
     * @param string $view
151
     * @param array $data
152
     * @param array $mergeData
153
     * @return mixed
154
     */
155
    public function render($view, $data = [], $mergeData = [])
156
    {
157
        if ($this->request()->ajax() && $this->request()->wantsJson()) {
158
            return app()->call([$this, 'ajax']);
159
        }
160
161
        if ($action = $this->request()->get('action') and in_array($action, $this->actions)) {
162
            if ($action == 'print') {
163
                return app()->call([$this, 'printPreview']);
164
            }
165
166
            return app()->call([$this, $action]);
167
        }
168
169
        return view($view, $data, $mergeData)->with($this->dataTableVariable, $this->getHtmlBuilder());
170
    }
171
172
    /**
173
     * Get DataTables Request instance.
174
     *
175
     * @return \Yajra\DataTables\Utilities\Request
176
     */
177
    public function request()
178
    {
179
        return $this->request ?: $this->request = app('datatables.request');
180
    }
181
182
    /**
183
     * Display ajax response.
184
     *
185
     * @return \Illuminate\Http\JsonResponse
186
     */
187
    public function ajax()
188
    {
189
        $query = null;
190
        if (method_exists($this, 'query')) {
191
            $query = app()->call([$this, 'query']);
192
            $query = $this->applyScopes($query);
193
        }
194
195
        /** @var \Yajra\DataTables\DataTableAbstract $dataTable */
196
        $dataTable = app()->call([$this, 'dataTable'], compact('query'));
197
198
        if ($callback = $this->beforeCallback) {
199
            $callback($dataTable);
200
        }
201
202
        if ($callback = $this->responseCallback) {
203
            $data = new Collection($dataTable->toArray());
204
205
            return new JsonResponse($callback($data));
206
        }
207
208
        return $dataTable->toJson();
209
    }
210
211
    /**
212
     * Display printable view of datatables.
213
     *
214
     * @return \Illuminate\Contracts\View\View
215
     */
216
    public function printPreview()
217
    {
218
        $data = $this->getDataForPrint();
219
220
        return view($this->printPreview, compact('data'));
221
    }
222
223
    /**
224
     * Get mapped columns versus final decorated output.
225
     *
226
     * @return array
227
     */
228
    protected function getDataForPrint()
229
    {
230
        $columns = $this->printColumns();
231
232
        return $this->mapResponseToColumns($columns, 'printable');
233
    }
234
235
    /**
236
     * Get printable columns.
237
     *
238
     * @return array|string
239
     */
240
    protected function printColumns()
241
    {
242
        return is_array($this->printColumns) ? $this->toColumnsCollection($this->printColumns) : $this->getPrintColumnsFromBuilder();
243
    }
244
245
    /**
246
     * Get filtered print columns definition from html builder.
247
     *
248
     * @return \Illuminate\Support\Collection
249
     */
250
    protected function getPrintColumnsFromBuilder()
251
    {
252
        return $this->html()->removeColumn(...$this->excludeFromPrint)->getColumns();
253
    }
254
255
    /**
256
     * Get filtered export columns definition from html builder.
257
     *
258
     * @return \Illuminate\Support\Collection
259
     */
260
    protected function getExportColumnsFromBuilder()
261
    {
262
        return $this->html()->removeColumn(...$this->excludeFromExport)->getColumns();
263
    }
264
265
    /**
266
     * Get columns definition from html builder.
267
     *
268
     * @return \Illuminate\Support\Collection
269
     */
270
    protected function getColumnsFromBuilder()
271
    {
272
        return $this->html()->getColumns();
273
    }
274
275
    /**
276
     * Optional method if you want to use html builder.
277
     *
278
     * @return \Yajra\DataTables\Html\Builder
279
     */
280
    public function html()
281
    {
282
        return $this->builder();
283
    }
284
285
    /**
286
     * Get DataTables Html Builder instance.
287
     *
288
     * @return \Yajra\DataTables\Html\Builder
289
     */
290
    public function builder()
291
    {
292
        if ($this->htmlBuilder) {
293
            return $this->htmlBuilder;
294
        }
295
296
        if (method_exists($this, 'htmlBuilder')) {
297
            return $this->htmlBuilder = app()->call([$this, 'htmlBuilder']);
298
        }
299
300
        return $this->htmlBuilder = app('datatables.html');
301
    }
302
303
    /**
304
     * Map ajax response to columns definition.
305
     *
306
     * @param mixed $columns
307
     * @param string $type
308
     * @return array
309
     */
310
    protected function mapResponseToColumns($columns, $type)
311
    {
312
        $transformer = new DataArrayTransformer;
313
314
        return array_map(function ($row) use ($columns, $type, $transformer) {
315
            return $transformer->transform($row, $columns, $type);
316
        }, $this->getAjaxResponseData());
317
    }
318
319
    /**
320
     * Get decorated data as defined in datatables ajax response.
321
     *
322
     * @return array
323
     */
324
    protected function getAjaxResponseData()
325
    {
326
        $this->request()->merge(['length' => -1]);
327
328
        $response = app()->call([$this, 'ajax']);
329
        $data     = $response->getData(true);
330
331
        return $data['data'];
332
    }
333
334
    /**
335
     * @return \Yajra\DataTables\Html\Builder
336
     */
337
    protected function getHtmlBuilder()
338
    {
339
        $builder = $this->html();
340
        if ($this->htmlCallback) {
341
            call_user_func($this->htmlCallback, $builder);
342
        }
343
344
        return $builder;
345
    }
346
347
    /**
348
     * Add html builder callback hook.
349
     *
350
     * @param callable $callback
351
     * @return $this
352
     */
353
    public function withHtml(callable $callback)
354
    {
355
        $this->htmlCallback = $callback;
356
357
        return $this;
358
    }
359
360
    /**
361
     * Add callback before sending the response.
362
     *
363
     * @param callable $callback
364
     * @return $this
365
     */
366
    public function before(callable $callback)
367
    {
368
        $this->beforeCallback = $callback;
369
370
        return $this;
371
    }
372
373
    /**
374
     * Add callback after the response was processed.
375
     *
376
     * @param callable $callback
377
     * @return $this
378
     */
379
    public function response(callable $callback)
380
    {
381
        $this->responseCallback = $callback;
382
383
        return $this;
384
    }
385
386
    /**
387
     * Export results to Excel file.
388
     *
389
     * @return void
390
     */
391
    public function excel()
392
    {
393
        $ext = '.' . strtolower($this->excelWriter);
394
395
        return $this->buildExcelFile()->download($this->getFilename() . $ext, $this->excelWriter);
396
    }
397
398
    /**
399
     * Build excel file and prepare for export.
400
     *
401
     * @return \Maatwebsite\Excel\Concerns\Exportable
0 ignored issues
show
Comprehensibility Bug introduced by
The return type \Maatwebsite\Excel\Concerns\Exportable is a trait, and thus cannot be used for type-hinting in PHP. Maybe consider adding an interface and use that for type-hinting?

In PHP traits cannot be used for type-hinting as they do not define a well-defined structure. This is because any class that uses a trait can rename that trait’s methods.

If you would like to return an object that has a guaranteed set of methods, you could create a companion interface that lists these methods explicitly.

Loading history...
402
     */
403
    protected function buildExcelFile()
404
    {
405
        $dataForExport = collect($this->getDataForExport());
406
407
        return new $this->exportClass($dataForExport);
408
    }
409
410
    /**
411
     * Get export filename.
412
     *
413
     * @return string
414
     */
415
    public function getFilename()
416
    {
417
        return $this->filename ?: $this->filename();
418
    }
419
420
    /**
421
     * Set export filename.
422
     *
423
     * @param string $filename
424
     * @return DataTable
425
     */
426
    public function setFilename($filename)
427
    {
428
        $this->filename = $filename;
429
430
        return $this;
431
    }
432
433
    /**
434
     * Get filename for export.
435
     *
436
     * @return string
437
     */
438
    protected function filename()
439
    {
440
        return class_basename($this) . '_' . date('YmdHis');
441
    }
442
443
    /**
444
     * Get mapped columns versus final decorated output.
445
     *
446
     * @return array
447
     */
448
    protected function getDataForExport()
449
    {
450
        $columns = $this->exportColumns();
451
452
        return $this->mapResponseToColumns($columns, 'exportable');
453
    }
454
455
    /**
456
     * Get export columns definition.
457
     *
458
     * @return array|string
459
     */
460
    private function exportColumns()
461
    {
462
        return is_array($this->exportColumns) ? $this->toColumnsCollection($this->exportColumns) : $this->getExportColumnsFromBuilder();
463
    }
464
465
    /**
466
     * Convert array to collection of Column class.
467
     *
468
     * @param array $columns
469
     * @return Collection
470
     */
471
    private function toColumnsCollection(array $columns)
472
    {
473
        $collection = collect();
474
        foreach ($columns as $column) {
475
            if (isset($column['data'])) {
476
                $column['title'] = $column['title'] ?? $column['data'];
477
                $collection->push(new Column($column));
478
            } else {
479
                $data          = [];
480
                $data['data']  = $column;
481
                $data['title'] = $column;
482
                $collection->push(new Column($data));
483
            }
484
        }
485
486
        return $collection;
487
    }
488
489
    /**
490
     * Export results to CSV file.
491
     *
492
     * @return mixed
493
     */
494
    public function csv()
495
    {
496
        $ext = '.' . strtolower($this->csvWriter);
497
498
        return $this->buildExcelFile()->download($this->getFilename() . $ext, $this->csvWriter);
499
    }
500
501
    /**
502
     * Export results to PDF file.
503
     *
504
     * @return mixed
505
     */
506
    public function pdf()
507
    {
508
        if ('snappy' == config('datatables-buttons.pdf_generator', 'snappy')) {
509
            return $this->snappyPdf();
510
        }
511
512
        return $this->buildExcelFile()->download($this->getFilename() . '.pdf', $this->pdfWriter);
513
    }
514
515
    /**
516
     * PDF version of the table using print preview blade template.
517
     *
518
     * @return mixed
519
     */
520
    public function snappyPdf()
521
    {
522
        /** @var \Barryvdh\Snappy\PdfWrapper $snappy */
523
        $snappy      = app('snappy.pdf.wrapper');
524
        $options     = config('datatables-buttons.snappy.options');
525
        $orientation = config('datatables-buttons.snappy.orientation');
526
527
        $snappy->setOptions($options)->setOrientation($orientation);
528
529
        return $snappy->loadHTML($this->printPreview())->download($this->getFilename() . '.pdf');
530
    }
531
532
    /**
533
     * Add basic array query scopes.
534
     *
535
     * @param \Yajra\DataTables\Contracts\DataTableScope $scope
536
     * @return $this
537
     */
538
    public function addScope(DataTableScope $scope)
539
    {
540
        $this->scopes[] = $scope;
541
542
        return $this;
543
    }
544
545
    /**
546
     * Push multiples scopes to array query scopes.
547
     *
548
     * @param array $scopes
549
     * @return $this
550
     */
551
    public function addScopes(array $scopes)
552
    {
553
        array_push($this->scopes, $scopes);
554
555
        return $this;
556
    }
557
558
    /**
559
     * Set a custom class attribute.
560
     *
561
     * @param mixed $key
562
     * @param mixed|null $value
563
     * @return $this
564
     */
565
    public function with($key, $value = null)
566
    {
567
        if (is_array($key)) {
568
            $this->attributes = array_merge($this->attributes, $key);
569
        } else {
570
            $this->attributes[$key] = $value;
571
        }
572
573
        return $this;
574
    }
575
576
    /**
577
     * Dynamically retrieve the value of an attribute.
578
     *
579
     * @param string $key
580
     * @return mixed|null
581
     */
582
    public function __get($key)
583
    {
584
        if (array_key_exists($key, $this->attributes)) {
585
            return $this->attributes[$key];
586
        }
587
    }
588
589
    /**
590
     * Apply query scopes.
591
     *
592
     * @param \Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder $query
593
     * @return mixed
594
     */
595
    protected function applyScopes($query)
596
    {
597
        foreach ($this->scopes as $scope) {
598
            $scope->apply($query);
599
        }
600
601
        return $query;
602
    }
603
604
    /**
605
     * Determine if the DataTable has scopes.
606
     *
607
     * @param  array $scopes
608
     * @param  bool $validateAll
609
     * @return bool
610
     */
611
    protected function hasScopes(array $scopes, $validateAll = false)
612
    {
613
        $filteredScopes = array_filter($this->scopes, function ($scope) use ($scopes) {
614
            return in_array(get_class($scope), $scopes);
615
        });
616
617
        return $validateAll ? count($filteredScopes) === count($scopes) : ! empty($filteredScopes);
618
    }
619
620
    /**
621
     * Get default builder parameters.
622
     *
623
     * @return array
624
     */
625
    protected function getBuilderParameters()
626
    {
627
        return config('datatables-buttons.parameters');
628
    }
629
}
630