Skip to content

Commit 3238ae6

Browse files
authored
Merge pull request #3251 from yajra/column-control
feat: server-side column control
2 parents 7b5f1f5 + 5c10fd3 commit 3238ae6

File tree

3 files changed

+126
-13
lines changed

3 files changed

+126
-13
lines changed

src/DataTableAbstract.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,10 +730,16 @@ protected function filterRecords(): void
730730
}
731731

732732
$this->columnSearch();
733+
$this->columnControlSearch();
733734
$this->searchPanesSearch();
734735
$this->filteredCount();
735736
}
736737

738+
public function columnControlSearch(): void
739+
{
740+
// Not implemented in the abstract class.
741+
}
742+
737743
/**
738744
* Perform global search.
739745
*/

src/QueryDataTable.php

Lines changed: 110 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Illuminate\Database\Eloquent\Model;
99
use Illuminate\Database\Query\Expression;
1010
use Illuminate\Http\JsonResponse;
11+
use Illuminate\Support\Carbon;
1112
use Illuminate\Support\Collection;
1213
use Illuminate\Support\Facades\DB;
1314
use Illuminate\Support\Str;
@@ -258,6 +259,7 @@ protected function filterRecords(): void
258259
}
259260

260261
$this->columnSearch();
262+
$this->columnControlSearch();
261263
$this->searchPanesSearch();
262264

263265
// If no modification between the original query and the filtered one has been made
@@ -281,23 +283,122 @@ public function columnSearch(): void
281283
$columns = $this->request->columns();
282284

