Completed
Push — master ( 437d23...b8ed8b )
by Arjay
01:32
created

Builder::getTableAttributes()   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
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 0
1
<?php
2
3
namespace Yajra\Datatables\Html;
4
5
use Collective\Html\HtmlBuilder;
6
use Illuminate\Contracts\Config\Repository;
7
use Illuminate\Contracts\View\Factory;
8
use Illuminate\Support\Collection;
9
use Illuminate\Support\HtmlString;
10
use Illuminate\Support\Str;
11
12
/**
13
 * Class Builder.
14
 *
15
 * @package Yajra\Datatables\Html
16
 * @author  Arjay Angeles <[email protected]>
17
 */
18
class Builder
19
{
20
    /**
21
     * @var Collection
22
     */
23
    public $collection;
24
25
    /**
26
     * @var Repository
27
     */
28
    public $config;
29
30
    /**
31
     * @var Factory
32
     */
33
    public $view;
34
35
    /**
36
     * @var HtmlBuilder
37
     */
38
    public $html;
39
40
    /**
41
     * @var string|array
42
     */
43
    protected $ajax = '';
44
45
    /**
46
     * @var array
47
     */
48
    protected $tableAttributes = [];
49
50
    /**
51
     * @var string
52
     */
53
    protected $template = '';
54
55
    /**
56
     * @var array
57
     */
58
    protected $attributes = [];
59
60
    /**
61
     * Lists of valid DataTables Callbacks.
62
     *
63
     * @link https://datatables.net/reference/option/.
64
     * @var array
65
     */
66
    protected $validCallbacks = [
67
        'createdRow',
68
        'drawCallback',
69
        'footerCallback',
70
        'formatNumber',
71
        'headerCallback',
72
        'infoCallback',
73
        'initComplete',
74
        'preDrawCallback',
75
        'rowCallback',
76
        'stateLoadCallback',
77
        'stateLoaded',
78
        'stateLoadParams',
79
        'stateSaveCallback',
80
        'stateSaveParams',
81
    ];
82
83
    /**
84
     * @param Repository  $config
85
     * @param Factory     $view
86
     * @param HtmlBuilder $html
87
     */
88
    public function __construct(Repository $config, Factory $view, HtmlBuilder $html)
89
    {
90
        $this->config     = $config;
91
        $this->view       = $view;
92
        $this->html       = $html;
93
        $this->collection = new Collection;
94
    }
95
96
    /**
97
     * Generate DataTable javascript.
98
     *
99
     * @param  null  $script
100
     * @param  array $attributes
101
     * @return \Illuminate\Support\HtmlString
102
     */
103
    public function scripts($script = null, array $attributes = ['type' => 'text/javascript'])
104
    {
105
        $script     = $script ?: $this->generateScripts();
106
        $attributes = $this->html->attributes($attributes);
107
108
        return new HtmlString("<script{$attributes}>{$script}</script>\n");
109
    }
110
111
    /**
112
     * Get generated raw scripts.
113
     *
114
     * @return \Illuminate\Support\HtmlString
115
     */
116
    public function generateScripts()
117
    {
118
        $args = array_merge(
119
            $this->attributes, [
120
                'ajax'    => $this->ajax,
121
                'columns' => $this->collection->toArray(),
122
            ]
123
        );
124
125
        $parameters = $this->parameterize($args);
126
127
        return new HtmlString(
128
            sprintf($this->template(), $this->tableAttributes['id'], $parameters)
129
        );
130
    }
131
132
    /**
133
     * Generate DataTables js parameters.
134
     *
135
     * @param  array $attributes
136
     * @return string
137
     */
138
    public function parameterize($attributes = [])
139
    {
140
        $parameters = (new Parameters($attributes))->toArray();
141
142
        $values       = [];
143
        $replacements = [];
144
        foreach ($parameters as $key => &$value) {
145
            if (!is_array($value)) {
146
                if (strpos($value, '$.') === 0) {
147
                    // Store function string.
148
                    $values[] = $value;
149
                    // Replace function string in $foo with a 'unique' special key.
150
                    $value = '%' . $key . '%';
151
                    // Later on, we'll look for the value, and replace it.
152
                    $replacements[] = '"' . $value . '"';
153
                }
154
            }
155
        }
156
157
        list($ajaxDataFunction, $parameters) = $this->encodeAjaxDataFunction($parameters);
158
        list($columnFunctions, $parameters) = $this->encodeColumnFunctions($parameters);
159
        list($callbackFunctions, $parameters) = $this->encodeCallbackFunctions($parameters);
160
161
        $json = json_encode($parameters);
162
163
        $json = str_replace($replacements, $values, $json);
164
165
        $json = $this->decodeAjaxDataFunction($ajaxDataFunction, $json);
166
        $json = $this->decodeColumnFunctions($columnFunctions, $json);
167
        $json = $this->decodeCallbackFunctions($callbackFunctions, $json);
168
169
        return $json;
170
    }
171
172
    /**
173
     * Encode ajax data function param.
174
     *
175
     * @param array $parameters
176
     * @return mixed
177
     */
178
    protected function encodeAjaxDataFunction($parameters)
179
    {
180
        $ajaxData = '';
181
        $ajax     = $parameters['ajax'];
182
        if (isset($ajax['data'])) {
183
            $ajaxData     = $ajax['data'];
184
            $ajax['data'] = "#ajax_data#";
185
        }
186
187
        return [$ajaxData, $parameters];
188
    }
189
190
    /**
191
     * Encode columns render function.
192
     *
193
     * @param array $parameters
194
     * @return array
195
     */
196
    protected function encodeColumnFunctions(array $parameters)
197
    {
198
        $fn      = [];
199
        $columns = $parameters['columns'];
200
201
        foreach ($columns as $i => $column) {
202
            unset($columns[$i]['exportable']);
203
            unset($columns[$i]['printable']);
204
            unset($columns[$i]['footer']);
205
206
            if (isset($column['render'])) {
207
                $fn[$i]                = $column['render'];
208
                $columns[$i]['render'] = "#column_function.{$i}#";
209
            }
210
        }
211
212
        return [$fn, $parameters];
213
    }
214
215
    /**
216
     * Encode DataTables callbacks function.
217
     *
218
     * @param array $parameters
219
     * @return array
220
     */
221
    protected function encodeCallbackFunctions(array $parameters)
222
    {
223
        $callbackFunctions = [];
224
        foreach ($parameters as $key => $callback) {
225
            if (in_array($key, $this->validCallbacks)) {
226
                $callbackFunctions[$key] = $this->compileCallback($callback);
227
                $parameters[$key]        = "#callback_function.{$key}#";
228
            }
229
        }
230
231
        return [$callbackFunctions, $parameters];
232
    }
233
234
    /**
235
     * Compile DataTable callback value.
236
     *
237
     * @param mixed $callback
238
     * @return mixed|string
239
     */
240
    private function compileCallback($callback)
241
    {
242
        if (is_callable($callback)) {
243
            return value($callback);
244
        } elseif ($this->view->exists($callback)) {
245
            return $this->view->make($callback)->render();
246
        }
247
248
        return $callback;
249
    }
250
251
    /**
252
     * Decode ajax data method.
253
     *
254
     * @param string $function
255
     * @param string $json
256
     * @return string
257
     */
258
    protected function decodeAjaxDataFunction($function, $json)
259
    {
260
        return str_replace("\"#ajax_data#\"", $function, $json);
261
    }
262
263
    /**
264
     * Decode columns render functions.
265
     *
266
     * @param array  $columnFunctions
267
     * @param string $json
268
     * @return string
269
     */
270
    protected function decodeColumnFunctions(array $columnFunctions, $json)
271
    {
272
        foreach ($columnFunctions as $i => $function) {
273
            $json = str_replace("\"#column_function.{$i}#\"", $function, $json);
274
        }
275
276
        return $json;
277
    }
278
279
    /**
280
     * Decode DataTables callbacks function.
281
     *
282
     * @param array  $callbackFunctions
283
     * @param string $json
284
     * @return string
285
     */
286
    protected function decodeCallbackFunctions(array $callbackFunctions, $json)
287
    {
288
        foreach ($callbackFunctions as $i => $function) {
289
            $json = str_replace("\"#callback_function.{$i}#\"", $function, $json);
290
        }
291
292
        return $json;
293
    }
294
295
    /**
296
     * Get javascript template to use.
297
     *
298
     * @return string
299
     */
300
    protected function template()
301
    {
302
        return $this->view->make(
303
            $this->template ?: $this->config->get('datatables.script_template', 'datatables::script')
304
        )->render();
305
    }
306
307
    /**
308
     * Sets HTML table attribute(s).
309
     *
310
     * @param string|array $attribute
311
     * @param mixed        $value
312
     * @return $this
313
     */
314
    public function setTableAttribute($attribute, $value = null)
315
    {
316
        if (is_array($attribute)) {
317
            $this->setTableAttributes($attribute);
318
        } else {
319
            $this->tableAttributes[$attribute] = $value;
320
        }
321
322
        return $this;
323
    }
324
325
    /**
326
     * Retrieves HTML table attribute value.
327
     *
328
     * @param string $attribute
329
     * @return mixed
330
     * @throws \Exception
331
     */
332
    public function getTableAttribute($attribute)
333
    {
334
        if (!array_key_exists($attribute, $this->tableAttributes)) {
335
            throw new \Exception("Table attribute '{$attribute}' does not exist.");
336
        }
337
338
        return $this->tableAttributes[$attribute];
339
    }
340
341
    /**
342
     * Add a column in collection usingsl attributes.
343
     *
344
     * @param  array $attributes
345
     * @return $this
346
     */
347
    public function addColumn(array $attributes)
348
    {
349
        $this->collection->push(new Column($attributes));
350
351
        return $this;
352
    }
353
354
    /**
355
     * Add a Column object at the beginning of collection
356
     *
357
     * @param \Yajra\Datatables\Html\Column $column
358
     * @return $this
359
     */
360
    public function addBefore(Column $column)
361
    {
362
        $this->collection->prepend($column);
363
364
        return $this;
365
    }
366
367
    /**
368
     * Add a column at the beginning of collection using attributes.
369
     *
370
     * @param  array $attributes
371
     * @return $this
372
     */
373
    public function addColumnBefore(array $attributes)
374
    {
375
        $this->collection->prepend(new Column($attributes));
376
377
        return $this;
378
    }
379
380
    /**
381
     * Add a Column object in collection.
382
     *
383
     * @param \Yajra\Datatables\Html\Column $column
384
     * @return $this
385
     */
386
    public function add(Column $column)
387
    {
388
        $this->collection->push($column);
389
390
        return $this;
391
    }
392
393
    /**
394
     * Set datatables columns from array definition.
395
     *
396
     * @param array $columns
397
     * @return $this
398
     */
399
    public function columns(array $columns)
400
    {
401
        $this->collection = new Collection;
402
403
        foreach ($columns as $key => $value) {
404
            if (!is_a($value, Column::class)) {
405
                if (is_array($value)) {
406
                    $attributes = array_merge(['name' => $key, 'data' => $key], $this->setTitle($key, $value));
407
                } else {
408
                    $attributes = [
409
                        'name'  => $value,
410
                        'data'  => $value,
411
                        'title' => $this->getQualifiedTitle($value),
412
                    ];
413
                }
414
415
                $this->collection->push(new Column($attributes));
416
            } else {
417
                $this->collection->push($value);
418
            }
419
        }
420
421
        return $this;
422
    }
423
424
    /**
425
     * Set title attribute of an array if not set.
426
     *
427
     * @param string $title
428
     * @param array  $attributes
429
     * @return array
430
     */
431
    public function setTitle($title, array $attributes)
432
    {
433
        if (!isset($attributes['title'])) {
434
            $attributes['title'] = $this->getQualifiedTitle($title);
435
        }
436
437
        return $attributes;
438
    }
439
440
    /**
441
     * Convert string into a readable title.
442
     *
443
     * @param string $title
444
     * @return string
445
     */
446
    public function getQualifiedTitle($title)
447
    {
448
        return Str::title(str_replace(['.', '_'], ' ', Str::snake($title)));
449
    }
450
451
    /**
452
     * Add a checkbox column.
453
     *
454
     * @param  array $attributes
455
     * @return $this
456
     */
457
    public function addCheckbox(array $attributes = [])
458
    {
459
        $attributes = array_merge([
460
            'defaultContent' => '<input type="checkbox" ' . $this->html->attributes($attributes) . '/>',
461
            'title'          => '<input type="checkbox" ' . $this->html->attributes($attributes + ['id' => 'dataTablesCheckbox']) . '/>',
462
            'data'           => 'checkbox',
463
            'name'           => 'checkbox',
464
            'orderable'      => false,
465
            'searchable'     => false,
466
            'exportable'     => false,
467
            'printable'      => true,
468
            'width'          => '10px',
469
        ], $attributes);
470
        $this->collection->push(new Column($attributes));
471
472
        return $this;
473
    }
474
475
    /**
476
     * Add a action column.
477
     *
478
     * @param  array $attributes
479
     * @return $this
480
     */
481
    public function addAction(array $attributes = [])
482
    {
483
        $attributes = array_merge([
484
            'defaultContent' => '',
485
            'data'           => 'action',
486
            'name'           => 'action',
487
            'title'          => 'Action',
488
            'render'         => null,
489
            'orderable'      => false,
490
            'searchable'     => false,
491
            'exportable'     => false,
492
            'printable'      => true,
493
            'footer'         => '',
494
        ], $attributes);
495
        $this->collection->push(new Column($attributes));
496
497
        return $this;
498
    }
499
500
    /**
501
     * Add a index column.
502
     *
503
     * @param  array $attributes
504
     * @return $this
505
     */
506
    public function addIndex(array $attributes = [])
507
    {
508
        $indexColumn = config('datatables.index_column', 'DT_Row_Index');
509
        $attributes  = array_merge([
510
            'defaultContent' => '',
511
            'data'           => $indexColumn,
512
            'name'           => $indexColumn,
513
            'title'          => '',
514
            'render'         => null,
515
            'orderable'      => false,
516
            'searchable'     => false,
517
            'exportable'     => false,
518
            'printable'      => true,
519
            'footer'         => '',
520
        ], $attributes);
521
        $this->collection->push(new Column($attributes));
522
523
        return $this;
524
    }
525
526
    /**
527
     * Setup ajax parameter for datatables pipeline plugin
528
     *
529
     * @param  string $url
530
     * @param  string $pages
531
     * @return $this
532
     */
533
    public function pipeline($url, $pages)
534
    {
535
        $this->ajax = "$.fn.dataTable.pipeline({ url: '{$url}', pages: {$pages} })";
536
537
        return $this;
538
    }
539
540
    /**
541
     * Setup ajax parameter
542
     *
543
     * @param  string|array $attributes
544
     * @return $this
545
     */
546
    public function ajax($attributes = '')
547
    {
548
        $this->ajax = $attributes;
549
550
        return $this;
551
    }
552
553
    /**
554
     * Generate DataTable's table html.
555
     *
556
     * @param array $attributes
557
     * @param bool  $drawFooter
558
     * @return \Illuminate\Support\HtmlString
559
     */
560
    public function table(array $attributes = [], $drawFooter = false)
561
    {
562
        $this->tableAttributes = array_merge($this->getTableAttributes(), $attributes);
563
564
        $th       = $this->compileTableHeaders();
565
        $htmlAttr = $this->html->attributes($this->tableAttributes);
566
567
        $tableHtml = '<table ' . $htmlAttr . '>';
568
        $tableHtml .= '<thead><tr>' . implode('', $th) . '</tr></thead>';
569
        if ($drawFooter) {
570
            $tf        = $this->compileTableFooter();
571
            $tableHtml .= '<tfoot><tr>' . implode('', $tf) . '</tr></tfoot>';
572
        }
573
        $tableHtml .= '</table>';
574
575
        return new HtmlString($tableHtml);
576
    }
577
578
    /**
579
     * Get table computed table attributes.
580
     *
581
     * @return array
582
     */
583
    public function getTableAttributes()
584
    {
585
        $default = $this->config->get('datatables-html.table', ['class' => 'table', 'id' => 'dataTableBuilder']);
586
587
        return array_merge($default, $this->tableAttributes);
588
    }
589
590
    /**
591
     * Sets multiple HTML table attributes at once.
592
     *
593
     * @param array $attributes
594
     * @return $this
595
     */
596
    public function setTableAttributes(array $attributes)
597
    {
598
        foreach ($attributes as $attribute => $value) {
599
            $this->setTableAttribute($attribute, $value);
600
        }
601
602
        return $this;
603
    }
604
605
    /**
606
     * Compile table headers and to support responsive extension.
607
     *
608
     * @return array
609
     */
610
    private function compileTableHeaders()
611
    {
612
        $th = [];
613
        foreach ($this->collection->toArray() as $row) {
614
            $thAttr = $this->html->attributes(array_merge(
615
                array_only($row, ['class', 'id', 'width', 'style', 'data-class', 'data-hide']),
616
                $row['attributes']
617
            ));
618
            $th[]   = '<th ' . $thAttr . '>' . $row['title'] . '</th>';
619
        }
620
621
        return $th;
622
    }
623
624
    /**
625
     * Compile table footer contents.
626
     *
627
     * @return array
628
     */
629
    private function compileTableFooter()
630
    {
631
        $footer = [];
632
        foreach ($this->collection->all() as $row) {
633
            $footer[] = '<th>' . $row->footer . '</th>';
634
        }
635
636
        return $footer;
637
    }
638
639
    /**
640
     * Configure DataTable's parameters.
641
     *
642
     * @param  array $attributes
643
     * @return $this
644
     */
645
    public function parameters(array $attributes = [])
646
    {
647
        $this->attributes = array_merge($this->attributes, $attributes);
648
649
        return $this;
650
    }
651
652
    /**
653
     * Set custom javascript template.
654
     *
655
     * @param string $template
656
     * @return $this
657
     */
658
    public function setTemplate($template)
659
    {
660
        $this->template = $template;
661
662
        return $this;
663
    }
664
665
    /**
666
     * Get collection of columns.
667
     *
668
     * @return \Illuminate\Support\Collection
669
     */
670
    public function getColumns()
671
    {
672
        return $this->collection;
673
    }
674
675
    /**
676
     * Remove column by name.
677
     *
678
     * @param array $names
679
     * @return $this
680
     */
681
    public function removeColumn(...$names)
682
    {
683
        foreach ($names as $name) {
684
            $this->collection = $this->collection->filter(function (Column $column) use ($name) {
685
                return $column->name !== $name;
686
            })->flatten();
687
        }
688
689
        return $this;
690
    }
691
692
    /**
693
     * Minify ajax url generated when using get request
694
     * by deleting unnecessary url params.
695
     *
696
     * @param string $url
697
     * @param string $script
698
     * @param array  $data
699
     * @return $this
700
     */
701
    public function minifiedAjax($url = '', $script = null, $data = [])
702
    {
703
        $appendData = $this->makeDataScript($data);
704
        $this->ajax = [
705
            'url'  => $url,
706
            'data' => "function(data) {
707
    for (var i = 0, len = data.columns.length; i < len; i++) {
708
        if (! data.columns[i].search.value) delete data.columns[i].search;
709
        if (data.columns[i].searchable === true) delete data.columns[i].searchable;
710
        if (data.columns[i].orderable === true) delete data.columns[i].orderable;
711
        if (data.columns[i].data === data.columns[i].name) delete data.columns[i].name;
712
    }
713
    delete data.search.regex;
714
    $appendData
715
    $script;
716
}",
717
        ];
718
719
        return $this;
720
    }
721
722
    /**
723
     * Make a data script to be appended on ajax request of dataTables.
724
     *
725
     * @param array $data
726
     * @return string
727
     */
728
    protected function makeDataScript(array $data)
729
    {
730
        $script = '';
731
        foreach ($data as $key => $value) {
732
            $script .= PHP_EOL . "data.{$key} = '{$value}';";
733
        }
734
735
        return $script;
736
    }
737
}
738