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: 16: 17: 18: 19:
20: class Builder
21: {
22: 23: 24:
25: public $collection;
26:
27: 28: 29:
30: public $config;
31:
32: 33: 34:
35: public $view;
36:
37: 38: 39:
40: public $html;
41:
42: 43: 44:
45: public $url;
46:
47: 48: 49:
50: public $form;
51:
52: 53: 54:
55: protected $ajax = '';
56:
57: 58: 59:
60: protected $tableAttributes = ['class' => 'table', 'id' => 'dataTableBuilder'];
61:
62: 63: 64:
65: protected $template = '';
66:
67: 68: 69:
70: protected $attributes = [];
71:
72: 73: 74: 75: 76: 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: 97: 98: 99: 100: 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: 119: 120: 121: 122: 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: 133: 134: 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: 155: 156: 157: 158:
159: public function parameterize($attributes = [])
160: {
161: $parameters = (new Parameters($attributes))->toArray();
162:
163: list($ajaxDataFunction, $parameters) = $this->encodeAjaxDataFunction($parameters);
164: list($columnFunctions, $parameters) = $this->encodeColumnFunctions($parameters);
165: list($callbackFunctions, $parameters) = $this->encodeCallbackFunctions($parameters);
166:
167: $json = json_encode($parameters);
168:
169: $json = $this->decodeAjaxDataFunction($ajaxDataFunction, $json);
170: $json = $this->decodeColumnFunctions($columnFunctions, $json);
171: $json = $this->decodeCallbackFunctions($callbackFunctions, $json);
172:
173: return $json;
174: }
175:
176: 177: 178: 179: 180: 181:
182: protected function encodeAjaxDataFunction($parameters)
183: {
184: $ajaxData = '';
185: if (isset($parameters['ajax']['data'])) {
186: $ajaxData = $parameters['ajax']['data'];
187: $parameters['ajax']['data'] = "#ajax_data#";
188: }
189:
190: return [$ajaxData, $parameters];
191: }
192:
193: 194: 195: 196: 197: 198:
199: protected function encodeColumnFunctions(array $parameters)
200: {
201: $columnFunctions = [];
202: foreach ($parameters['columns'] as $i => $column) {
203: unset($parameters['columns'][$i]['exportable']);
204: unset($parameters['columns'][$i]['printable']);
205: unset($parameters['columns'][$i]['footer']);
206:
207: if (isset($column['render'])) {
208: $columnFunctions[$i] = $column['render'];
209: $parameters['columns'][$i]['render'] = "#column_function.{$i}#";
210: }
211: }
212:
213: return [$columnFunctions, $parameters];
214: }
215:
216: 217: 218: 219: 220: 221:
222: protected function encodeCallbackFunctions(array $parameters)
223: {
224: $callbackFunctions = [];
225: foreach ($parameters as $key => $callback) {
226: if (in_array($key, $this->validCallbacks)) {
227: $callbackFunctions[$key] = $this->compileCallback($callback);
228: $parameters[$key] = "#callback_function.{$key}#";
229: }
230: }
231:
232: return [$callbackFunctions, $parameters];
233: }
234:
235: 236: 237: 238: 239: 240:
241: private function compileCallback($callback)
242: {
243: if (is_callable($callback)) {
244: return value($callback);
245: } elseif ($this->view->exists($callback)) {
246: return $this->view->make($callback)->render();
247: }
248:
249: return $callback;
250: }
251:
252: 253: 254: 255: 256: 257: 258:
259: protected function decodeAjaxDataFunction($function, $json)
260: {
261: return str_replace("\"#ajax_data#\"", $function, $json);
262: }
263:
264: 265: 266: 267: 268: 269: 270:
271: protected function decodeColumnFunctions(array $columnFunctions, $json)
272: {
273: foreach ($columnFunctions as $i => $function) {
274: $json = str_replace("\"#column_function.{$i}#\"", $function, $json);
275: }
276:
277: return $json;
278: }
279:
280: 281: 282: 283: 284: 285: 286:
287: protected function decodeCallbackFunctions(array $callbackFunctions, $json)
288: {
289: foreach ($callbackFunctions as $i => $function) {
290: $json = str_replace("\"#callback_function.{$i}#\"", $function, $json);
291: }
292:
293: return $json;
294: }
295:
296: 297: 298: 299: 300:
301: protected function template()
302: {
303: return $this->view->make(
304: $this->template ?: $this->config->get('datatables.script_template', 'datatables::script')
305: )->render();
306: }
307:
308: 309: 310: 311: 312: 313: 314:
315: public function setTableAttribute($attribute, $value = null)
316: {
317: if (is_array($attribute)) {
318: $this->setTableAttributes($attribute);
319: } else {
320: $this->tableAttributes[$attribute] = $value;
321: }
322:
323: return $this;
324: }
325:
326: 327: 328: 329: 330: 331:
332: public function setTableAttributes(array $attributes)
333: {
334: foreach ($attributes as $attribute => $value) {
335: $this->setTableAttribute($attribute, $value);
336: }
337:
338: return $this;
339: }
340:
341: 342: 343: 344: 345: 346: 347:
348: public function getTableAttribute($attribute)
349: {
350: if (! array_key_exists($attribute, $this->tableAttributes)) {
351: throw new \Exception("Table attribute '{$attribute}' does not exist.");
352: }
353:
354: return $this->tableAttributes[$attribute];
355: }
356:
357: 358: 359: 360: 361: 362:
363: public function addColumn(array $attributes)
364: {
365: $this->collection->push(new Column($attributes));
366:
367: return $this;
368: }
369:
370: 371: 372: 373: 374: 375:
376: public function add(Column $column)
377: {
378: $this->collection->push($column);
379:
380: return $this;
381: }
382:
383: 384: 385: 386: 387: 388:
389: public function columns(array $columns)
390: {
391: foreach ($columns as $key => $value) {
392: if (! is_a($value, Column::class)) {
393: if (is_array($value)) {
394: $attributes = array_merge(['name' => $key, 'data' => $key], $this->setTitle($key, $value));
395: } else {
396: $attributes = [
397: 'name' => $value,
398: 'data' => $value,
399: 'title' => $this->getQualifiedTitle($value),
400: ];
401: }
402:
403: $this->collection->push(new Column($attributes));
404: } else {
405: $this->collection->push($value);
406: }
407: }
408:
409: return $this;
410: }
411:
412: 413: 414: 415: 416: 417: 418:
419: public function setTitle($title, array $attributes)
420: {
421: if (! isset($attributes['title'])) {
422: $attributes['title'] = $this->getQualifiedTitle($title);
423: }
424:
425: return $attributes;
426: }
427:
428: 429: 430: 431: 432: 433:
434: public function getQualifiedTitle($title)
435: {
436: return Str::title(str_replace(['.', '_'], ' ', Str::snake($title)));
437: }
438:
439: 440: 441: 442: 443: 444:
445: public function addCheckbox(array $attributes = [])
446: {
447: $attributes = array_merge([
448: 'defaultContent' => '<input type="checkbox" ' . $this->html->attributes($attributes) . '/>',
449: 'title' => $this->form->checkbox('', '', false, ['id' => 'dataTablesCheckbox']),
450: 'data' => 'checkbox',
451: 'name' => 'checkbox',
452: 'orderable' => false,
453: 'searchable' => false,
454: 'exportable' => false,
455: 'printable' => true,
456: 'width' => '10px',
457: ], $attributes);
458: $this->collection->push(new Column($attributes));
459:
460: return $this;
461: }
462:
463: 464: 465: 466: 467: 468:
469: public function addAction(array $attributes = [])
470: {
471: $attributes = array_merge([
472: 'defaultContent' => '',
473: 'data' => 'action',
474: 'name' => 'action',
475: 'title' => 'Action',
476: 'render' => null,
477: 'orderable' => false,
478: 'searchable' => false,
479: 'exportable' => false,
480: 'printable' => true,
481: 'footer' => '',
482: ], $attributes);
483: $this->collection->push(new Column($attributes));
484:
485: return $this;
486: }
487:
488: 489: 490: 491: 492: 493:
494: public function addIndex(array $attributes = [])
495: {
496: $indexColumn = Config::get('datatables.index_column', 'DT_Row_Index');
497: $attributes = array_merge([
498: 'defaultContent' => '',
499: 'data' => $indexColumn,
500: 'name' => $indexColumn,
501: 'title' => '',
502: 'render' => null,
503: 'orderable' => false,
504: 'searchable' => false,
505: 'exportable' => false,
506: 'printable' => true,
507: 'footer' => '',
508: ], $attributes);
509: $this->collection->push(new Column($attributes));
510:
511: return $this;
512: }
513:
514: 515: 516: 517: 518: 519:
520: public function ajax($attributes)
521: {
522: $this->ajax = $attributes;
523:
524: return $this;
525: }
526:
527: 528: 529: 530: 531: 532: 533:
534: public function table(array $attributes = [], $drawFooter = false)
535: {
536: $this->tableAttributes = array_merge($this->tableAttributes, $attributes);
537:
538: $th = $this->compileTableHeaders();
539: $htmlAttr = $this->html->attributes($this->tableAttributes);
540:
541: $tableHtml = '<table ' . $htmlAttr . '>';
542: $tableHtml .= '<thead><tr>' . implode('', $th) . '</tr></thead>';
543: if ($drawFooter) {
544: $tf = $this->compileTableFooter();
545: $tableHtml .= '<tfoot><tr>' . implode('', $tf) . '</tr></tfoot>';
546: }
547: $tableHtml .= '</table>';
548:
549: return $tableHtml;
550: }
551:
552: 553: 554: 555: 556:
557: private function compileTableHeaders()
558: {
559: $th = [];
560: foreach ($this->collection->toArray() as $row) {
561: $thAttr = $this->html->attributes(
562: array_only($row, ['class', 'id', 'width', 'style', 'data-class', 'data-hide'])
563: );
564: $th[] = '<th ' . $thAttr . '>' . $row['title'] . '</th>';
565: }
566:
567: return $th;
568: }
569:
570: 571: 572: 573: 574:
575: private function compileTableFooter()
576: {
577: $footer = [];
578: foreach ($this->collection->toArray() as $row) {
579: $footer[] = '<th>' . $row['footer'] . '</th>';
580: }
581:
582: return $footer;
583: }
584:
585: 586: 587: 588: 589: 590:
591: public function parameters(array $attributes = [])
592: {
593: $this->attributes = array_merge($this->attributes, $attributes);
594:
595: return $this;
596: }
597:
598: 599: 600: 601: 602: 603:
604: public function setTemplate($template)
605: {
606: $this->template = $template;
607:
608: return $this;
609: }
610:
611: 612: 613: 614: 615:
616: public function getColumns()
617: {
618: return $this->collection;
619: }
620: }
621: