Completed
Push — master ( 22b1eb...7f0534 )
by Arjay
01:24
created

Builder::getColumns()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
692
            })->flatten();
693
        }
694
695
        return $this;
696
    }
697
698
    /**
699
     * Minify ajax url generated when using get request
700
     * by deleting unnecessary url params.
701
     *
702
     * @param string $url
703
     * @param string $script
704
     * @param array  $data
705
     * @return $this
706
     */
707
    public function minifiedAjax($url, $script = null, $data = [])
708
    {
709
        $appendData = $this->makeDataScript($data);
710
        $this->ajax = [
711
            'url'  => $url,
712
            'data' => "function(data) {
713
    for (var i = 0, len = data.columns.length; i < len; i++) {
714
        if (! data.columns[i].search.value) delete data.columns[i].search;
715
        if (data.columns[i].searchable === true) delete data.columns[i].searchable;
716
        if (data.columns[i].orderable === true) delete data.columns[i].orderable;
717
        if (data.columns[i].data === data.columns[i].name) delete data.columns[i].name;
718
    }
719
    delete data.search.regex;
720
    $appendData
721
    $script;
722
}",
723
        ];
724
725
        return $this;
726
    }
727
728
    /**
729
     * Make a data script to be appended on ajax request of dataTables.
730
     *
731
     * @param array $data
732
     * @return string
733
     */
734
    protected function makeDataScript(array $data)
735
    {
736
        $script = '';
737
        foreach ($data as $key => $value) {
738
            $script .= PHP_EOL . "data.{$key} = '{$value}';";
739
        }
740
741
        return $script;
742
    }
743
}
744