283285
foreach ($columns as $index => $column) {
284-
$column = $this->getColumnName($index);
286+
$columnName = $this->getColumnName($index);
285287

286-
if (is_null($column)) {
288+
if (is_null($columnName)) {
287289
continue;
288290
}
289291

290-
if (! $this->request->isColumnSearchable($index) || $this->isBlacklisted($column) && ! $this->hasFilterColumn($column)) {
292+
if (! $this->request->isColumnSearchable($index) || $this->isBlacklisted($columnName) && ! $this->hasFilterColumn($columnName)) {
291293
continue;
292294
}
293295

294-
if ($this->hasFilterColumn($column)) {
296+
if ($this->hasFilterColumn($columnName)) {
295297
$keyword = $this->getColumnSearchKeyword($index, true);
296-
$this->applyFilterColumn($this->getBaseQueryBuilder(), $column, $keyword);
298+
$this->applyFilterColumn($this->getBaseQueryBuilder(), $columnName, $keyword);
297299
} else {
298-
$column = $this->resolveRelationColumn($column);
300+
$columnName = $this->resolveRelationColumn($columnName);
299301
$keyword = $this->getColumnSearchKeyword($index);
300-
$this->compileColumnSearch($index, $column, $keyword);
302+
$this->compileColumnSearch($index, $columnName, $keyword);
303+
}
304+
}
305+
}
306+
307+
public function columnControlSearch(): void
308+
{
309+
$columns = $this->request->columns();
310+
311+
foreach ($columns as $index => $column) {
312+
$columnName = $this->getColumnName($index);
313+
314+
if (is_null($columnName) || ! ($column['searchable'] ?? false)) {
315+
continue;
316+
}
317+
318+
if ($this->isBlacklisted($columnName) && ! $this->hasFilterColumn($columnName)) {
319+
continue;
320+
}
321+
322+
$columnControl = $this->request->columnControl($index);
323+
$list = $columnControl['list'] ?? [];
324+
$search = $columnControl['search'] ?? [];
325+
$value = $search['value'] ?? '';
326+
$logic = $search['logic'] ?? 'equal';
327+
$mask = $search['mask'] ?? ''; // for date type
328+
$type = $search['type'] ?? 'text'; // text, num, date
329+
330+
if ($value != '' || str_contains(strtolower($logic), 'empty') || $list) {
331+
$operator = match ($logic) {
332+
'contains', 'notContains', 'starts', 'ends' => 'LIKE',
333+
'greater' => '>',
334+
'less' => '<',
335+
'greaterOrEqual' => '>=',
336+
'lessOrEqual' => '<=',
337+
'empty', 'notEmpty' => null,
338+
default => '=',
339+
};
340+
341+
switch ($logic) {
342+
case 'contains':
343+
case 'notContains':
344+
$value = '%'.$value.'%';
345+
break;
346+
case 'starts':
347+
$value = $value.'%';
348+
break;
349+
case 'ends':
350+
$value = '%'.$value;
351+
break;
352+
}
353+
354+
if ($this->hasFilterColumn($columnName)) {
355+
$value = $list ? implode(', ', $list) : $value;
356+
$this->applyFilterColumn($this->getBaseQueryBuilder(), $columnName, $value);
357+
358+
continue;
359+
}
360+
361+
if ($list) {
362+
if (str_contains($logic, 'not')) {
363+
$this->query->whereNotIn($columnName, $list);
364+
} else {
365+
$this->query->whereIn($columnName, $list);
366+
}
367+
368+
continue;
369+
}
370+
371+
if (str_contains(strtolower($logic), 'empty')) {
372+
$this->query->whereNull($columnName, not: $logic === 'notEmpty');
373+
374+
continue;
375+
}
376+
377+
if ($type === 'date') {
378+
try {
379+
$value = $mask ? Carbon::createFromFormat($mask, $value) : Carbon::parse($value);
380+
} catch (\Exception) {
381+
// can't parse date
382+
}
383+
384+
if ($logic === 'notEqual') {
385+
$this->query->where(function ($q) use ($columnName, $value) {
386+
$q->whereDate($columnName, '!=', $value)->orWhereNull($columnName);
387+
});
388+
} else {
389+
$this->query->whereDate($columnName, $operator, $value);
390+
}
391+
392+
continue;
393+
}
394+
395+
if (str_contains($logic, 'not')) {
396+
$this->query->whereNot($columnName, $operator, $value);
397+
398+
continue;
399+
}
400+
401+
$this->query->where($columnName, $operator, $value);
301402
}
302403
}
303404
}
@@ -536,11 +637,11 @@ protected function getSelectedColumns($query): array
536637
];
537638

538639
foreach ($q->columns ?? [] as $select) {
539-
$sql = trim($select instanceof Expression ? $select->getValue($this->getConnection()->getQueryGrammar()) : $select);
640+
$sql = trim($select instanceof Expression ? $select->getValue($this->getConnection()->getQueryGrammar()) : (string) $select);
540641
// Remove expressions
541642
$sql = preg_replace('/\s*\w*\((?:[^()]*|(?R))*\)/', '_', $sql);
542643
// Remove multiple spaces
543-
$sql = preg_replace('/\s+/', ' ', $sql);
644+
$sql = preg_replace('/\s+/', ' ', (string) $sql);
544645
// Remove wrappers
545646
$sql = str_replace(['`', '"', '[', ']'], '', $sql);
546647
// Loop on select columns
@@ -694,10 +795,6 @@ public function addColumn($name, $content, $order = false): static
694795

695796
/**
696797
* Perform search using search pane values.
697-
*
698-
*
699-
* @throws \Psr\Container\ContainerExceptionInterface
700-
* @throws \Psr\Container\NotFoundExceptionInterface
701798
*/
702799
protected function searchPanesSearch(): void
703800
{

src/Utilities/Request.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,16 @@ public function columnKeyword(int $index): string
151151
return $this->prepareKeyword($keyword);
152152
}
153153

154+
public function columnControl(int $index): array
155+
{
156+
return request()->array("columns.$index.columnControl");
157+
}
158+
159+
public function columnControlSearch(int $index): array
160+
{
161+
return request()->array("columns.$index.columnControl.search");
162+
}
163+
154164
/**
155165
* Prepare keyword string value.
156166
*/

0 commit comments

Comments
 (0)