Completed
Push — master ( 875706...2db17d )
by Arjay
06:52
created

Builder::setTitle()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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