1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace SleepingOwl\Admin\Display; |
4
|
|
|
|
5
|
|
|
use Illuminate\Routing\Router; |
6
|
|
|
use Illuminate\Support\Collection; |
7
|
|
|
use Illuminate\Support\Facades\Request; |
8
|
|
|
use Illuminate\Database\Eloquent\Builder; |
9
|
|
|
use SleepingOwl\Admin\Display\Column\Control; |
10
|
|
|
use SleepingOwl\Admin\Contracts\WithRoutesInterface; |
11
|
|
|
use SleepingOwl\Admin\Contracts\Display\ColumnInterface; |
12
|
|
|
use SleepingOwl\Admin\Contracts\Display\ColumnMetaInterface; |
13
|
|
|
|
14
|
|
|
class DisplayDatatablesAsync extends DisplayDatatables implements WithRoutesInterface |
15
|
|
|
{ |
16
|
|
|
/** |
17
|
|
|
* Register display routes. |
18
|
|
|
* |
19
|
|
|
* @param Router $router |
20
|
|
|
* |
21
|
|
|
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException |
22
|
|
|
*/ |
23
|
|
|
public static function registerRoutes(Router $router) |
24
|
|
|
{ |
25
|
|
|
$routeName = 'admin.display.async'; |
26
|
|
|
if (! $router->has($routeName)) { |
27
|
|
|
$router->get('{adminModel}/async/{adminDisplayName?}', [ |
28
|
|
|
'as' => $routeName, |
29
|
|
|
'uses' => 'SleepingOwl\Admin\Http\Controllers\DisplayController@async', |
|
|
|
|
30
|
|
|
]); |
31
|
|
|
} |
32
|
|
|
|
33
|
|
|
$routeName = 'admin.display.async.inlineEdit'; |
34
|
|
|
if (! $router->has($routeName)) { |
35
|
|
|
$router->post('{adminModel}/async/{adminDisplayName?}', [ |
36
|
|
|
'as' => $routeName, |
37
|
|
|
'uses' => 'SleepingOwl\Admin\Http\Controllers\AdminController@inlineEdit', |
|
|
|
|
38
|
|
|
]); |
39
|
|
|
} |
40
|
|
|
} |
41
|
|
|
|
42
|
|
|
protected $payload; |
43
|
|
|
/** |
44
|
|
|
* @var string |
45
|
|
|
*/ |
46
|
|
|
protected $name; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @param string|null $name |
50
|
|
|
*/ |
51
|
|
|
protected $distinct; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* @var |
55
|
|
|
*/ |
56
|
|
|
protected $displaySearch = false; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* @var |
60
|
|
|
*/ |
61
|
|
|
protected $displayLength = false; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* DisplayDatatablesAsync constructor. |
65
|
|
|
* |
66
|
|
|
* @param string|null $name |
67
|
|
|
* @param string|null $distinct |
68
|
|
|
*/ |
69
|
|
|
public function __construct($name = null, $distinct = null) |
70
|
|
|
{ |
71
|
|
|
parent::__construct(); |
72
|
|
|
|
73
|
|
|
$this->setName($name); |
74
|
|
|
$this->setDistinct($distinct); |
75
|
|
|
|
76
|
|
|
$this->getColumns()->setView('display.extensions.columns_async'); |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* Initialize display. |
81
|
|
|
*/ |
82
|
|
|
public function initialize() |
83
|
|
|
{ |
84
|
|
|
parent::initialize(); |
85
|
|
|
|
86
|
|
|
$attributes = Request::all(); |
87
|
|
|
array_unshift($attributes, $this->getName()); |
88
|
|
|
array_unshift($attributes, $this->getModelConfiguration()->getAlias()); |
89
|
|
|
|
90
|
|
|
$this->setHtmlAttribute('style', 'width:100%'); |
91
|
|
|
$this->setHtmlAttribute('data-url', route('admin.display.async', $attributes)); |
92
|
|
|
$this->setHtmlAttribute('data-payload', json_encode($this->payload)); |
93
|
|
|
|
94
|
|
|
if ($this->getDisplaySearch()) { |
95
|
|
|
$this->setHtmlAttribute('data-display-search', 1); |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
if ($this->getDisplayLength()) { |
99
|
|
|
$this->setHtmlAttribute('data-display-dtlength', 1); |
100
|
|
|
} |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* @param bool $length |
105
|
|
|
* @return $this |
106
|
|
|
*/ |
107
|
|
|
public function setDisplayLength($length) |
108
|
|
|
{ |
109
|
|
|
$this->displayLength = $length; |
110
|
|
|
|
111
|
|
|
return $this; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* @return bool |
116
|
|
|
*/ |
117
|
|
|
public function getDisplayLength() |
118
|
|
|
{ |
119
|
|
|
return $this->displayLength; |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* @param $search |
124
|
|
|
* @return $this |
125
|
|
|
*/ |
126
|
|
|
public function setDisplaySearch($search) |
127
|
|
|
{ |
128
|
|
|
$this->displaySearch = $search; |
129
|
|
|
|
130
|
|
|
return $this; |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
/** |
134
|
|
|
* @return bool |
135
|
|
|
*/ |
136
|
|
|
public function getDisplaySearch() |
137
|
|
|
{ |
138
|
|
|
return $this->displaySearch; |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
/** |
142
|
|
|
* @return string |
143
|
|
|
*/ |
144
|
|
|
public function getName() |
145
|
|
|
{ |
146
|
|
|
return $this->name; |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
/** |
150
|
|
|
* @param string $name |
151
|
|
|
* |
152
|
|
|
* @return $this |
153
|
|
|
*/ |
154
|
|
|
public function setName($name) |
155
|
|
|
{ |
156
|
|
|
$this->name = $name; |
157
|
|
|
|
158
|
|
|
return $this; |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* @return mixed |
163
|
|
|
*/ |
164
|
|
|
public function getDistinct() |
165
|
|
|
{ |
166
|
|
|
return $this->distinct; |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
/** |
170
|
|
|
* @param mixed $distinct |
171
|
|
|
* |
172
|
|
|
* @return $this |
173
|
|
|
*/ |
174
|
|
|
public function setDistinct($distinct) |
175
|
|
|
{ |
176
|
|
|
$this->distinct = $distinct; |
177
|
|
|
|
178
|
|
|
return $this; |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* Render async request. |
183
|
|
|
* |
184
|
|
|
* @param \Illuminate\Http\Request $request |
185
|
|
|
* |
186
|
|
|
* @return array |
187
|
|
|
*/ |
188
|
|
|
public function renderAsync(\Illuminate\Http\Request $request) |
189
|
|
|
{ |
190
|
|
|
$query = $this->getRepository()->getQuery(); |
191
|
|
|
$totalCount = $query->count(); |
192
|
|
|
$filteredCount = 0; |
193
|
|
|
|
194
|
|
|
if (! is_null($this->distinct)) { |
195
|
|
|
$filteredCount = $query->distinct()->count($this->getDistinct()); |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
$this->modifyQuery($query); |
199
|
|
|
$this->applySearch($query, $request); |
200
|
|
|
|
201
|
|
|
if (is_null($this->distinct)) { |
202
|
|
|
$countQuery = clone $query; |
203
|
|
|
$countQuery->getQuery()->orders = null; |
204
|
|
|
$filteredCount = $countQuery->count(); |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
$this->applyOffset($query, $request); |
|
|
|
|
208
|
|
|
$collection = $query->get(); |
209
|
|
|
|
210
|
|
|
return $this->prepareDatatablesStructure($request, $collection, $totalCount, $filteredCount); |
|
|
|
|
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* Apply offset and limit to the query. |
215
|
|
|
* |
216
|
|
|
* @param \Illuminate\Database\Query\Builder $query |
217
|
|
|
* @param \Illuminate\Http\Request $request |
218
|
|
|
*/ |
219
|
|
|
public function applyOffset($query, \Illuminate\Http\Request $request) |
220
|
|
|
{ |
221
|
|
|
$offset = $request->input('start', 0); |
222
|
|
|
$limit = $request->input('length', 10); |
223
|
|
|
|
224
|
|
|
if ($limit == -1) { |
225
|
|
|
return; |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
$query->offset((int) $offset)->limit((int) $limit); |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
/** |
232
|
|
|
* Apply search to the query. |
233
|
|
|
* |
234
|
|
|
* @param Builder $query |
235
|
|
|
* @param \Illuminate\Http\Request $request |
236
|
|
|
*/ |
237
|
|
|
public function applySearch(Builder $query, \Illuminate\Http\Request $request) |
238
|
|
|
{ |
239
|
|
|
$search = $request->input('search.value'); |
240
|
|
|
if (empty($search)) { |
241
|
|
|
return; |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
$query->where(function (Builder $query) use ($search) { |
245
|
|
|
$columns = $this->getColumns()->all(); |
246
|
|
|
|
247
|
|
|
$_model = $query->getModel(); |
248
|
|
|
|
249
|
|
|
foreach ($columns as $column) { |
250
|
|
|
if ($column->isSearchable()) { |
251
|
|
|
if ($column instanceof ColumnInterface) { |
252
|
|
|
if (($metaInstance = $column->getMetaData()) instanceof ColumnMetaInterface) { |
|
|
|
|
253
|
|
|
if (method_exists($metaInstance, 'onSearch')) { |
254
|
|
|
$metaInstance->onSearch($column, $query, $search); |
255
|
|
|
continue; |
256
|
|
|
} |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
if (is_callable($callback = $column->getSearchCallback())) { |
|
|
|
|
260
|
|
|
$callback($column, $query, $search); |
261
|
|
|
continue; |
262
|
|
|
} |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
if ($_model->getAttribute($column->getName())) { |
|
|
|
|
266
|
|
|
continue; |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
$query->orWhere($column->getName(), 'like', '%'.$search.'%'); |
270
|
|
|
} |
271
|
|
|
} |
272
|
|
|
}); |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
/** |
276
|
|
|
* Convert collection to the datatables structure. |
277
|
|
|
* |
278
|
|
|
* @param \Illuminate\Http\Request $request |
279
|
|
|
* @param array|Collection $collection |
280
|
|
|
* @param int $totalCount |
281
|
|
|
* @param int $filteredCount |
282
|
|
|
* |
283
|
|
|
* @return array |
284
|
|
|
*/ |
285
|
|
|
protected function prepareDatatablesStructure( |
286
|
|
|
\Illuminate\Http\Request $request, |
287
|
|
|
Collection $collection, |
288
|
|
|
$totalCount, |
289
|
|
|
$filteredCount |
290
|
|
|
) { |
291
|
|
|
$columns = $this->getColumns(); |
292
|
|
|
|
293
|
|
|
$result = []; |
294
|
|
|
$result['draw'] = $request->input('draw', 0); |
295
|
|
|
$result['recordsTotal'] = $totalCount; |
296
|
|
|
$result['recordsFiltered'] = $filteredCount; |
297
|
|
|
$result['data'] = []; |
298
|
|
|
|
299
|
|
|
foreach ($collection as $instance) { |
300
|
|
|
$_row = []; |
301
|
|
|
|
302
|
|
|
foreach ($columns->all() as $column) { |
303
|
|
|
$column->setModel($instance); |
304
|
|
|
|
305
|
|
|
if ($column instanceof Control) { |
306
|
|
|
$column->initialize(); |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
$_row[] = (string) $column; |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
$result['data'][] = $_row; |
313
|
|
|
} |
314
|
|
|
|
315
|
|
|
return $result; |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
/** |
319
|
|
|
* @return void |
320
|
|
|
*/ |
321
|
|
|
public function getCollection() |
322
|
|
|
{ |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
/** |
326
|
|
|
* @param $payload |
327
|
|
|
*/ |
328
|
|
|
public function setPayload($payload) |
329
|
|
|
{ |
330
|
|
|
$this->payload = $payload; |
331
|
|
|
} |
332
|
|
|
|
333
|
|
|
/** |
334
|
|
|
* @return mixed |
335
|
|
|
*/ |
336
|
|
|
public function getPayload() |
337
|
|
|
{ |
338
|
|
|
return $this->payload; |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
/** |
342
|
|
|
* @return array |
343
|
|
|
* @throws \Exception |
344
|
|
|
*/ |
345
|
|
|
public function toArray() |
346
|
|
|
{ |
347
|
|
|
$params = parent::toArray(); |
348
|
|
|
$params['payload'] = $this->payload; |
349
|
|
|
|
350
|
|
|
return $params; |
351
|
|
|
} |
352
|
|
|
} |
353
|
|
|
|
If there is a route defined but the controller class cannot be found there are two options: 1. the controller class needs to be implemented or 2. the route is outdated and can be removed.
If ?FooController? was found and ?BarController? is missing for the following example, either the controller should be implemented or the route should be removed: