Completed
Pull Request — master (#5)
by Ozan
01:50
created

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