Completed
Pull Request — master (#5)
by Ozan
01:47
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 jQuery 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 parseJQueryFunctions(&$parameters, $values = [], $replacements = [])
173
    {
174
        foreach($parameters as $key => &$value) {
175
            if (is_array($value)) {
176
                list($values, $replacements) = $this->parseJQueryFunctions($value, $values, $replacements);
177
            } else {
178
                if (strpos($value, '$.') === 0)
179
                {
180
                    // Store function string.
181
                    $values[] = $value;
182
                    // Replace function string in $foo with a 'unique' special key.
183
                    $value = '%' . $key . '%';
184
                    // Later on, we'll look for the value, and replace it.
185
                    $replacements[] = '"' . $value . '"';
186
                }
187
            }
188
        }
189
190
        return [$values, $replacements];
191
    }
192
193
    /**
194
     * Generate DataTables js parameters.
195
     *
196
     * @param  array $attributes
197
     * @return string
198
     */
199
    public function parameterize($attributes = [])
200
    {
201
        $parameters = (new Parameters($attributes))->toArray();
202
203
        list($values, $replacements) = $this->parseJQueryFunctions($parameters);
204
205
        list($ajaxDataFunction, $parameters) = $this->encodeAjaxDataFunction($parameters);
206
        list($columnFunctions, $parameters) = $this->encodeColumnFunctions($parameters);
207
        list($callbackFunctions, $parameters) = $this->encodeCallbackFunctions($parameters);
208
209
        $json = json_encode($parameters);
210
211
        $json = str_replace($replacements, $values, $json);
212
213
        $json = $this->decodeAjaxDataFunction($ajaxDataFunction, $json);
214
        $json = $this->decodeColumnFunctions($columnFunctions, $json);
215
        $json = $this->decodeCallbackFunctions($callbackFunctions, $json);
216
217
        return $json;
218
    }
219
220
    /**
221
     * Encode ajax data function param.
222
     *
223
     * @param array $parameters
224
     * @return mixed
225
     */
226
    protected function encodeAjaxDataFunction($parameters)
227
    {
228
        $ajaxData = '';
229
        if (isset($parameters['ajax']['data'])) {
230
            $ajaxData                   = $parameters['ajax']['data'];
231
            $parameters['ajax']['data'] = "#ajax_data#";
232
        }
233
234
        return [$ajaxData, $parameters];
235
    }
236
237
    /**
238
     * Encode columns render function.
239
     *
240
     * @param array $parameters
241
     * @return array
242
     */
243
    protected function encodeColumnFunctions(array $parameters)
244
    {
245
        $columnFunctions = [];
246
        foreach ($parameters['columns'] as $i => $column) {
247
            unset($parameters['columns'][$i]['exportable']);
248
            unset($parameters['columns'][$i]['printable']);
249
            unset($parameters['columns'][$i]['footer']);
250
251
            if (isset($column['render'])) {
252
                $columnFunctions[$i]                 = $column['render'];
253
                $parameters['columns'][$i]['render'] = "#column_function.{$i}#";
254
            }
255
        }
256
257
        return [$columnFunctions, $parameters];
258
    }
259
260
    /**
261
     * Encode DataTables callbacks function.
262
     *
263
     * @param array $parameters
264
     * @return array
265
     */
266
    protected function encodeCallbackFunctions(array $parameters)
267
    {
268
        $callbackFunctions = [];
269
        foreach ($parameters as $key => $callback) {
270
            if (in_array($key, $this->validCallbacks)) {
271
                $callbackFunctions[$key] = $this->compileCallback($callback);
272
                $parameters[$key]        = "#callback_function.{$key}#";
273
            }
274
        }
275
276
        return [$callbackFunctions, $parameters];
277
    }
278
279
    /**
280
     * Compile DataTable callback value.
281
     *
282
     * @param mixed $callback
283
     * @return mixed|string
284
     */
285
    private function compileCallback($callback)
286
    {
287
        if (is_callable($callback)) {
288
            return value($callback);
289
        } elseif ($this->view->exists($callback)) {
290
            return $this->view->make($callback)->render();
291
        }
292
293
        return $callback;
294
    }
295
296
    /**
297
     * Decode ajax data method.
298
     *
299
     * @param string $function
300
     * @param string $json
301
     * @return string
302
     */
303
    protected function decodeAjaxDataFunction($function, $json)
304
    {
305
        return str_replace("\"#ajax_data#\"", $function, $json);
306
    }
307
308
    /**
309
     * Decode columns render functions.
310
     *
311
     * @param array $columnFunctions
312
     * @param string $json
313
     * @return string
314
     */
315
    protected function decodeColumnFunctions(array $columnFunctions, $json)
316
    {
317
        foreach ($columnFunctions as $i => $function) {
318
            $json = str_replace("\"#column_function.{$i}#\"", $function, $json);
319
        }
320
321
        return $json;
322
    }
323
324
    /**
325
     * Decode DataTables callbacks function.
326
     *
327
     * @param array $callbackFunctions
328
     * @param string $json
329
     * @return string
330
     */
331
    protected function decodeCallbackFunctions(array $callbackFunctions, $json)
332
    {
333
        foreach ($callbackFunctions as $i => $function) {
334
            $json = str_replace("\"#callback_function.{$i}#\"", $function, $json);
335
        }
336
337
        return $json;
338
    }
339
340
    /**
341
     * Get javascript template to use.
342
     *
343
     * @return string
344
     */
345
    protected function template()
346
    {
347
        return $this->view->make(
348
            $this->template ?: $this->config->get('datatables.script_template', 'datatables::script')
349
        )->render();
350
    }
351
352
    /**
353
     * Sets HTML table attribute(s).
354
     *
355
     * @param string|array $attribute
356
     * @param mixed $value
357
     * @return $this
358
     */
359
    public function setTableAttribute($attribute, $value = null)
360
    {
361
        if (is_array($attribute)) {
362
            $this->setTableAttributes($attribute);
363
        } else {
364
            $this->tableAttributes[$attribute] = $value;
365
        }
366
367
        return $this;
368
    }
369
370
    /**
371
     * Sets multiple HTML table attributes at once.
372
     *
373
     * @param array $attributes
374
     * @return $this
375
     */
376
    public function setTableAttributes(array $attributes)
377
    {
378
        foreach ($attributes as $attribute => $value) {
379
            $this->setTableAttribute($attribute, $value);
380
        }
381
382
        return $this;
383
    }
384
385
    /**
386
     * Retrieves HTML table attribute value.
387
     *
388
     * @param string $attribute
389
     * @return mixed
390
     * @throws \Exception
391
     */
392
    public function getTableAttribute($attribute)
393
    {
394
        if (! array_key_exists($attribute, $this->tableAttributes)) {
395
            throw new \Exception("Table attribute '{$attribute}' does not exist.");
396
        }
397
398
        return $this->tableAttributes[$attribute];
399
    }
400
401
    /**
402
     * Add a column in collection using attributes.
403
     *
404
     * @param  array $attributes
405
     * @return $this
406
     */
407
    public function addColumn(array $attributes)
408
    {
409
        $this->collection->push(new Column($attributes));
410
411
        return $this;
412
    }
413
414
    /**
415
     * Add a Column object in collection.
416
     *
417
     * @param \Yajra\Datatables\Html\Column $column
418
     * @return $this
419
     */
420
    public function add(Column $column)
421
    {
422
        $this->collection->push($column);
423
424
        return $this;
425
    }
426
427
    /**
428
     * Set datatables columns from array definition.
429
     *
430
     * @param array $columns
431
     * @return $this
432
     */
433
    public function columns(array $columns)
434
    {
435
        foreach ($columns as $key => $value) {
436
            if (! is_a($value, Column::class)) {
437
                if (is_array($value)) {
438
                    $attributes = array_merge(['name' => $key, 'data' => $key], $this->setTitle($key, $value));
439
                } else {
440
                    $attributes = [
441
                        'name'  => $value,
442
                        'data'  => $value,
443
                        'title' => $this->getQualifiedTitle($value),
444
                    ];
445
                }
446
447
                $this->collection->push(new Column($attributes));
448
            } else {
449
                $this->collection->push($value);
450
            }
451
        }
452
453
        return $this;
454
    }
455
456
    /**
457
     * Set title attribute of an array if not set.
458
     *
459
     * @param string $title
460
     * @param array $attributes
461
     * @return array
462
     */
463
    public function setTitle($title, array $attributes)
464
    {
465
        if (! isset($attributes['title'])) {
466
            $attributes['title'] = $this->getQualifiedTitle($title);
467
        }
468
469
        return $attributes;
470
    }
471
472
    /**
473
     * Convert string into a readable title.
474
     *
475
     * @param string $title
476
     * @return string
477
     */
478
    public function getQualifiedTitle($title)
479
    {
480
        return Str::title(str_replace(['.', '_'], ' ', Str::snake($title)));
481
    }
482
483
    /**
484
     * Add a checkbox column.
485
     *
486
     * @param  array $attributes
487
     * @return $this
488
     */
489
    public function addCheckbox(array $attributes = [])
490
    {
491
        $attributes = array_merge([
492
            'defaultContent' => '<input type="checkbox" ' . $this->html->attributes($attributes) . '/>',
493
            'title'          => $this->form->checkbox('', '', false, ['id' => 'dataTablesCheckbox']),
494
            'data'           => 'checkbox',
495
            'name'           => 'checkbox',
496
            'orderable'      => false,
497
            'searchable'     => false,
498
            'exportable'     => false,
499
            'printable'      => true,
500
            'width'          => '10px',
501
        ], $attributes);
502
        $this->collection->push(new Column($attributes));
503
504
        return $this;
505
    }
506
507
    /**
508
     * Add a action column.
509
     *
510
     * @param  array $attributes
511
     * @return $this
512
     */
513
    public function addAction(array $attributes = [])
514
    {
515
        $attributes = array_merge([
516
            'defaultContent' => '',
517
            'data'           => 'action',
518
            'name'           => 'action',
519
            'title'          => 'Action',
520
            'render'         => null,
521
            'orderable'      => false,
522
            'searchable'     => false,
523
            'exportable'     => false,
524
            'printable'      => true,
525
            'footer'         => '',
526
        ], $attributes);
527
        $this->collection->push(new Column($attributes));
528
529
        return $this;
530
    }
531
532
    /**
533
     * Add a index column.
534
     *
535
     * @param  array $attributes
536
     * @return $this
537
     */
538
    public function addIndex(array $attributes = [])
539
    {
540
        $indexColumn = Config::get('datatables.index_column', 'DT_Row_Index');
541
        $attributes  = array_merge([
542
            'defaultContent' => '',
543
            'data'           => $indexColumn,
544
            'name'           => $indexColumn,
545
            'title'          => '',
546
            'render'         => null,
547
            'orderable'      => false,
548
            'searchable'     => false,
549
            'exportable'     => false,
550
            'printable'      => true,
551
            'footer'         => '',
552
        ], $attributes);
553
        $this->collection->push(new Column($attributes));
554
555
        return $this;
556
    }
557
558
    /**
559
     * Setup ajax parameter for datatables pipeline plugin
560
     *
561
     * @param  string $url
562
     * @param  string $pages
563
     * @return $this
564
     */
565
    public function pipeline($url, $pages)
566
    {
567
        $url .= $this->getQueryString();
568
569
        $this->ajax = "$.fn.dataTable.pipeline({ url: '{$url}', pages: {$pages} })";
570
571
        return $this;
572
    }
573
574
    public function getQueryString()
575
    {
576
        return $this->hasCustomTableId() ? '?'.http_build_query([
577
            'tableId' => $this->tableAttributes['id'],
578
        ]) : '';
579
    }
580
581
    /**
582
     * Setup ajax parameter
583
     *
584
     * @param  string|array $attributes
585
     * @return $this
586
     */
587
    public function ajax($attributes)
588
    {
589
        $this->ajax = $attributes;
590
591
        $queryString = $this->getQueryString();
592
593
        if (is_array($this->ajax)) {
594
            $this->ajax['url'] = $this->ajax['url'].$queryString;
595
        } else {
596
            $this->ajax = $this->ajax.$queryString;
597
        }
598
599
        return $this;
600
    }
601
602
    /**
603
     * Generate DataTable's table html.
604
     *
605
     * @param array $attributes
606
     * @param bool $drawFooter
607
     * @return string
608
     */
609
    public function table(array $attributes = [], $drawFooter = false)
610
    {
611
        $this->tableAttributes = array_merge($this->tableAttributes, $attributes);
612
613
        $th       = $this->compileTableHeaders();
614
        $htmlAttr = $this->html->attributes($this->tableAttributes);
615
616
        $tableHtml = '<table ' . $htmlAttr . '>';
617
        $tableHtml .= '<thead><tr>' . implode('', $th) . '</tr></thead>';
618
        if ($drawFooter) {
619
            $tf = $this->compileTableFooter();
620
            $tableHtml .= '<tfoot><tr>' . implode('', $tf) . '</tr></tfoot>';
621
        }
622
        $tableHtml .= '</table>';
623
624
        return $tableHtml;
625
    }
626
627
    /**
628
     * Compile table headers and to support responsive extension.
629
     *
630
     * @return array
631
     */
632
    private function compileTableHeaders()
633
    {
634
        $th = [];
635
        foreach ($this->collection->toArray() as $row) {
636
            $thAttr = $this->html->attributes(array_merge(
637
                array_only($row, ['class', 'id', 'width', 'style', 'data-class', 'data-hide']),
638
                $row['attributes']
639
            ));
640
            $th[]   = '<th ' . $thAttr . '>' . $row['title'] . '</th>';
641
        }
642
643
        return $th;
644
    }
645
646
    /**
647
     * Compile table footer contents.
648
     *
649
     * @return array
650
     */
651
    private function compileTableFooter()
652
    {
653
        $footer = [];
654
        foreach ($this->collection->toArray() as $row) {
655
            $footer[] = '<th>' . $row['footer'] . '</th>';
656
        }
657
658
        return $footer;
659
    }
660
661
    /**
662
     * Configure DataTable's parameters.
663
     *
664
     * @param  array $attributes
665
     * @return $this
666
     */
667
    public function parameters(array $attributes = [])
668
    {
669
        $this->attributes = array_merge($this->attributes, $attributes);
670
671
        return $this;
672
    }
673
674
    /**
675
     * Set custom javascript template.
676
     *
677
     * @param string $template
678
     * @return $this
679
     */
680
    public function setTemplate($template)
681
    {
682
        $this->template = $template;
683
684
        return $this;
685
    }
686
687
    /**
688
     * Get collection of columns.
689
     *
690
     * @return \Illuminate\Support\Collection
691
     */
692
    public function getColumns()
693
    {
694
        return $this->collection;
695
    }
696
}
697