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

Builder::addIndex()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 19
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 19
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 16
nc 1
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
        if (!$this->hasCustomTableId()) {
576
            return '';
577
        }
578
579
        $url = is_array($ajax) ? $this->ajax['url'] : $this->ajax;
0 ignored issues
show
Bug introduced by
The variable $ajax does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
580
581
        $hasQueryString = str_contains($url, ['?']);
582
        $separator = $hasQueryString ? '&' : '?';
583
584
        return $separator.http_build_query([
585
            'tableId' => $this->tableAttributes['id'],
586
        ]);
587
    }
588
589
    /**
590
     * Setup ajax parameter
591
     *
592
     * @param  string|array $attributes
593
     * @return $this
594
     */
595
    public function ajax($attributes)
596
    {
597
        $this->ajax = $attributes;
598
599
        $queryString = $this->getQueryString();
600
601
        if (is_array($this->ajax)) {
602
            $this->ajax['url'] = $this->ajax['url'].$queryString;
603
        } else {
604
            $this->ajax = $this->ajax.$queryString;
605
        }
606
607
        return $this;
608
    }
609
610
    /**
611
     * Generate DataTable's table html.
612
     *
613
     * @param array $attributes
614
     * @param bool $drawFooter
615
     * @return string
616
     */
617
    public function table(array $attributes = [], $drawFooter = false)
618
    {
619
        $this->tableAttributes = array_merge($this->tableAttributes, $attributes);
620
621
        $th       = $this->compileTableHeaders();
622
        $htmlAttr = $this->html->attributes($this->tableAttributes);
623
624
        $tableHtml = '<table ' . $htmlAttr . '>';
625
        $tableHtml .= '<thead><tr>' . implode('', $th) . '</tr></thead>';
626
        if ($drawFooter) {
627
            $tf = $this->compileTableFooter();
628
            $tableHtml .= '<tfoot><tr>' . implode('', $tf) . '</tr></tfoot>';
629
        }
630
        $tableHtml .= '</table>';
631
632
        return $tableHtml;
633
    }
634
635
    /**
636
     * Compile table headers and to support responsive extension.
637
     *
638
     * @return array
639
     */
640
    private function compileTableHeaders()
641
    {
642
        $th = [];
643
        foreach ($this->collection->toArray() as $row) {
644
            $thAttr = $this->html->attributes(array_merge(
645
                array_only($row, ['class', 'id', 'width', 'style', 'data-class', 'data-hide']),
646
                $row['attributes']
647
            ));
648
            $th[]   = '<th ' . $thAttr . '>' . $row['title'] . '</th>';
649
        }
650
651
        return $th;
652
    }
653
654
    /**
655
     * Compile table footer contents.
656
     *
657
     * @return array
658
     */
659
    private function compileTableFooter()
660
    {
661
        $footer = [];
662
        foreach ($this->collection->toArray() as $row) {
663
            $footer[] = '<th>' . $row['footer'] . '</th>';
664
        }
665
666
        return $footer;
667
    }
668
669
    /**
670
     * Configure DataTable's parameters.
671
     *
672
     * @param  array $attributes
673
     * @return $this
674
     */
675
    public function parameters(array $attributes = [])
676
    {
677
        $this->attributes = array_merge($this->attributes, $attributes);
678
679
        return $this;
680
    }
681
682
    /**
683
     * Set custom javascript template.
684
     *
685
     * @param string $template
686
     * @return $this
687
     */
688
    public function setTemplate($template)
689
    {
690
        $this->template = $template;
691
692
        return $this;
693
    }
694
695
    /**
696
     * Get collection of columns.
697
     *
698
     * @return \Illuminate\Support\Collection
699
     */
700
    public function getColumns()
701
    {
702
        return $this->collection;
703
    }
704
}
705