1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace SleepingOwl\Admin\Display; |
4
|
|
|
|
5
|
|
|
use Exception; |
6
|
|
|
use Illuminate\Support\Collection; |
7
|
|
|
use Illuminate\Support\Facades\Request; |
8
|
|
|
use Illuminate\Database\Eloquent\Builder; |
9
|
|
|
use SleepingOwl\Admin\Traits\CardControl; |
10
|
|
|
use SleepingOwl\Admin\Traits\PanelControl; |
11
|
|
|
use Illuminate\Pagination\LengthAwarePaginator; |
12
|
|
|
use SleepingOwl\Admin\Display\Extension\Columns; |
13
|
|
|
use SleepingOwl\Admin\Display\Extension\ColumnsTotal; |
14
|
|
|
use SleepingOwl\Admin\Display\Extension\ColumnFilters; |
15
|
|
|
use SleepingOwl\Admin\Contracts\Display\ColumnInterface; |
16
|
|
|
use SleepingOwl\Admin\Contracts\Display\ColumnMetaInterface; |
17
|
|
|
use SleepingOwl\Admin\Contracts\Display\Extension\ColumnFilterInterface; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* Class DisplayTable. |
21
|
|
|
* |
22
|
|
|
* @method Columns getColumns() |
23
|
|
|
* @method $this setColumns(ColumnInterface|ColumnInterface[] $column) |
24
|
|
|
* |
25
|
|
|
* @method ColumnFilters getColumnFilters() |
26
|
|
|
* @method $this setColumnFilters(ColumnFilterInterface|ColumnFilterInterface[] $filters = null, ...$filters) |
27
|
|
|
*/ |
28
|
|
|
class DisplayTable extends Display |
29
|
|
|
{ |
30
|
|
|
use CardControl, PanelControl; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* @var string |
34
|
|
|
*/ |
35
|
|
|
protected $view = 'display.table'; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* @var array |
39
|
|
|
*/ |
40
|
|
|
protected $parameters = []; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @var int|null |
44
|
|
|
*/ |
45
|
|
|
protected $paginate = 25; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* @var string |
49
|
|
|
*/ |
50
|
|
|
protected $pageName = 'page'; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* @var bool|null |
54
|
|
|
*/ |
55
|
|
|
protected $creatable = null; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* @var Collection |
59
|
|
|
*/ |
60
|
|
|
protected $collection; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* @var string|null |
64
|
|
|
*/ |
65
|
|
|
protected $newEntryButtonText; |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Display constructor. |
69
|
|
|
*/ |
70
|
|
|
public function __construct() |
71
|
|
|
{ |
72
|
|
|
parent::__construct(); |
73
|
|
|
|
74
|
|
|
$this->extend('columns', new Columns()); |
75
|
|
|
$this->extend('column_filters', new ColumnFilters()); |
76
|
|
|
$this->extend('columns_total', new ColumnsTotal()); |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* @throws \Exception |
81
|
|
|
*/ |
82
|
|
|
public function initialize() |
83
|
|
|
{ |
84
|
|
|
parent::initialize(); |
85
|
|
|
|
86
|
|
|
if ($this->getModelConfiguration()->isRestorableModel()) { |
87
|
|
|
$this->setApply(function (Builder $q) { |
88
|
|
|
return $q->withTrashed(); |
89
|
|
|
}); |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
$this->setHtmlAttribute('class', 'table'); |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* @return array|\Illuminate\Contracts\Translation\Translator|null|string |
97
|
|
|
*/ |
98
|
|
|
public function getNewEntryButtonText() |
99
|
|
|
{ |
100
|
|
|
if (is_null($this->newEntryButtonText)) { |
101
|
|
|
$this->newEntryButtonText = trans('sleeping_owl::lang.button.new-entry'); |
|
|
|
|
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
return $this->newEntryButtonText; |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* @param string $newEntryButtonText |
109
|
|
|
* |
110
|
|
|
* @return $this |
111
|
|
|
*/ |
112
|
|
|
public function setNewEntryButtonText($newEntryButtonText) |
113
|
|
|
{ |
114
|
|
|
$this->newEntryButtonText = $newEntryButtonText; |
115
|
|
|
|
116
|
|
|
return $this; |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* @return array |
121
|
|
|
*/ |
122
|
|
|
public function getParameters() |
123
|
|
|
{ |
124
|
|
|
return $this->parameters; |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* @param array $parameters |
129
|
|
|
* |
130
|
|
|
* @return $this |
131
|
|
|
*/ |
132
|
|
|
public function setParameters($parameters) |
133
|
|
|
{ |
134
|
|
|
$this->parameters = $parameters; |
135
|
|
|
|
136
|
|
|
return $this; |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
/** |
140
|
|
|
* @param string $key |
141
|
|
|
* @param mixed $value |
142
|
|
|
* |
143
|
|
|
* @return $this |
144
|
|
|
*/ |
145
|
|
|
public function setParameter($key, $value) |
146
|
|
|
{ |
147
|
|
|
$this->parameters[$key] = $value; |
148
|
|
|
|
149
|
|
|
return $this; |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
/** |
153
|
|
|
* @param int $perPage |
154
|
|
|
* @param string $pageName |
155
|
|
|
* |
156
|
|
|
* @return $this |
157
|
|
|
*/ |
158
|
|
|
public function paginate($perPage = 25, $pageName = 'page') |
159
|
|
|
{ |
160
|
|
|
$this->paginate = (int) $perPage; |
161
|
|
|
$this->pageName = $pageName; |
162
|
|
|
|
163
|
|
|
return $this; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* @return $this |
168
|
|
|
*/ |
169
|
|
|
public function disablePagination() |
170
|
|
|
{ |
171
|
|
|
$this->paginate = 0; |
172
|
|
|
|
173
|
|
|
return $this; |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
/** |
177
|
|
|
* @return bool |
178
|
|
|
*/ |
179
|
|
|
public function usePagination() |
180
|
|
|
{ |
181
|
|
|
return $this->paginate > 0; |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* @return bool|null |
186
|
|
|
*/ |
187
|
|
|
public function getCreatable() |
188
|
|
|
{ |
189
|
|
|
return $this->creatable; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* @param bool|null $creatable |
194
|
|
|
* |
195
|
|
|
* @return $this |
196
|
|
|
*/ |
197
|
|
|
public function setCreatable($creatable) |
198
|
|
|
{ |
199
|
|
|
$this->creatable = $creatable; |
200
|
|
|
|
201
|
|
|
return $this; |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* @return array |
206
|
|
|
* @throws \Exception |
207
|
|
|
*/ |
208
|
|
|
public function toArray() |
209
|
|
|
{ |
210
|
|
|
$model = $this->getModelConfiguration(); |
211
|
|
|
|
212
|
|
|
$params = parent::toArray(); |
213
|
|
|
|
214
|
|
|
$params['creatable'] = $this->getCreatable() !== null ? $this->getCreatable() : $model->isCreatable(); |
215
|
|
|
$params['createUrl'] = $model->getCreateUrl($this->getParameters() + Request::all()); |
216
|
|
|
$params['collection'] = $this->getCollection(); |
217
|
|
|
|
218
|
|
|
$params['extensions'] = $this->getExtensions()->renderable()->sortByOrder(); |
219
|
|
|
$params['newEntryButtonText'] = $this->getNewEntryButtonText(); |
220
|
|
|
$params['panel_class'] = $this->getPanelClass(); |
221
|
|
|
|
222
|
|
|
return $params; |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
/** |
226
|
|
|
* $collection Collection|LengthAwarePaginator|Builder. |
227
|
|
|
*/ |
228
|
|
|
public function setCollection($collection) |
229
|
|
|
{ |
230
|
|
|
$this->collection = $collection; |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
/** |
234
|
|
|
* @return Collection|LengthAwarePaginator|Builder |
235
|
|
|
* @throws \Exception |
236
|
|
|
*/ |
237
|
|
|
public function getCollection() |
238
|
|
|
{ |
239
|
|
|
if (! $this->isInitialized()) { |
240
|
|
|
throw new Exception('Display is not initialized'); |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
if (! is_null($this->collection)) { |
244
|
|
|
return $this->collection; |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
$query = $this->getRepository()->getQuery(); |
248
|
|
|
|
249
|
|
|
$this->modifyQuery($query); |
250
|
|
|
|
251
|
|
|
return $this->collection = $this->usePagination() |
|
|
|
|
252
|
|
|
? $query->paginate($this->paginate, ['*'], $this->pageName)->appends(request()->except($this->pageName)) |
253
|
|
|
: $query->get(); |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
/** |
257
|
|
|
* @param \Illuminate\Database\Eloquent\Builder|Builder $query |
258
|
|
|
*/ |
259
|
|
|
protected function modifyQuery(Builder $query) |
260
|
|
|
{ |
261
|
|
|
$this->extensions->modifyQuery($query); |
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
/** |
265
|
|
|
* Apply offset and limit to the query. |
266
|
|
|
* |
267
|
|
|
* @param \Illuminate\Database\Query\Builder $query |
268
|
|
|
* @param \Illuminate\Http\Request $request |
269
|
|
|
*/ |
270
|
|
|
public function applyOffset($query, \Illuminate\Http\Request $request) |
271
|
|
|
{ |
272
|
|
|
$offset = $request->input('start', 0); |
273
|
|
|
$limit = $request->input('length', 10); |
274
|
|
|
|
275
|
|
|
if ($limit == -1) { |
276
|
|
|
return; |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
$query->offset((int) $offset)->limit((int) $limit); |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
/** |
283
|
|
|
* Apply search to the query. |
284
|
|
|
* |
285
|
|
|
* @param Builder $query |
286
|
|
|
* @param \Illuminate\Http\Request $request |
287
|
|
|
*/ |
288
|
|
|
public function applySearch(Builder $query, \Illuminate\Http\Request $request) |
289
|
|
|
{ |
290
|
|
|
$search = $request->input('search.value'); |
291
|
|
|
if (empty($search)) { |
292
|
|
|
return; |
293
|
|
|
} |
294
|
|
|
|
295
|
|
|
$query->where(function (Builder $query) use ($search) { |
296
|
|
|
$columns = $this->getColumns()->all(); |
297
|
|
|
|
298
|
|
|
$_model = $query->getModel(); |
299
|
|
|
|
300
|
|
|
foreach ($columns as $column) { |
301
|
|
|
if ($column->isSearchable()) { |
302
|
|
|
if ($column instanceof ColumnInterface) { |
303
|
|
|
if (($metaInstance = $column->getMetaData()) instanceof ColumnMetaInterface) { |
|
|
|
|
304
|
|
|
if (method_exists($metaInstance, 'onSearch')) { |
305
|
|
|
$metaInstance->onSearch($column, $query, $search); |
306
|
|
|
continue; |
307
|
|
|
} |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
if (is_callable($callback = $column->getSearchCallback())) { |
|
|
|
|
311
|
|
|
$callback($column, $query, $search); |
312
|
|
|
continue; |
313
|
|
|
} |
314
|
|
|
} |
315
|
|
|
|
316
|
|
|
if ($_model->getAttribute($column->getName())) { |
|
|
|
|
317
|
|
|
continue; |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
$query->orWhere($column->getName(), 'like', '%'.$search.'%'); |
321
|
|
|
} |
322
|
|
|
} |
323
|
|
|
}); |
324
|
|
|
} |
325
|
|
|
} |
326
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.