1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Yajra\Datatables\Engines; |
4
|
|
|
|
5
|
|
|
use Illuminate\Contracts\Logging\Log; |
6
|
|
|
use Illuminate\Contracts\Support\Arrayable; |
7
|
|
|
use Illuminate\Contracts\Support\Jsonable; |
8
|
|
|
use Illuminate\Http\JsonResponse; |
9
|
|
|
use Illuminate\Support\Str; |
10
|
|
|
use Illuminate\Support\Traits\Macroable; |
11
|
|
|
use Yajra\Datatables\Contracts\DataTableEngine; |
12
|
|
|
use Yajra\Datatables\Exception; |
13
|
|
|
use Yajra\Datatables\Helper; |
14
|
|
|
use Yajra\Datatables\Processors\DataProcessor; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* Class BaseEngine. |
18
|
|
|
* |
19
|
|
|
* @package Yajra\Datatables\Engines |
20
|
|
|
* @method setTransformer($transformer) |
21
|
|
|
* @method setSerializer($transformer) |
22
|
|
|
* @property mixed transformer |
23
|
|
|
* @property mixed serializer |
24
|
|
|
* @see https://github.com/yajra/laravel-datatables-fractal for transformer related methods. |
25
|
|
|
* @author Arjay Angeles <[email protected]> |
26
|
|
|
*/ |
27
|
|
|
abstract class BaseEngine implements DataTableEngine, Arrayable, Jsonable |
28
|
|
|
{ |
29
|
|
|
use Macroable; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Datatables Request object. |
33
|
|
|
* |
34
|
|
|
* @var \Yajra\Datatables\Request |
35
|
|
|
*/ |
36
|
|
|
public $request; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* @var \Illuminate\Contracts\Logging\Log |
40
|
|
|
*/ |
41
|
|
|
protected $logger; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Array of result columns/fields. |
45
|
|
|
* |
46
|
|
|
* @var array |
47
|
|
|
*/ |
48
|
|
|
protected $columns = []; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* DT columns definitions container (add/edit/remove/filter/order/escape). |
52
|
|
|
* |
53
|
|
|
* @var array |
54
|
|
|
*/ |
55
|
|
|
protected $columnDef = [ |
56
|
|
|
'index' => false, |
57
|
|
|
'append' => [], |
58
|
|
|
'edit' => [], |
59
|
|
|
'filter' => [], |
60
|
|
|
'order' => [], |
61
|
|
|
]; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* Extra/Added columns. |
65
|
|
|
* |
66
|
|
|
* @var array |
67
|
|
|
*/ |
68
|
|
|
protected $extraColumns = []; |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* Total records. |
72
|
|
|
* |
73
|
|
|
* @var int |
74
|
|
|
*/ |
75
|
|
|
protected $totalRecords = 0; |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* Total filtered records. |
79
|
|
|
* |
80
|
|
|
* @var int |
81
|
|
|
*/ |
82
|
|
|
protected $filteredRecords = 0; |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* Auto-filter flag. |
86
|
|
|
* |
87
|
|
|
* @var bool |
88
|
|
|
*/ |
89
|
|
|
protected $autoFilter = true; |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* Callback to override global search. |
93
|
|
|
* |
94
|
|
|
* @var callable |
95
|
|
|
*/ |
96
|
|
|
protected $filterCallback; |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* DT row templates container. |
100
|
|
|
* |
101
|
|
|
* @var array |
102
|
|
|
*/ |
103
|
|
|
protected $templates = [ |
104
|
|
|
'DT_RowId' => '', |
105
|
|
|
'DT_RowClass' => '', |
106
|
|
|
'DT_RowData' => [], |
107
|
|
|
'DT_RowAttr' => [], |
108
|
|
|
]; |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* [internal] Track if any filter was applied for at least one column. |
112
|
|
|
* |
113
|
|
|
* @var boolean |
114
|
|
|
*/ |
115
|
|
|
protected $isFilterApplied = false; |
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* Custom ordering callback. |
119
|
|
|
* |
120
|
|
|
* @var callable |
121
|
|
|
*/ |
122
|
|
|
protected $orderCallback; |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* Skip paginate as needed. |
126
|
|
|
* |
127
|
|
|
* @var bool |
128
|
|
|
*/ |
129
|
|
|
protected $skipPaging = false; |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* Array of data to append on json response. |
133
|
|
|
* |
134
|
|
|
* @var array |
135
|
|
|
*/ |
136
|
|
|
protected $appends = []; |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* @var \Yajra\Datatables\Config |
140
|
|
|
*/ |
141
|
|
|
protected $config; |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* Add column in collection. |
145
|
|
|
* |
146
|
|
|
* @param string $name |
147
|
|
|
* @param string|callable $content |
148
|
|
|
* @param bool|int $order |
149
|
|
|
* @return $this |
150
|
|
|
*/ |
151
|
|
|
public function addColumn($name, $content, $order = false) |
152
|
|
|
{ |
153
|
|
|
$this->extraColumns[] = $name; |
154
|
|
|
|
155
|
|
|
$this->columnDef['append'][] = ['name' => $name, 'content' => $content, 'order' => $order]; |
156
|
|
|
|
157
|
|
|
return $this; |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* Add DT row index column on response. |
162
|
|
|
* |
163
|
|
|
* @return $this |
164
|
|
|
*/ |
165
|
|
|
public function addIndexColumn() |
166
|
|
|
{ |
167
|
|
|
$this->columnDef['index'] = true; |
168
|
|
|
|
169
|
|
|
return $this; |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
/** |
173
|
|
|
* Edit column's content. |
174
|
|
|
* |
175
|
|
|
* @param string $name |
176
|
|
|
* @param string|callable $content |
177
|
|
|
* @return $this |
178
|
|
|
*/ |
179
|
|
|
public function editColumn($name, $content) |
180
|
|
|
{ |
181
|
|
|
$this->columnDef['edit'][] = ['name' => $name, 'content' => $content]; |
182
|
|
|
|
183
|
|
|
return $this; |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* Remove column from collection. |
188
|
|
|
* |
189
|
|
|
* @return $this |
190
|
|
|
*/ |
191
|
|
|
public function removeColumn() |
192
|
|
|
{ |
193
|
|
|
$names = func_get_args(); |
194
|
|
|
$this->columnDef['excess'] = array_merge($this->columnDef['excess'], $names); |
195
|
|
|
|
196
|
|
|
return $this; |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
/** |
200
|
|
|
* Declare columns to escape values. |
201
|
|
|
* |
202
|
|
|
* @param string|array $columns |
203
|
|
|
* @return $this |
204
|
|
|
*/ |
205
|
|
|
public function escapeColumns($columns = '*') |
206
|
|
|
{ |
207
|
|
|
$this->columnDef['escape'] = $columns; |
208
|
|
|
|
209
|
|
|
return $this; |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
/** |
213
|
|
|
* Set columns that should not be escaped. |
214
|
|
|
* |
215
|
|
|
* @param array $columns |
216
|
|
|
* @return $this |
217
|
|
|
*/ |
218
|
|
|
public function rawColumns(array $columns) |
219
|
|
|
{ |
220
|
|
|
$this->columnDef['raw'] = $columns; |
221
|
|
|
|
222
|
|
|
return $this; |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
/** |
226
|
|
|
* Sets DT_RowClass template. |
227
|
|
|
* result: <tr class="output_from_your_template">. |
228
|
|
|
* |
229
|
|
|
* @param string|callable $content |
230
|
|
|
* @return $this |
231
|
|
|
*/ |
232
|
|
|
public function setRowClass($content) |
233
|
|
|
{ |
234
|
|
|
$this->templates['DT_RowClass'] = $content; |
235
|
|
|
|
236
|
|
|
return $this; |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
/** |
240
|
|
|
* Sets DT_RowId template. |
241
|
|
|
* result: <tr id="output_from_your_template">. |
242
|
|
|
* |
243
|
|
|
* @param string|callable $content |
244
|
|
|
* @return $this |
245
|
|
|
*/ |
246
|
|
|
public function setRowId($content) |
247
|
|
|
{ |
248
|
|
|
$this->templates['DT_RowId'] = $content; |
249
|
|
|
|
250
|
|
|
return $this; |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
/** |
254
|
|
|
* Set DT_RowData templates. |
255
|
|
|
* |
256
|
|
|
* @param array $data |
257
|
|
|
* @return $this |
258
|
|
|
*/ |
259
|
|
|
public function setRowData(array $data) |
260
|
|
|
{ |
261
|
|
|
$this->templates['DT_RowData'] = $data; |
262
|
|
|
|
263
|
|
|
return $this; |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
/** |
267
|
|
|
* Add DT_RowData template. |
268
|
|
|
* |
269
|
|
|
* @param string $key |
270
|
|
|
* @param string|callable $value |
271
|
|
|
* @return $this |
272
|
|
|
*/ |
273
|
|
|
public function addRowData($key, $value) |
274
|
|
|
{ |
275
|
|
|
$this->templates['DT_RowData'][$key] = $value; |
276
|
|
|
|
277
|
|
|
return $this; |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
/** |
281
|
|
|
* Set DT_RowAttr templates. |
282
|
|
|
* result: <tr attr1="attr1" attr2="attr2">. |
283
|
|
|
* |
284
|
|
|
* @param array $data |
285
|
|
|
* @return $this |
286
|
|
|
*/ |
287
|
|
|
public function setRowAttr(array $data) |
288
|
|
|
{ |
289
|
|
|
$this->templates['DT_RowAttr'] = $data; |
290
|
|
|
|
291
|
|
|
return $this; |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
/** |
295
|
|
|
* Add DT_RowAttr template. |
296
|
|
|
* |
297
|
|
|
* @param string $key |
298
|
|
|
* @param string|callable $value |
299
|
|
|
* @return $this |
300
|
|
|
*/ |
301
|
|
|
public function addRowAttr($key, $value) |
302
|
|
|
{ |
303
|
|
|
$this->templates['DT_RowAttr'][$key] = $value; |
304
|
|
|
|
305
|
|
|
return $this; |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
/** |
309
|
|
|
* Append data on json response. |
310
|
|
|
* |
311
|
|
|
* @param mixed $key |
312
|
|
|
* @param mixed $value |
313
|
|
|
* @return $this |
314
|
|
|
*/ |
315
|
|
|
public function with($key, $value = '') |
316
|
|
|
{ |
317
|
|
|
if (is_array($key)) { |
318
|
|
|
$this->appends = $key; |
319
|
|
|
} elseif (is_callable($value)) { |
320
|
|
|
$this->appends[$key] = value($value); |
321
|
|
|
} else { |
322
|
|
|
$this->appends[$key] = value($value); |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
return $this; |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
/** |
329
|
|
|
* Override default ordering method with a closure callback. |
330
|
|
|
* |
331
|
|
|
* @param callable $closure |
332
|
|
|
* @return $this |
333
|
|
|
*/ |
334
|
|
|
public function order(callable $closure) |
335
|
|
|
{ |
336
|
|
|
$this->orderCallback = $closure; |
337
|
|
|
|
338
|
|
|
return $this; |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
/** |
342
|
|
|
* Update list of columns that is not allowed for search/sort. |
343
|
|
|
* |
344
|
|
|
* @param array $blacklist |
345
|
|
|
* @return $this |
346
|
|
|
*/ |
347
|
|
|
public function blacklist(array $blacklist) |
348
|
|
|
{ |
349
|
|
|
$this->columnDef['blacklist'] = $blacklist; |
350
|
|
|
|
351
|
|
|
return $this; |
352
|
|
|
} |
353
|
|
|
|
354
|
|
|
/** |
355
|
|
|
* Update list of columns that is allowed for search/sort. |
356
|
|
|
* |
357
|
|
|
* @param string|array $whitelist |
358
|
|
|
* @return $this |
359
|
|
|
*/ |
360
|
|
|
public function whitelist($whitelist = '*') |
361
|
|
|
{ |
362
|
|
|
$this->columnDef['whitelist'] = $whitelist; |
363
|
|
|
|
364
|
|
|
return $this; |
365
|
|
|
} |
366
|
|
|
|
367
|
|
|
/** |
368
|
|
|
* Set smart search config at runtime. |
369
|
|
|
* |
370
|
|
|
* @param bool $bool |
371
|
|
|
* @return $this |
372
|
|
|
*/ |
373
|
|
|
public function smart($bool = true) |
374
|
|
|
{ |
375
|
|
|
$this->config->set(['datatables.search.smart' => $bool]); |
376
|
|
|
|
377
|
|
|
return $this; |
378
|
|
|
} |
379
|
|
|
|
380
|
|
|
/** |
381
|
|
|
* Set total records manually. |
382
|
|
|
* |
383
|
|
|
* @param int $total |
384
|
|
|
* @return $this |
385
|
|
|
*/ |
386
|
|
|
public function setTotalRecords($total) |
387
|
|
|
{ |
388
|
|
|
$this->totalRecords = $total; |
389
|
|
|
|
390
|
|
|
return $this; |
391
|
|
|
} |
392
|
|
|
|
393
|
|
|
/** |
394
|
|
|
* Skip pagination as needed. |
395
|
|
|
* |
396
|
|
|
* @return $this |
397
|
|
|
*/ |
398
|
|
|
public function skipPaging() |
399
|
|
|
{ |
400
|
|
|
$this->skipPaging = true; |
401
|
|
|
|
402
|
|
|
return $this; |
403
|
|
|
} |
404
|
|
|
|
405
|
|
|
/** |
406
|
|
|
* Push a new column name to blacklist. |
407
|
|
|
* |
408
|
|
|
* @param string $column |
409
|
|
|
* @return $this |
410
|
|
|
*/ |
411
|
|
|
public function pushToBlacklist($column) |
412
|
|
|
{ |
413
|
|
|
if (!$this->isBlacklisted($column)) { |
414
|
|
|
$this->columnDef['blacklist'][] = $column; |
415
|
|
|
} |
416
|
|
|
|
417
|
|
|
return $this; |
418
|
|
|
} |
419
|
|
|
|
420
|
|
|
/** |
421
|
|
|
* Check if column is blacklisted. |
422
|
|
|
* |
423
|
|
|
* @param string $column |
424
|
|
|
* @return bool |
425
|
|
|
*/ |
426
|
|
|
protected function isBlacklisted($column) |
427
|
|
|
{ |
428
|
|
|
$colDef = $this->getColumnsDefinition(); |
429
|
|
|
|
430
|
|
|
if (in_array($column, $colDef['blacklist'])) { |
431
|
|
|
return true; |
432
|
|
|
} |
433
|
|
|
|
434
|
|
|
if ($colDef['whitelist'] === '*' || in_array($column, $colDef['whitelist'])) { |
435
|
|
|
return false; |
436
|
|
|
} |
437
|
|
|
|
438
|
|
|
return true; |
439
|
|
|
} |
440
|
|
|
|
441
|
|
|
/** |
442
|
|
|
* Get columns definition. |
443
|
|
|
* |
444
|
|
|
* @return array |
445
|
|
|
*/ |
446
|
|
|
protected function getColumnsDefinition() |
447
|
|
|
{ |
448
|
|
|
$config = $this->config->get('datatables.columns'); |
449
|
|
|
$allowed = ['excess', 'escape', 'raw', 'blacklist', 'whitelist']; |
450
|
|
|
|
451
|
|
|
return array_merge(array_only($config, $allowed), $this->columnDef); |
452
|
|
|
} |
453
|
|
|
|
454
|
|
|
/** |
455
|
|
|
* Perform sorting of columns. |
456
|
|
|
*/ |
457
|
|
|
public function ordering() |
458
|
|
|
{ |
459
|
|
|
if ($this->orderCallback) { |
460
|
|
|
return call_user_func($this->orderCallback, $this->resolveCallbackParameter()); |
461
|
|
|
} |
462
|
|
|
|
463
|
|
|
return $this->defaultOrdering(); |
464
|
|
|
} |
465
|
|
|
|
466
|
|
|
/** |
467
|
|
|
* Resolve callback parameter instance. |
468
|
|
|
* |
469
|
|
|
* @return mixed |
470
|
|
|
*/ |
471
|
|
|
abstract protected function resolveCallbackParameter(); |
472
|
|
|
|
473
|
|
|
/** |
474
|
|
|
* Perform default query orderBy clause. |
475
|
|
|
*/ |
476
|
|
|
abstract protected function defaultOrdering(); |
477
|
|
|
|
478
|
|
|
/** |
479
|
|
|
* Set auto filter off and run your own filter. |
480
|
|
|
* Overrides global search. |
481
|
|
|
* |
482
|
|
|
* @param callable $callback |
483
|
|
|
* @param bool $globalSearch |
484
|
|
|
* @return $this |
485
|
|
|
*/ |
486
|
|
|
public function filter(callable $callback, $globalSearch = false) |
487
|
|
|
{ |
488
|
|
|
$this->autoFilter = $globalSearch; |
489
|
|
|
$this->isFilterApplied = true; |
490
|
|
|
$this->filterCallback = $callback; |
491
|
|
|
|
492
|
|
|
return $this; |
493
|
|
|
} |
494
|
|
|
|
495
|
|
|
/** |
496
|
|
|
* Perform necessary filters. |
497
|
|
|
* |
498
|
|
|
* @return void |
499
|
|
|
*/ |
500
|
|
|
protected function filterRecords() |
501
|
|
|
{ |
502
|
|
|
if ($this->autoFilter && $this->request->isSearchable()) { |
503
|
|
|
$this->filtering(); |
504
|
|
|
} |
505
|
|
|
|
506
|
|
|
if (is_callable($this->filterCallback)) { |
507
|
|
|
call_user_func($this->filterCallback, $this->resolveCallbackParameter()); |
508
|
|
|
} |
509
|
|
|
|
510
|
|
|
$this->columnSearch(); |
511
|
|
|
$this->filteredRecords = $this->isFilterApplied ? $this->count() : $this->totalRecords; |
512
|
|
|
} |
513
|
|
|
|
514
|
|
|
/** |
515
|
|
|
* Perform global search. |
516
|
|
|
* |
517
|
|
|
* @return void |
518
|
|
|
*/ |
519
|
|
|
public function filtering() |
520
|
|
|
{ |
521
|
|
|
$keyword = $this->request->keyword(); |
522
|
|
|
|
523
|
|
|
if ($this->config->isMultiTerm()) { |
524
|
|
|
$this->smartGlobalSearch($keyword); |
525
|
|
|
|
526
|
|
|
return; |
527
|
|
|
} |
528
|
|
|
|
529
|
|
|
$this->globalSearch($keyword); |
530
|
|
|
} |
531
|
|
|
|
532
|
|
|
/** |
533
|
|
|
* Perform multi-term search by splitting keyword into |
534
|
|
|
* individual words and searches for each of them. |
535
|
|
|
* |
536
|
|
|
* @param string $keyword |
537
|
|
|
*/ |
538
|
|
|
protected function smartGlobalSearch($keyword) |
539
|
|
|
{ |
540
|
|
|
$keywords = array_filter(explode(' ', $keyword)); |
541
|
|
|
|
542
|
|
|
foreach ($keywords as $keyword) { |
543
|
|
|
$this->globalSearch($keyword); |
544
|
|
|
} |
545
|
|
|
} |
546
|
|
|
|
547
|
|
|
/** |
548
|
|
|
* Perform global search for the given keyword. |
549
|
|
|
* |
550
|
|
|
* @param string $keyword |
551
|
|
|
*/ |
552
|
|
|
abstract protected function globalSearch($keyword); |
553
|
|
|
|
554
|
|
|
/** |
555
|
|
|
* Apply pagination. |
556
|
|
|
* |
557
|
|
|
* @return void |
558
|
|
|
*/ |
559
|
|
|
protected function paginate() |
560
|
|
|
{ |
561
|
|
|
if ($this->request->isPaginationable() && !$this->skipPaging) { |
562
|
|
|
$this->paging(); |
563
|
|
|
} |
564
|
|
|
} |
565
|
|
|
|
566
|
|
|
/** |
567
|
|
|
* Transform output. |
568
|
|
|
* |
569
|
|
|
* @param mixed $results |
570
|
|
|
* @param mixed $processed |
571
|
|
|
* @return array |
572
|
|
|
*/ |
573
|
|
|
protected function transform($results, $processed) |
574
|
|
|
{ |
575
|
|
|
if (isset($this->transformer) && class_exists('Yajra\\Datatables\\Transformers\\FractalTransformer')) { |
576
|
|
|
return resolve('datatables.transformer')->transform($results, $this->transformer, $this->serializer ?? null); |
577
|
|
|
} |
578
|
|
|
|
579
|
|
|
return Helper::transform($processed); |
580
|
|
|
} |
581
|
|
|
|
582
|
|
|
/** |
583
|
|
|
* Get processed data. |
584
|
|
|
* |
585
|
|
|
* @param mixed $results |
586
|
|
|
* @param bool $object |
587
|
|
|
* @return array |
588
|
|
|
*/ |
589
|
|
|
protected function processResults($results, $object = false) |
590
|
|
|
{ |
591
|
|
|
$processor = new DataProcessor( |
592
|
|
|
$results, |
593
|
|
|
$this->getColumnsDefinition(), |
594
|
|
|
$this->templates, |
595
|
|
|
$this->request->input('start') |
596
|
|
|
); |
597
|
|
|
|
598
|
|
|
return $processor->process($object); |
599
|
|
|
} |
600
|
|
|
|
601
|
|
|
/** |
602
|
|
|
* Render json response. |
603
|
|
|
* |
604
|
|
|
* @param array $data |
605
|
|
|
* @return \Illuminate\Http\JsonResponse |
606
|
|
|
*/ |
607
|
|
|
protected function render(array $data) |
608
|
|
|
{ |
609
|
|
|
$output = array_merge([ |
610
|
|
|
'draw' => (int) $this->request->input('draw'), |
611
|
|
|
'recordsTotal' => $this->totalRecords, |
612
|
|
|
'recordsFiltered' => $this->filteredRecords, |
613
|
|
|
'data' => $data, |
614
|
|
|
], $this->appends); |
615
|
|
|
|
616
|
|
|
if ($this->config->isDebugging()) { |
617
|
|
|
$output = $this->showDebugger($output); |
618
|
|
|
} |
619
|
|
|
|
620
|
|
|
return new JsonResponse( |
621
|
|
|
$output, |
622
|
|
|
200, |
623
|
|
|
$this->config->get('datatables.json.header', []), |
624
|
|
|
$this->config->get('datatables.json.options', 0) |
625
|
|
|
); |
626
|
|
|
} |
627
|
|
|
|
628
|
|
|
/** |
629
|
|
|
* Append debug parameters on output. |
630
|
|
|
* |
631
|
|
|
* @param array $output |
632
|
|
|
* @return array |
633
|
|
|
*/ |
634
|
|
|
abstract protected function showDebugger(array $output); |
635
|
|
|
|
636
|
|
|
/** |
637
|
|
|
* Return an error json response. |
638
|
|
|
* |
639
|
|
|
* @param \Exception $exception |
640
|
|
|
* @return \Illuminate\Http\JsonResponse |
641
|
|
|
* @throws \Yajra\Datatables\Exception |
642
|
|
|
*/ |
643
|
|
|
protected function errorResponse(\Exception $exception) |
644
|
|
|
{ |
645
|
|
|
$error = $this->config->get('datatables.error'); |
646
|
|
|
if ($error === 'throw') { |
647
|
|
|
throw new Exception($exception->getMessage(), $code = 0, $exception); |
648
|
|
|
} |
649
|
|
|
|
650
|
|
|
$this->getLogger()->error($exception); |
651
|
|
|
|
652
|
|
|
return new JsonResponse([ |
653
|
|
|
'draw' => (int) $this->request->input('draw'), |
654
|
|
|
'recordsTotal' => (int) $this->totalRecords, |
655
|
|
|
'recordsFiltered' => 0, |
656
|
|
|
'data' => [], |
657
|
|
|
'error' => $error ? __($error) : "Exception Message:\n\n" . $exception->getMessage(), |
658
|
|
|
]); |
659
|
|
|
} |
660
|
|
|
|
661
|
|
|
/** |
662
|
|
|
* Get monolog/logger instance. |
663
|
|
|
* |
664
|
|
|
* @return \Illuminate\Contracts\Logging\Log |
665
|
|
|
*/ |
666
|
|
|
public function getLogger() |
667
|
|
|
{ |
668
|
|
|
$this->logger = $this->logger ?: resolve(Log::class); |
669
|
|
|
|
670
|
|
|
return $this->logger; |
671
|
|
|
} |
672
|
|
|
|
673
|
|
|
/** |
674
|
|
|
* Set monolog/logger instance. |
675
|
|
|
* |
676
|
|
|
* @param \Illuminate\Contracts\Logging\Log $logger |
677
|
|
|
* @return $this |
678
|
|
|
*/ |
679
|
|
|
public function setLogger(Log $logger) |
680
|
|
|
{ |
681
|
|
|
$this->logger = $logger; |
682
|
|
|
|
683
|
|
|
return $this; |
684
|
|
|
} |
685
|
|
|
|
686
|
|
|
/** |
687
|
|
|
* Setup search keyword. |
688
|
|
|
* |
689
|
|
|
* @param string $value |
690
|
|
|
* @return string |
691
|
|
|
*/ |
692
|
|
|
protected function setupKeyword($value) |
693
|
|
|
{ |
694
|
|
|
if ($this->config->isSmartSearch()) { |
695
|
|
|
$keyword = '%' . $value . '%'; |
696
|
|
|
if ($this->config->isWildcard()) { |
697
|
|
|
$keyword = Helper::wildcardLikeString($value); |
698
|
|
|
} |
699
|
|
|
// remove escaping slash added on js script request |
700
|
|
|
$keyword = str_replace('\\', '%', $keyword); |
701
|
|
|
|
702
|
|
|
return $keyword; |
703
|
|
|
} |
704
|
|
|
|
705
|
|
|
return $value; |
706
|
|
|
} |
707
|
|
|
|
708
|
|
|
/** |
709
|
|
|
* Get column name to be use for filtering and sorting. |
710
|
|
|
* |
711
|
|
|
* @param integer $index |
712
|
|
|
* @param bool $wantsAlias |
713
|
|
|
* @return string |
714
|
|
|
*/ |
715
|
|
|
protected function getColumnName($index, $wantsAlias = false) |
716
|
|
|
{ |
717
|
|
|
$column = $this->request->columnName($index); |
718
|
|
|
|
719
|
|
|
// DataTables is using make(false) |
720
|
|
|
if (is_numeric($column)) { |
721
|
|
|
$column = $this->getColumnNameByIndex($index); |
722
|
|
|
} |
723
|
|
|
|
724
|
|
|
if (Str::contains(Str::upper($column), ' AS ')) { |
725
|
|
|
$column = Helper::extractColumnName($column, $wantsAlias); |
726
|
|
|
} |
727
|
|
|
|
728
|
|
|
return $column; |
729
|
|
|
} |
730
|
|
|
|
731
|
|
|
/** |
732
|
|
|
* Get column name by order column index. |
733
|
|
|
* |
734
|
|
|
* @param int $index |
735
|
|
|
* @return string |
736
|
|
|
*/ |
737
|
|
|
protected function getColumnNameByIndex($index) |
738
|
|
|
{ |
739
|
|
|
$name = (isset($this->columns[$index]) && $this->columns[$index] != '*') ? $this->columns[$index] : $this->getPrimaryKeyName(); |
740
|
|
|
|
741
|
|
|
return in_array($name, $this->extraColumns, true) ? $this->getPrimaryKeyName() : $name; |
742
|
|
|
} |
743
|
|
|
|
744
|
|
|
/** |
745
|
|
|
* If column name could not be resolved then use primary key. |
746
|
|
|
* |
747
|
|
|
* @return string |
748
|
|
|
*/ |
749
|
|
|
protected function getPrimaryKeyName() |
750
|
|
|
{ |
751
|
|
|
return 'id'; |
752
|
|
|
} |
753
|
|
|
|
754
|
|
|
/** |
755
|
|
|
* Convert instance to array. |
756
|
|
|
* |
757
|
|
|
* @return \Illuminate\Http\JsonResponse |
758
|
|
|
*/ |
759
|
|
|
public function toArray() |
760
|
|
|
{ |
761
|
|
|
return $this->make(); |
|
|
|
|
762
|
|
|
} |
763
|
|
|
|
764
|
|
|
/** |
765
|
|
|
* Convert the object to its JSON representation. |
766
|
|
|
* |
767
|
|
|
* @param int $options |
768
|
|
|
* @return string |
769
|
|
|
*/ |
770
|
|
|
public function toJson($options = 0) |
771
|
|
|
{ |
772
|
|
|
if ($options) { |
773
|
|
|
$this->config->set('datatables.json.options', $options); |
774
|
|
|
} |
775
|
|
|
|
776
|
|
|
return $this->make(); |
777
|
|
|
} |
778
|
|
|
} |
779
|
|
|
|
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.