Completed
Pull Request — master (#13)
by Arjay
01:24
created

Builder::encodeCallbackFunctions()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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