From 942e6d0d434aec4e8de41072501eb781c75c42ca Mon Sep 17 00:00:00 2001 From: Renardo Date: Tue, 29 Jun 2021 14:29:00 +0200 Subject: [PATCH 001/113] Add over-ridden aggregates support in filters If we specify a custom agreggate (max, avg, min, ...) use this aggregate for filtering data Also fix filtering when null values are returned with aggregates --- src/Http/Livewire/LivewireDatatable.php | 5 +++-- src/LivewireDatatablesServiceProvider.php | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index a5e2cfce..6d1c3e99 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -471,7 +471,7 @@ public function doNumberFilterStart($index, $start) public function doNumberFilterEnd($index, $end) { - $this->activeNumberFilters[$index]['end'] = $end ? (int) $end : null; + $this->activeNumberFilters[$index]['end'] = ($end !== '') ? (int) $end : null; $this->clearEmptyNumberFilter($index); $this->page = 1; } @@ -683,9 +683,10 @@ public function columnIsAggregateRelation($column) public function columnAggregateType($column) { + $custom_aggregate = Str::after(explode('.', $column['name'])[1], ':'); return $column['type'] === 'string' ? 'group_concat' - : 'count'; + : $custom_aggregate ?? 'count'; } public function buildDatabaseQuery($export = false) diff --git a/src/LivewireDatatablesServiceProvider.php b/src/LivewireDatatablesServiceProvider.php index 5ad9809d..a588cddd 100644 --- a/src/LivewireDatatablesServiceProvider.php +++ b/src/LivewireDatatablesServiceProvider.php @@ -141,7 +141,7 @@ public function loadRelationMacros() Relation::macro('getRelationExistenceAggregatesQuery', function (EloquentBuilder $query, EloquentBuilder $parentQuery, $aggregate, $column) { $expression = $aggregate === 'group_concat' ? new Expression($aggregate."(distinct {$column} separator ', ')") - : new Expression($aggregate."({$column})"); + : new Expression("COALESCE(".$aggregate."({$column}),0)"); return $this->getRelationExistenceQuery( $query, From 3fa7aca1c57b87f62ea90168c81be94ee325be91 Mon Sep 17 00:00:00 2001 From: Tom Shaw Date: Sun, 10 Oct 2021 10:13:06 -0500 Subject: [PATCH 002/113] Base Mass Actions starting point. --- .../livewire/datatables/datatable.blade.php | 22 +++++++ src/Action.php | 57 ++++++++++++++++++ src/Http/Livewire/LivewireDatatable.php | 59 +++++++++++++++++++ 3 files changed, 138 insertions(+) create mode 100644 src/Action.php diff --git a/resources/views/livewire/datatables/datatable.blade.php b/resources/views/livewire/datatables/datatable.blade.php index bd854f69..c6bec622 100644 --- a/resources/views/livewire/datatables/datatable.blade.php +++ b/resources/views/livewire/datatables/datatable.blade.php @@ -39,6 +39,28 @@ @endif + @if(count($this->selectActions)) +
+ + +
+ @endif + @if($exportable)
@endforelse + + @if ($this->hasSummaryRow()) +
+ @foreach($this->columns as $column) + @if ($column['summary']) +
+ {{ $this->summarize($column['name']) }} +
+ @else +
+ @endif + @endforeach +
+ @endif
diff --git a/src/Column.php b/src/Column.php index 8578111e..34a5d2a1 100644 --- a/src/Column.php +++ b/src/Column.php @@ -33,6 +33,12 @@ class Column public $width; public $exportCallback; + /** + * @var bool should the sum of all summarizable cells in this column be + * displayed as a summary at the bottom of the table? + */ + public $summary = false; + /** * @var string (optional) you can group your columns to let the user toggle the visibility of a group at once. */ @@ -113,6 +119,20 @@ public function label($label) return $this; } + public function enableSummary() + { + $this->summary = true; + + return $this; + } + + public function disableSummary() + { + $this->summary = false; + + return $this; + } + public function sortBy($column) { $this->sort = $column; diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 6d76e41d..e09f5442 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -202,6 +202,7 @@ public function getViewColumns() 'hidden', 'label', 'group', + 'summary', 'content', 'align', 'type', @@ -617,6 +618,34 @@ public function getSortString() } } + /** + * @return bool has the user defined at least one column to display a summary row? + */ + public function hasSummaryRow() + { + foreach ($this->columns as $column) { + if ($column['summary']) { + return true; + } + } + + return false; + } + + /** + * Attempt so summarize each data cell of the given column. + * In case we have a string or any other value that is not summarizable, + * we return a empty string. + */ + public function summarize($column) + { + try { + return $this->results->sum($column); + } catch (\TypeError $e) { + return ''; + } + } + public function updatingPerPage() { $this->refreshLivewireDatatable(); From 59afa0c1b047268e2d666ea125975dfa9855ad86 Mon Sep 17 00:00:00 2001 From: Tom Shaw Date: Thu, 14 Oct 2021 10:44:08 -0500 Subject: [PATCH 008/113] Addition mass action exports with settings. --- .../livewire/datatables/datatable.blade.php | 3 +- src/Action.php | 42 ++++++- src/Exports/DatatableExport.php | 119 +++++++++++++++++- src/Http/Livewire/LivewireDatatable.php | 71 ++++++++--- 4 files changed, 212 insertions(+), 23 deletions(-) diff --git a/resources/views/livewire/datatables/datatable.blade.php b/resources/views/livewire/datatables/datatable.blade.php index c6bec622..89fe8359 100644 --- a/resources/views/livewire/datatables/datatable.blade.php +++ b/resources/views/livewire/datatables/datatable.blade.php @@ -40,7 +40,7 @@ @endif @if(count($this->selectActions)) -
+
+
@endif diff --git a/src/Action.php b/src/Action.php index c148ef40..779ffca9 100644 --- a/src/Action.php +++ b/src/Action.php @@ -7,8 +7,11 @@ class Action public $value; public $label; public $group; - public $exportable = false; - public $exportableOptions = []; + public $isExport = false; + public $name; + public $type; + public $styles = []; + public $widths = []; public $callable; public function __call($method, $args) @@ -40,7 +43,7 @@ public function group($group) return $this; } - public static function groups($group, $actions) + public static function groupBy($group, $actions) { if ($actions instanceof \Closure) { return collect($actions())->each(function ($item) use ($group) { @@ -49,10 +52,37 @@ public static function groups($group, $actions) } } - public function exportable($exportable = true, $exportableOptions = []) + public function isExport($isExport = true) { - $this->exportable = $exportable; - $this->exportableOptions = $exportableOptions; + $this->isExport = $isExport; + + return $this; + } + + public function name($name) + { + $this->name = $name; + + return $this; + } + + public function type($type) + { + $this->type = $type; + + return $this; + } + + public function styles($styles) + { + $this->styles = $styles; + + return $this; + } + + public function widths($widths) + { + $this->widths = $widths; return $this; } diff --git a/src/Exports/DatatableExport.php b/src/Exports/DatatableExport.php index a31179e5..8249d1af 100644 --- a/src/Exports/DatatableExport.php +++ b/src/Exports/DatatableExport.php @@ -2,15 +2,26 @@ namespace Mediconesystems\LivewireDatatables\Exports; +use Maatwebsite\Excel\Excel as ExcelExport; use Maatwebsite\Excel\Concerns\Exportable; use Maatwebsite\Excel\Concerns\FromCollection; use Maatwebsite\Excel\Concerns\WithHeadings; +use Maatwebsite\Excel\Concerns\ShouldAutoSize; +use Maatwebsite\Excel\Concerns\WithColumnWidths; +use Maatwebsite\Excel\Concerns\WithStyles; +use Maatwebsite\Excel\Facades\Excel; -class DatatableExport implements FromCollection, WithHeadings +use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; + +class DatatableExport implements FromCollection, WithHeadings, ShouldAutoSize, WithColumnWidths, WithStyles { use Exportable; public $collection; + public $fileName = 'DatatableExport'; + public $fileType = 'xlsx'; + public $styles = []; + public $columnWidths = []; public function __construct($collection) { @@ -26,4 +37,110 @@ public function headings(): array { return array_keys((array) $this->collection->first()); } + + public function setFileName($fileName) + { + $this->fileName = $fileName; + + return $this; + } + + public function getFileName(): string + { + return $this->fileName; + } + + public function setFileType($fileType) + { + $this->fileType = strtolower($fileType); + + return $this; + } + + public function getFileType(): string + { + return $this->fileType; + } + + public function setColumnWidths($columnWidths) + { + $this->columnWidths = $columnWidths; + + return $this; + } + + public function getColumnWidths(): array + { + return $this->columnWidths; + } + + public function columnWidths(): array + { + return $this->getColumnWidths(); + } + + public function setStyles($styles) + { + $this->styles = $styles; + + return $this; + } + + public function getStyles(): array + { + return $this->styles; + } + + public function styles(Worksheet $sheet) + { + return $this->getStyles(); + } + + public function getFileWriter($fileType) + { + switch ($fileType) { + case "xlsx": + $writer = ExcelExport::XLSX; + break; + case "csv": + $writer = ExcelExport::CSV; + break; + case "tsv": + $writer = ExcelExport::TSV; + break; + case "ods": + $writer = ExcelExport::ODS; + break; + case "xls": + $writer = ExcelExport::XLS; + break; + case "html": + $writer = ExcelExport::HTML; + break; + case "mpdf": + $writer = ExcelExport::MPDF; + break; + case "dompdf": + $writer = ExcelExport::DOMPDF; + break; + case "tcpdf": + $writer = ExcelExport::TCPDF; + break; + default: + $writer = ExcelExport::XLSX; + } + + return $writer; + } + + public function download() + { + $fileName = $this->getFileName(); + $fileType = $this->getFileType(); + + $writer = $this->getFileWriter($fileType); + $headers = ($fileType === 'csv') ? ['Content-Type' => 'text/csv'] : []; + + return Excel::download($this, $fileName . '.' . $fileType, $writer, $headers); + } } diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index fa938a28..713125fc 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -1487,7 +1487,18 @@ public function export() { $this->forgetComputed(); - $results = $this->mapCallbacks( + $export = new DatatableExport($this->getExportResultsSet()); + + $export->setFileName('DatatableExport'); + + $export->setFileType('xlsx'); + + return $export->download(); + } + + public function getExportResultsSet() + { + return $this->mapCallbacks( $this->getQuery()->when(count($this->selected), function ($query) { return $query->havingRaw('checkbox_attribute IN (' . implode(',', $this->selected) . ')'); })->get(), @@ -1499,8 +1510,6 @@ public function export() return [$value['label'] ?? $value['name'] => $item->{$value['name']}]; })->all(); }); - - return Excel::download(new DatatableExport($results), 'DatatableExport.xlsx'); } public function getQuery($export = false) @@ -1573,11 +1582,10 @@ public function cellClasses($row, $column) // Override this method with your own method for adding classes to a cell return config('livewire-datatables.default_classes.cell', 'text-sm text-gray-900'); } - public function getMassActions() { return collect($this->massActions)->map(function ($action) { - return collect($action)->except(['exportable', 'exportableOptions', 'callable'])->toArray(); + return collect($action)->except(['callable'])->toArray(); })->toArray(); } @@ -1601,28 +1609,61 @@ public function getSelectActionsProperty() }, true); } - public function updatedSelectedAction($value) + public function handleMassActions() { - if (!count($this->selected)) { - $this->selectedAction = null; + if (!$this->selectedAction) { return; } - $action = collect($this->massActions)->filter(function ($item) use ($value) { - return $item->value === $value; + $option = $this->selectedAction; + + $action = collect($this->massActions)->filter(function ($item) use ($option) { + return $item->value === $option; })->shift(); $collection = collect($action); - // @todo -- Export with type and user defined params if any. - if ($collection->has('exportable')) { - if ($collection->has('exportableOptions')) { - $exportOptions = $collection->get('exportableOptions'); + $isExport = $collection->get('isExport'); + + if ($isExport) { + + $fileName = $collection->get('name'); + $fileType = $collection->get('type'); + + $styles = $collection->get('styles'); + $widths = $collection->get('widths'); + + $datatableExport = new DatatableExport($this->getExportResultsSet()); + + if ($fileName) { + $datatableExport->setFileName($fileName); + } + + if ($fileType) { + $datatableExport->setFileType($fileType); + } + + if ($styles) { + $datatableExport->setStyles($styles); + } + + if ($widths) { + $datatableExport->setColumnWidths($widths); } + + return $datatableExport->download(); + } + + if (!count($this->selected)) { + $this->selectedAction = null; + return; } if ($collection->has('callable') && is_callable($action->callable)) { - $action->callable($value, $this->selected); + $action->callable($option, $this->selected); } + + $this->selectedAction = null; + $this->selected = []; } } From 4953ad721e45046fc7a91c5bdb42d238338bf7e3 Mon Sep 17 00:00:00 2001 From: Tom Shaw Date: Thu, 14 Oct 2021 17:15:20 -0500 Subject: [PATCH 009/113] Adjust spacing. --- src/Exports/DatatableExport.php | 18 +++++++++--------- src/Http/Livewire/LivewireDatatable.php | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Exports/DatatableExport.php b/src/Exports/DatatableExport.php index 8249d1af..fcf4f97c 100644 --- a/src/Exports/DatatableExport.php +++ b/src/Exports/DatatableExport.php @@ -99,31 +99,31 @@ public function styles(Worksheet $sheet) public function getFileWriter($fileType) { switch ($fileType) { - case "xlsx": + case 'xlsx': $writer = ExcelExport::XLSX; break; - case "csv": + case 'csv': $writer = ExcelExport::CSV; break; - case "tsv": + case 'tsv': $writer = ExcelExport::TSV; break; - case "ods": + case 'ods': $writer = ExcelExport::ODS; break; - case "xls": + case 'xls': $writer = ExcelExport::XLS; break; - case "html": + case 'html': $writer = ExcelExport::HTML; break; - case "mpdf": + case 'mpdf': $writer = ExcelExport::MPDF; break; - case "dompdf": + case 'dompdf': $writer = ExcelExport::DOMPDF; break; - case "tcpdf": + case 'tcpdf': $writer = ExcelExport::TCPDF; break; default: diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 713125fc..bf11e543 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -16,7 +16,6 @@ use Illuminate\View\View; use Livewire\Component; use Livewire\WithPagination; -use Maatwebsite\Excel\Facades\Excel; use Mediconesystems\LivewireDatatables\Column; use Mediconesystems\LivewireDatatables\ColumnSet; use Mediconesystems\LivewireDatatables\Exports\DatatableExport; @@ -1582,6 +1581,7 @@ public function cellClasses($row, $column) // Override this method with your own method for adding classes to a cell return config('livewire-datatables.default_classes.cell', 'text-sm text-gray-900'); } + public function getMassActions() { return collect($this->massActions)->map(function ($action) { @@ -1611,7 +1611,7 @@ public function getSelectActionsProperty() public function handleMassActions() { - if (!$this->selectedAction) { + if (! $this->selectedAction) { return; } @@ -1650,11 +1650,11 @@ public function handleMassActions() if ($widths) { $datatableExport->setColumnWidths($widths); } - + return $datatableExport->download(); } - if (!count($this->selected)) { + if (! count($this->selected)) { $this->selectedAction = null; return; } From abdf2dc03727f688e4f1b5198911ac5bfd1051a2 Mon Sep 17 00:00:00 2001 From: Tom Shaw Date: Thu, 14 Oct 2021 17:20:35 -0500 Subject: [PATCH 010/113] Adjust spacing. --- src/Exports/DatatableExport.php | 4 ++-- src/Http/Livewire/LivewireDatatable.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Exports/DatatableExport.php b/src/Exports/DatatableExport.php index fcf4f97c..5565ad73 100644 --- a/src/Exports/DatatableExport.php +++ b/src/Exports/DatatableExport.php @@ -2,13 +2,13 @@ namespace Mediconesystems\LivewireDatatables\Exports; -use Maatwebsite\Excel\Excel as ExcelExport; use Maatwebsite\Excel\Concerns\Exportable; use Maatwebsite\Excel\Concerns\FromCollection; -use Maatwebsite\Excel\Concerns\WithHeadings; use Maatwebsite\Excel\Concerns\ShouldAutoSize; use Maatwebsite\Excel\Concerns\WithColumnWidths; +use Maatwebsite\Excel\Concerns\WithHeadings; use Maatwebsite\Excel\Concerns\WithStyles; +use Maatwebsite\Excel\Excel as ExcelExport; use Maatwebsite\Excel\Facades\Excel; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index bf11e543..e8758a77 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -1626,7 +1626,6 @@ public function handleMassActions() $isExport = $collection->get('isExport'); if ($isExport) { - $fileName = $collection->get('name'); $fileType = $collection->get('type'); @@ -1656,6 +1655,7 @@ public function handleMassActions() if (! count($this->selected)) { $this->selectedAction = null; + return; } From 7e8b93f1cb927e5121a6c924ba49d7c73bb7db8e Mon Sep 17 00:00:00 2001 From: Tom Shaw Date: Thu, 14 Oct 2021 17:23:03 -0500 Subject: [PATCH 011/113] Adjusting style ci issues. --- src/Exports/DatatableExport.php | 1 - src/Http/Livewire/LivewireDatatable.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Exports/DatatableExport.php b/src/Exports/DatatableExport.php index 5565ad73..9304e71f 100644 --- a/src/Exports/DatatableExport.php +++ b/src/Exports/DatatableExport.php @@ -10,7 +10,6 @@ use Maatwebsite\Excel\Concerns\WithStyles; use Maatwebsite\Excel\Excel as ExcelExport; use Maatwebsite\Excel\Facades\Excel; - use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; class DatatableExport implements FromCollection, WithHeadings, ShouldAutoSize, WithColumnWidths, WithStyles diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index e8758a77..80c5c93c 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -1581,7 +1581,7 @@ public function cellClasses($row, $column) // Override this method with your own method for adding classes to a cell return config('livewire-datatables.default_classes.cell', 'text-sm text-gray-900'); } - + public function getMassActions() { return collect($this->massActions)->map(function ($action) { From b922734b8fd5dde4d514e1b30e20171b20c121ec Mon Sep 17 00:00:00 2001 From: Tom Shaw Date: Fri, 15 Oct 2021 16:21:47 -0500 Subject: [PATCH 012/113] Evaluating a more verbose export API. --- src/Action.php | 19 +++----- src/Exports/DatatableExport.php | 61 +------------------------ src/Http/Livewire/LivewireDatatable.php | 21 ++------- 3 files changed, 12 insertions(+), 89 deletions(-) diff --git a/src/Action.php b/src/Action.php index 779ffca9..91d1ff06 100644 --- a/src/Action.php +++ b/src/Action.php @@ -7,9 +7,8 @@ class Action public $value; public $label; public $group; + public $fileName; public $isExport = false; - public $name; - public $type; public $styles = []; public $widths = []; public $callable; @@ -52,23 +51,17 @@ public static function groupBy($group, $actions) } } - public function isExport($isExport = true) + public function export($fileName) { - $this->isExport = $isExport; + $this->fileName = $fileName; + $this->isExport(); return $this; } - public function name($name) - { - $this->name = $name; - - return $this; - } - - public function type($type) + public function isExport($isExport = true) { - $this->type = $type; + $this->isExport = $isExport; return $this; } diff --git a/src/Exports/DatatableExport.php b/src/Exports/DatatableExport.php index 9304e71f..7800e539 100644 --- a/src/Exports/DatatableExport.php +++ b/src/Exports/DatatableExport.php @@ -8,7 +8,6 @@ use Maatwebsite\Excel\Concerns\WithColumnWidths; use Maatwebsite\Excel\Concerns\WithHeadings; use Maatwebsite\Excel\Concerns\WithStyles; -use Maatwebsite\Excel\Excel as ExcelExport; use Maatwebsite\Excel\Facades\Excel; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; @@ -17,8 +16,7 @@ class DatatableExport implements FromCollection, WithHeadings, ShouldAutoSize, W use Exportable; public $collection; - public $fileName = 'DatatableExport'; - public $fileType = 'xlsx'; + public $fileName = 'DatatableExport.xlsx'; public $styles = []; public $columnWidths = []; @@ -49,18 +47,6 @@ public function getFileName(): string return $this->fileName; } - public function setFileType($fileType) - { - $this->fileType = strtolower($fileType); - - return $this; - } - - public function getFileType(): string - { - return $this->fileType; - } - public function setColumnWidths($columnWidths) { $this->columnWidths = $columnWidths; @@ -95,51 +81,8 @@ public function styles(Worksheet $sheet) return $this->getStyles(); } - public function getFileWriter($fileType) - { - switch ($fileType) { - case 'xlsx': - $writer = ExcelExport::XLSX; - break; - case 'csv': - $writer = ExcelExport::CSV; - break; - case 'tsv': - $writer = ExcelExport::TSV; - break; - case 'ods': - $writer = ExcelExport::ODS; - break; - case 'xls': - $writer = ExcelExport::XLS; - break; - case 'html': - $writer = ExcelExport::HTML; - break; - case 'mpdf': - $writer = ExcelExport::MPDF; - break; - case 'dompdf': - $writer = ExcelExport::DOMPDF; - break; - case 'tcpdf': - $writer = ExcelExport::TCPDF; - break; - default: - $writer = ExcelExport::XLSX; - } - - return $writer; - } - public function download() { - $fileName = $this->getFileName(); - $fileType = $this->getFileType(); - - $writer = $this->getFileWriter($fileType); - $headers = ($fileType === 'csv') ? ['Content-Type' => 'text/csv'] : []; - - return Excel::download($this, $fileName . '.' . $fileType, $writer, $headers); + return Excel::download($this, $this->getFileName()); } } diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 80c5c93c..55008e51 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -1488,10 +1488,6 @@ public function export() $export = new DatatableExport($this->getExportResultsSet()); - $export->setFileName('DatatableExport'); - - $export->setFileType('xlsx'); - return $export->download(); } @@ -1585,7 +1581,7 @@ public function cellClasses($row, $column) public function getMassActions() { return collect($this->massActions)->map(function ($action) { - return collect($action)->except(['callable'])->toArray(); + return collect($action)->only(['group', 'value', 'label'])->toArray(); })->toArray(); } @@ -1623,24 +1619,15 @@ public function handleMassActions() $collection = collect($action); - $isExport = $collection->get('isExport'); - - if ($isExport) { - $fileName = $collection->get('name'); - $fileType = $collection->get('type'); + if ($collection->get('isExport')) { + $fileName = $collection->get('fileName'); $styles = $collection->get('styles'); $widths = $collection->get('widths'); $datatableExport = new DatatableExport($this->getExportResultsSet()); - if ($fileName) { - $datatableExport->setFileName($fileName); - } - - if ($fileType) { - $datatableExport->setFileType($fileType); - } + $datatableExport->setFileName($fileName); if ($styles) { $datatableExport->setStyles($styles); From 47d0548d5fdce4109584df422b84caba7d63bc12 Mon Sep 17 00:00:00 2001 From: Tom Shaw Date: Sat, 16 Oct 2021 07:07:33 -0500 Subject: [PATCH 013/113] Miscellaneous code cleanup/refactoring. --- .../livewire/datatables/datatable.blade.php | 10 +++--- src/Http/Livewire/LivewireDatatable.php | 31 +++++++------------ 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/resources/views/livewire/datatables/datatable.blade.php b/resources/views/livewire/datatables/datatable.blade.php index 89fe8359..d49374ec 100644 --- a/resources/views/livewire/datatables/datatable.blade.php +++ b/resources/views/livewire/datatables/datatable.blade.php @@ -39,12 +39,12 @@ @endif - @if(count($this->selectActions)) + @if(count($this->massActionsOptions))
- - - @foreach($this->selectActions as $group => $items) + @foreach($this->massActionsOptions as $group => $items) @if(!$group) @foreach($items as $item) @@ -58,7 +58,7 @@ @endif @endforeach - +
@endif diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 55008e51..b21dbc2b 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -67,7 +67,7 @@ class LivewireDatatable extends Component public $persistFilters = true; public $actions; - public $selectedAction; + public $massActionOption; /** * @var array List your groups and the corresponding label (or translation) here. @@ -1592,26 +1592,26 @@ public function getMassActionsProperty() $duplicates = $actions->pluck('value')->duplicates(); if ($duplicates->count()) { - throw new Exception('Duplicate Action(s): ' . implode(', ', $duplicates->toArray())); + throw new Exception('Duplicate Mass Action(s): ' . implode(', ', $duplicates->toArray())); } return $actions->toArray(); } - public function getSelectActionsProperty() + public function getMassActionsOptionsProperty() { return collect($this->actions)->groupBy(function ($item) { return $item['group']; }, true); } - public function handleMassActions() + public function massActionOptionHandler() { - if (! $this->selectedAction) { + if (! $this->massActionOption) { return; } - $option = $this->selectedAction; + $option = $this->massActionOption; $action = collect($this->massActions)->filter(function ($item) use ($option) { return $item->value === $option; @@ -1620,28 +1620,19 @@ public function handleMassActions() $collection = collect($action); if ($collection->get('isExport')) { - $fileName = $collection->get('fileName'); - - $styles = $collection->get('styles'); - $widths = $collection->get('widths'); - $datatableExport = new DatatableExport($this->getExportResultsSet()); - $datatableExport->setFileName($fileName); + $datatableExport->setFileName($collection->get('fileName')); - if ($styles) { - $datatableExport->setStyles($styles); - } + $datatableExport->setStyles($collection->get('styles')); - if ($widths) { - $datatableExport->setColumnWidths($widths); - } + $datatableExport->setColumnWidths($collection->get('widths')); return $datatableExport->download(); } if (! count($this->selected)) { - $this->selectedAction = null; + $this->massActionOption = null; return; } @@ -1650,7 +1641,7 @@ public function handleMassActions() $action->callable($option, $this->selected); } - $this->selectedAction = null; + $this->massActionOption = null; $this->selected = []; } } From 72fe18a3bb49b8f025046295b7e28ad03655fc87 Mon Sep 17 00:00:00 2001 From: Siddhartha Mehta Date: Tue, 16 Nov 2021 16:31:14 +0530 Subject: [PATCH 014/113] Add format method to Number Column. formats & rounds a number with grouped thousands - 1000000 => 1,000,000.00 (If places set to 2) with variable places. --- src/NumberColumn.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/NumberColumn.php b/src/NumberColumn.php index e37dd442..f4c3dcb9 100644 --- a/src/NumberColumn.php +++ b/src/NumberColumn.php @@ -18,4 +18,15 @@ public function round($places = 0) return $this; } + + // formats & rounds a number with grouped thousands - 1000000 => 1,000,000.00 (If places set to 2) + public function format($places = 2) + { + + $this->callback = function ($value) use ($places) { + return number_format($value, $places, '.', ','); + }; + + return $this; + } } From 81166415c08b0a42763d4a48b3d0c4c17ed21314 Mon Sep 17 00:00:00 2001 From: Siddhartha Mehta Date: Tue, 16 Nov 2021 16:36:52 +0530 Subject: [PATCH 015/113] Set Default places to 0 --- src/NumberColumn.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/NumberColumn.php b/src/NumberColumn.php index f4c3dcb9..8ecf1aa6 100644 --- a/src/NumberColumn.php +++ b/src/NumberColumn.php @@ -19,8 +19,7 @@ public function round($places = 0) return $this; } - // formats & rounds a number with grouped thousands - 1000000 => 1,000,000.00 (If places set to 2) - public function format($places = 2) + public function format($places = 0) { $this->callback = function ($value) use ($places) { From 2ef864de48a1dbd142b7c74d6cb25aeeb95b67f6 Mon Sep 17 00:00:00 2001 From: Siddhartha Mehta Date: Tue, 16 Nov 2021 16:41:30 +0530 Subject: [PATCH 016/113] Update NumberColumn.php --- src/NumberColumn.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/NumberColumn.php b/src/NumberColumn.php index 8ecf1aa6..49c5bccf 100644 --- a/src/NumberColumn.php +++ b/src/NumberColumn.php @@ -21,7 +21,6 @@ public function round($places = 0) public function format($places = 0) { - $this->callback = function ($value) use ($places) { return number_format($value, $places, '.', ','); }; From 141f40755d9fea1424b1a02cb22dc90966de0766 Mon Sep 17 00:00:00 2001 From: Chris Allen Date: Thu, 9 Dec 2021 22:36:30 +0000 Subject: [PATCH 017/113] Move highlight call before export callback calls. The highlight call is currently executed after the export callback, making it impossible to strip out the highlighting markup that is added for search results. Moving the highlighting before the export callbacks resolves this. --- src/Http/Livewire/LivewireDatatable.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 6d76e41d..08f5f5f3 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -1380,6 +1380,10 @@ public function mapCallbacks($paginatedCollection, $export = false) { $paginatedCollection->collect()->map(function ($row, $i) use ($export) { foreach ($row as $name => $value) { + if ($this->search && ! config('livewire-datatables.suppress_search_highlights') && $this->searchableColumns()->firstWhere('name', $name)) { + $row->$name = $this->highlight($row->$name, $this->search); + } + if ($export && isset($this->export_callbacks[$name])) { $values = Str::contains($value, static::SEPARATOR) ? explode(static::SEPARATOR, $value) : [$value, $row]; $row->$name = $this->export_callbacks[$name](...$values); @@ -1399,10 +1403,6 @@ public function mapCallbacks($paginatedCollection, $export = false) } elseif (isset($this->callbacks[$name]) && is_callable($this->callbacks[$name])) { $row->$name = $this->callbacks[$name]($value, $row); } - - if ($this->search && ! config('livewire-datatables.suppress_search_highlights') && $this->searchableColumns()->firstWhere('name', $name)) { - $row->$name = $this->highlight($row->$name, $this->search); - } } return $row; From 19abc3579d85a23359078ab10213dc8cc6631555 Mon Sep 17 00:00:00 2001 From: Chris Allen Date: Thu, 9 Dec 2021 22:36:30 +0000 Subject: [PATCH 018/113] Move highlight call before export callback calls. The highlight call is currently executed after the export callback, making it impossible to strip out the highlighting markup that is added for search results. Moving the highlighting before the export callbacks resolves this. --- src/Http/Livewire/LivewireDatatable.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 08f5f5f3..3c3f19e2 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -1383,7 +1383,6 @@ public function mapCallbacks($paginatedCollection, $export = false) if ($this->search && ! config('livewire-datatables.suppress_search_highlights') && $this->searchableColumns()->firstWhere('name', $name)) { $row->$name = $this->highlight($row->$name, $this->search); } - if ($export && isset($this->export_callbacks[$name])) { $values = Str::contains($value, static::SEPARATOR) ? explode(static::SEPARATOR, $value) : [$value, $row]; $row->$name = $this->export_callbacks[$name](...$values); From e84be27dfed87105c1b7c0ecd39566a2ef074bca Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Thu, 6 Jan 2022 14:45:49 +0000 Subject: [PATCH 019/113] wip --- composer.json | 3 +- src/Column.php | 49 ++++++++++++++++++++ src/Http/Livewire/LivewireDatatable.php | 60 +++++++++++++++++++------ 3 files changed, 97 insertions(+), 15 deletions(-) diff --git a/composer.json b/composer.json index 963137f4..8a2b19bb 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,8 @@ "php": "^7.2.5|^8.0", "illuminate/support": "^7.0|^8.0", "livewire/livewire": "^2.4.4", - "maatwebsite/excel": "^3.1" + "maatwebsite/excel": "^3.1", + "reedware/laravel-relation-joins": "^2.4" }, "require-dev": { "laravel/legacy-factories": "^1.0.4", diff --git a/src/Column.php b/src/Column.php index 1972f94a..4e77fab5 100644 --- a/src/Column.php +++ b/src/Column.php @@ -373,6 +373,55 @@ public function group($group) return $this; } + public function resolveName($query) + { + return $this->isBaseColumn() + ? $query->getModel()->getTable() . '.' . ($this->base ?? Str::before($this->name, ':')) + : $this->joinRelations($query); + } + + public function joinRelations($query) + { + $parts = explode('.', Str::before($this->name, ':')); + + $columnName = array_pop($parts); + // callsign + + $relation = implode('.', $parts); + // job.crew + + $table = ''; + $model = ''; + $lastQuery = $query; + foreach (explode('.', $relation) as $eachRelation) { + $model = $lastQuery->getRelation($eachRelation); + + ray( + $eachRelation, + $lastQuery + // collect($lastQuery->getQuery()->joins)->pluck('table'), + // $model->getQuery()->from, + // collect($lastQuery->getQuery()->joins)->pluck('table')->contains( + // $model->getQuery()->from + // ) + )->purple(); + if ( + ! collect($lastQuery->getQuery()->joins)->pluck('table')->contains( + $model->getQuery()->from + ) + ) { + ray($eachRelation, collect($lastQuery->getQuery()->joins)->pluck('table'), $model->getQuery()->from)->red(); + $query->leftJoinRelation($model); + } + + + + $lastQuery = $model->getQuery(); + } + + return $lastQuery->getQuery()->from . '.' . $columnName; + } + public function relations() { return $this->isBaseColumn() ? null : collect(explode('.', Str::beforeLast($this->name, '.'))); diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 15a91e45..a4cfd2c6 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -313,22 +313,16 @@ public function getProcessedColumnsProperty() ->sort($this->sort); } - public function resolveColumnName($column) - { - return $column->isBaseColumn() - ? $this->query->getModel()->getTable() . '.' . ($column->base ?? Str::before($column->name, ':')) - : $column->select ?? $this->resolveRelationColumn($column->base ?? $column->name, $column->aggregate); - } - public function resolveCheckboxColumnName($column) { - $column = is_object($column) - ? $column->toArray() - : $column; + // $column = is_object($column) + // ? $column->toArray() + // : $column; - return Str::contains($column['base'], '.') - ? $this->resolveRelationColumn($column['base'], $column['aggregate']) - : $this->query->getModel()->getTable() . '.' . $column['base']; + return $this->resolveColumnName($column); + // return Str::contains($column['base'], '.') + // ? $this->resolveRelationColumn($column['base'], $column['aggregate']) + // : $this->query->getModel()->getTable() . '.' . $column['base']; } public function resolveAdditionalSelects($column) @@ -372,13 +366,13 @@ public function getSelectStatements($withAlias = false, $export = false) } if (Str::startsWith($column->name, 'callback_')) { + ray($column)->red(); $column->select = $this->resolveAdditionalSelects($column); return $column; } $column->select = $this->resolveColumnName($column); - if ($column->isEditable()) { $column->select = $this->resolveEditableColumnName($column); } @@ -412,6 +406,44 @@ public function getSelectStatements($withAlias = false, $export = false) }); } + protected function resolveColumnName($column) + { + if ($column->isBaseColumn()) { + return $this->query->getModel()->getTable() . '.' . ($column->base ?? Str::before($column->name, ':')); + } + + $relations = explode('.', Str::before($column->name, ':')); + $aggregate = Str::after($column->name, ':'); + + if (! method_exists($this->query->getModel(), $relations[0])) { + return $column->name; + } + + $columnName = array_pop($relations); + $aggregateName = implode('.', $relations); + + $relatedQuery = $this->query; + + while (count($relations) > 0) { + $relation = array_shift($relations); + + $useThrough = collect($relatedQuery->getQuery()->joins) + ->pluck('table') + ->contains($relatedQuery->getRelation($relation)->getRelated()->getTable()); + + // BAIL HERE AND DO THE AGGREGATE THING + + if ($relatedQuery->getRelation($relation) instanceof HasMany || $relatedQuery->getRelation($relation) instanceof BelongsToMany) { + $this->query->customWithAggregate($aggregateName, $column->aggregate ?? 'count', $columnName, $column->name); + return null; + } + + $relatedQuery = $this->query->joinRelation($relation, null, 'left', $useThrough, $relatedQuery); + } + + return $relatedQuery->getQuery()->from . '.' . $columnName; + } + protected function resolveRelationColumn($name, $aggregate = null, $alias = null) { $parts = explode('.', Str::before($name, ':')); From 73e677b01c04e0442d5d02af79716c22b5edea16 Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Thu, 6 Jan 2022 15:53:38 +0000 Subject: [PATCH 020/113] wip --- src/Http/Livewire/LivewireDatatable.php | 4 ++-- tests/LivewireDatatableQueryBuilderTest.php | 10 +++++----- tests/TestCase.php | 6 ++++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index a4cfd2c6..7f994c02 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -427,13 +427,13 @@ protected function resolveColumnName($column) while (count($relations) > 0) { $relation = array_shift($relations); - $useThrough = collect($relatedQuery->getQuery()->joins) + $useThrough = collect($this->query->getQuery()->joins) ->pluck('table') ->contains($relatedQuery->getRelation($relation)->getRelated()->getTable()); // BAIL HERE AND DO THE AGGREGATE THING - if ($relatedQuery->getRelation($relation) instanceof HasMany || $relatedQuery->getRelation($relation) instanceof BelongsToMany) { + if ($relatedQuery->getRelation($relation) instanceof HasMany || $relatedQuery->getRelation($relation) instanceof HasManyThrough || $relatedQuery->getRelation($relation) instanceof BelongsToMany) { $this->query->customWithAggregate($aggregateName, $column->aggregate ?? 'count', $columnName, $column->name); return null; } diff --git a/tests/LivewireDatatableQueryBuilderTest.php b/tests/LivewireDatatableQueryBuilderTest.php index 6d839891..725b5c6f 100644 --- a/tests/LivewireDatatableQueryBuilderTest.php +++ b/tests/LivewireDatatableQueryBuilderTest.php @@ -123,17 +123,17 @@ public function it_creates_a_query_builder_for_belongs_to_relation_columns() $subject = new LivewireDatatable(1); $subject->mount(DummyHasManyModel::class, ['id', 'dummy_model.name']); - $this->assertEquals('select "dummy_has_many_models"."id" as "id", "dummy_models"."name" as "dummy_model.name" from "dummy_has_many_models" left join "dummy_models" on "dummy_has_many_models"."dummy_model_id" = "dummy_models"."id" order by `id` desc', $subject->getQuery()->toSql()); + $this->assertEquals('select "dummy_has_many_models"."id" as "id", "dummy_models"."name" as "dummy_model.name" from "dummy_has_many_models" left join "dummy_models" on "dummy_models"."id" = "dummy_has_many_models"."dummy_model_id" order by `id` desc', $subject->getQuery()->toSql()); $subject->sort(1); $subject->forgetComputed(); - $this->assertEquals('select "dummy_has_many_models"."id" as "id", "dummy_models"."name" as "dummy_model.name" from "dummy_has_many_models" left join "dummy_models" on "dummy_has_many_models"."dummy_model_id" = "dummy_models"."id" order by dummy_models.name desc', $subject->getQuery()->toSql()); + $this->assertEquals('select "dummy_has_many_models"."id" as "id", "dummy_models"."name" as "dummy_model.name" from "dummy_has_many_models" left join "dummy_models" on "dummy_models"."id" = "dummy_has_many_models"."dummy_model_id" order by dummy_models.name desc', $subject->getQuery()->toSql()); $subject->sort(1); $subject->forgetComputed(); - $this->assertEquals('select "dummy_has_many_models"."id" as "id", "dummy_models"."name" as "dummy_model.name" from "dummy_has_many_models" left join "dummy_models" on "dummy_has_many_models"."dummy_model_id" = "dummy_models"."id" order by dummy_models.name asc', $subject->getQuery()->toSql()); + $this->assertEquals('select "dummy_has_many_models"."id" as "id", "dummy_models"."name" as "dummy_model.name" from "dummy_has_many_models" left join "dummy_models" on "dummy_models"."id" = "dummy_has_many_models"."dummy_model_id" order by dummy_models.name asc', $subject->getQuery()->toSql()); } /** @test */ @@ -148,7 +148,7 @@ public function it_creates_a_where_query_for_belongs_to_relation_columns() // $subject->doNumberFilterEnd(1, 456); $subject->forgetComputed(); - $this->assertEquals('select "dummy_has_many_models"."id" as "id", "dummy_models"."name" as "dummy_model.name" from "dummy_has_many_models" left join "dummy_models" on "dummy_has_many_models"."dummy_model_id" = "dummy_models"."id" where (dummy_models.name >= ?) order by dummy_has_many_models.id desc', $subject->getQuery()->toSql()); + $this->assertEquals('select "dummy_has_many_models"."id" as "id", "dummy_models"."name" as "dummy_model.name" from "dummy_has_many_models" left join "dummy_models" on "dummy_models"."id" = "dummy_has_many_models"."dummy_model_id" where (dummy_models.name >= ?) order by dummy_has_many_models.id desc', $subject->getQuery()->toSql()); $this->assertEquals([123], $subject->getQuery()->getBindings()); @@ -163,7 +163,7 @@ public function it_creates_a_where_query_for_belongs_to_relation_columns() $subject->doNumberFilterEnd(1, null); $subject->forgetComputed(); - $this->assertEquals('select "dummy_has_many_models"."id" as "id", "dummy_models"."name" as "dummy_model.name" from "dummy_has_many_models" left join "dummy_models" on "dummy_has_many_models"."dummy_model_id" = "dummy_models"."id" order by dummy_has_many_models.id desc', $subject->getQuery()->toSql()); + $this->assertEquals('select "dummy_has_many_models"."id" as "id", "dummy_models"."name" as "dummy_model.name" from "dummy_has_many_models" left join "dummy_models" on "dummy_models"."id" = "dummy_has_many_models"."dummy_model_id" order by dummy_has_many_models.id desc', $subject->getQuery()->toSql()); $this->assertEquals([], $subject->getQuery()->getBindings()); } diff --git a/tests/TestCase.php b/tests/TestCase.php index 7f834513..0a2a1d45 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,12 +2,13 @@ namespace Mediconesystems\LivewireDatatables\Tests; -use Illuminate\Support\Facades\View; use Illuminate\Support\Str; +use Illuminate\Support\Facades\View; use Livewire\LivewireServiceProvider; use Maatwebsite\Excel\ExcelServiceProvider; -use Mediconesystems\LivewireDatatables\LivewireDatatablesServiceProvider; use Orchestra\Testbench\TestCase as Orchestra; +use Reedware\LaravelRelationJoins\LaravelRelationJoinServiceProvider; +use Mediconesystems\LivewireDatatables\LivewireDatatablesServiceProvider; class TestCase extends Orchestra { @@ -26,6 +27,7 @@ protected function getPackageProviders($app) LivewireServiceProvider::class, LivewireDatatablesServiceProvider::class, ExcelServiceProvider::class, + LaravelRelationJoinServiceProvider::class, ]; } From 653abb653d2d11ae0f7b2d240ac1e0e83e48dc17 Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Thu, 6 Jan 2022 15:56:01 +0000 Subject: [PATCH 021/113] wip --- src/Column.php | 49 ------------------------------------------------- 1 file changed, 49 deletions(-) diff --git a/src/Column.php b/src/Column.php index 4e77fab5..1972f94a 100644 --- a/src/Column.php +++ b/src/Column.php @@ -373,55 +373,6 @@ public function group($group) return $this; } - public function resolveName($query) - { - return $this->isBaseColumn() - ? $query->getModel()->getTable() . '.' . ($this->base ?? Str::before($this->name, ':')) - : $this->joinRelations($query); - } - - public function joinRelations($query) - { - $parts = explode('.', Str::before($this->name, ':')); - - $columnName = array_pop($parts); - // callsign - - $relation = implode('.', $parts); - // job.crew - - $table = ''; - $model = ''; - $lastQuery = $query; - foreach (explode('.', $relation) as $eachRelation) { - $model = $lastQuery->getRelation($eachRelation); - - ray( - $eachRelation, - $lastQuery - // collect($lastQuery->getQuery()->joins)->pluck('table'), - // $model->getQuery()->from, - // collect($lastQuery->getQuery()->joins)->pluck('table')->contains( - // $model->getQuery()->from - // ) - )->purple(); - if ( - ! collect($lastQuery->getQuery()->joins)->pluck('table')->contains( - $model->getQuery()->from - ) - ) { - ray($eachRelation, collect($lastQuery->getQuery()->joins)->pluck('table'), $model->getQuery()->from)->red(); - $query->leftJoinRelation($model); - } - - - - $lastQuery = $model->getQuery(); - } - - return $lastQuery->getQuery()->from . '.' . $columnName; - } - public function relations() { return $this->isBaseColumn() ? null : collect(explode('.', Str::beforeLast($this->name, '.'))); From 073688a0c4a75f29578a1f58c1aab21b3f101a18 Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Thu, 6 Jan 2022 20:47:19 +0000 Subject: [PATCH 022/113] wip --- src/Column.php | 2 +- src/Http/Livewire/LivewireDatatable.php | 134 ++---------------------- 2 files changed, 12 insertions(+), 124 deletions(-) diff --git a/src/Column.php b/src/Column.php index 1972f94a..699098bb 100644 --- a/src/Column.php +++ b/src/Column.php @@ -353,7 +353,7 @@ public function aggregate() public function isBaseColumn() { - return ! Str::contains($this->name, '.') && ! $this->raw; + return ! Str::startsWith($this->name, 'callback_') && ! Str::contains($this->name, '.') && ! $this->raw; } public function field() diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 7f994c02..1b2fbb2a 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -313,31 +313,20 @@ public function getProcessedColumnsProperty() ->sort($this->sort); } - public function resolveCheckboxColumnName($column) - { - // $column = is_object($column) - // ? $column->toArray() - // : $column; - - return $this->resolveColumnName($column); - // return Str::contains($column['base'], '.') - // ? $this->resolveRelationColumn($column['base'], $column['aggregate']) - // : $this->query->getModel()->getTable() . '.' . $column['base']; - } public function resolveAdditionalSelects($column) { $selects = collect($column->additionalSelects)->map(function ($select) use ($column) { return Str::contains($select, '.') - ? $this->resolveRelationColumn($select, Str::contains($select, ':') ? Str::after($select, ':') : null, $column->name) + ? $this->resolveColumnName($column, $select) : $this->query->getModel()->getTable() . '.' . $select; }); return $selects->count() > 1 ? new Expression("CONCAT_WS('" . static::SEPARATOR . "' ," . - collect($selects)->map(function ($select) { - return "COALESCE($select, '')"; - })->join(', ') . ')') + collect($selects)->map(function ($select) { + return "COALESCE($select, '')"; + })->join(', ') . ')') : $selects->first(); } @@ -345,7 +334,7 @@ public function resolveEditableColumnName($column) { return [ $column->select, - $this->query->getModel()->getTable() . '.' . $this->query->getModel()->getKeyName(), + $this->query->getModel()->getTable() . '.' . $this->query->getModel()->getKeyName() . ' AS ' . $column->name . '_edit_id', ]; } @@ -359,23 +348,18 @@ public function getSelectStatements($withAlias = false, $export = false) return $column; } - if ($column->isType('checkbox')) { - $column->select = $this->resolveCheckboxColumnName($column); - - return $column; - } - if (Str::startsWith($column->name, 'callback_')) { - ray($column)->red(); $column->select = $this->resolveAdditionalSelects($column); return $column; } $column->select = $this->resolveColumnName($column); + if ($column->isEditable()) { $column->select = $this->resolveEditableColumnName($column); } + // ray($column)->red(); return $column; })->when($withAlias, function ($columns) { @@ -406,14 +390,14 @@ public function getSelectStatements($withAlias = false, $export = false) }); } - protected function resolveColumnName($column) + protected function resolveColumnName($column, $additional = null) { if ($column->isBaseColumn()) { return $this->query->getModel()->getTable() . '.' . ($column->base ?? Str::before($column->name, ':')); } - $relations = explode('.', Str::before($column->name, ':')); - $aggregate = Str::after($column->name, ':'); + $relations = explode('.', Str::before(($additional ?: $column->name), ':')); + $aggregate = Str::after(($additional ?: $column->name), ':'); if (! method_exists($this->query->getModel(), $relations[0])) { return $column->name; @@ -431,8 +415,6 @@ protected function resolveColumnName($column) ->pluck('table') ->contains($relatedQuery->getRelation($relation)->getRelated()->getTable()); - // BAIL HERE AND DO THE AGGREGATE THING - if ($relatedQuery->getRelation($relation) instanceof HasMany || $relatedQuery->getRelation($relation) instanceof HasManyThrough || $relatedQuery->getRelation($relation) instanceof BelongsToMany) { $this->query->customWithAggregate($aggregateName, $column->aggregate ?? 'count', $columnName, $column->name); return null; @@ -444,96 +426,6 @@ protected function resolveColumnName($column) return $relatedQuery->getQuery()->from . '.' . $columnName; } - protected function resolveRelationColumn($name, $aggregate = null, $alias = null) - { - $parts = explode('.', Str::before($name, ':')); - $columnName = array_pop($parts); - $relation = implode('.', $parts); - - return method_exists($this->query->getModel(), $parts[0]) - ? $this->joinRelation($relation, $columnName, $aggregate, $alias ?? $name) - : $name; - } - - protected function joinRelation($relation, $relationColumn, $aggregate = null, $alias = null) - { - $table = ''; - $model = ''; - $lastQuery = $this->query; - foreach (explode('.', $relation) as $eachRelation) { - $model = $lastQuery->getRelation($eachRelation); - - switch (true) { - case $model instanceof HasOne: - $table = $model->getRelated()->getTable(); - $foreign = $model->getQualifiedForeignKeyName(); - $other = $model->getQualifiedParentKeyName(); - break; - - case $model instanceof HasMany: - $this->query->customWithAggregate($relation, $aggregate ?? 'count', $relationColumn, $alias); - $table = null; - break; - - case $model instanceof BelongsTo: - $table = $model->getRelated()->getTable(); - $foreign = $model->getQualifiedForeignKeyName(); - $other = $model->getQualifiedOwnerKeyName(); - break; - - case $model instanceof BelongsToMany: - $this->query->customWithAggregate($relation, $aggregate ?? 'count', $relationColumn, $alias); - $table = null; - break; - - case $model instanceof HasOneThrough: - $pivot = explode('.', $model->getQualifiedParentKeyName())[0]; - $pivotPK = $model->getQualifiedFirstKeyName(); - $pivotFK = $model->getQualifiedLocalKeyName(); - $this->performJoin($pivot, $pivotPK, $pivotFK); - - $related = $model->getRelated(); - $table = $related->getTable(); - $tablePK = $related->getForeignKey(); - $foreign = $pivot . '.' . $tablePK; - $other = $related->getQualifiedKeyName(); - - break; - - default: - $this->query->customWithAggregate($relation, $aggregate ?? 'count', $relationColumn, $alias); - } - if ($table) { - $this->performJoin($table, $foreign, $other); - } - $lastQuery = $model->getQuery(); - } - - if ($model instanceof HasOne || $model instanceof BelongsTo || $model instanceof HasOneThrough) { - return $table . '.' . $relationColumn; - } - - if ($model instanceof HasMany) { - return; - } - - if ($model instanceof BelongsToMany) { - return; - } - } - - protected function performJoin($table, $foreign, $other, $type = 'left') - { - $joins = []; - foreach ((array) $this->query->getQuery()->joins as $key => $join) { - $joins[] = $join->table; - } - - if (! in_array($table, $joins)) { - $this->query->join($table, $foreign, '=', $other, $type); - } - } - public function getFreshColumnsProperty() { $columns = $this->processedColumns->columnsArray(); @@ -1524,10 +1416,8 @@ public function mapCallbacks($paginatedCollection, $export = false) 'value' => $value, 'key' => $this->builder()->getModel()->getQualifiedKeyName(), 'column' => Str::after($name, '.'), - 'rowId' => $row->{$this->builder()->getModel()->getTable() . '.' . $this->builder()->getModel()->getKeyName()} ?? $row->{$this->builder()->getModel()->getKeyName()}, + 'rowId' => $row->{$name . '_edit_id'}, ]); - } elseif ($export && isset($this->export_callbacks[$name])) { - $row->$name = $this->export_callbacks[$name]($value, $row); } elseif (isset($this->callbacks[$name]) && is_string($this->callbacks[$name])) { $row->$name = $this->{$this->callbacks[$name]}($value, $row); } elseif (Str::startsWith($name, 'callback_')) { @@ -1643,8 +1533,6 @@ public function getQuery($export = false) public function checkboxQuery() { - $this->resolveCheckboxColumnName(collect($this->freshColumns)->firstWhere('type', 'checkbox')); - return $this->query->reorder()->get()->map(function ($row) { return (string) $row->checkbox_attribute; }); From 3f9566bf0276289bff532dcfbf5e6c093f776ff4 Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Thu, 6 Jan 2022 21:20:33 +0000 Subject: [PATCH 023/113] Apply fixes from StyleCI (#374) Co-authored-by: StyleCI Bot --- src/Http/Livewire/LivewireDatatable.php | 5 +---- tests/TestCase.php | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 1b2fbb2a..b2f151ad 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -3,12 +3,9 @@ namespace Mediconesystems\LivewireDatatables\Http\Livewire; use Exception; -use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasManyThrough; -use Illuminate\Database\Eloquent\Relations\HasOne; -use Illuminate\Database\Eloquent\Relations\HasOneThrough; use Illuminate\Database\Query\Expression; use Illuminate\Support\Arr; use Illuminate\Support\Facades\DB; @@ -313,7 +310,6 @@ public function getProcessedColumnsProperty() ->sort($this->sort); } - public function resolveAdditionalSelects($column) { $selects = collect($column->additionalSelects)->map(function ($select) use ($column) { @@ -417,6 +413,7 @@ protected function resolveColumnName($column, $additional = null) if ($relatedQuery->getRelation($relation) instanceof HasMany || $relatedQuery->getRelation($relation) instanceof HasManyThrough || $relatedQuery->getRelation($relation) instanceof BelongsToMany) { $this->query->customWithAggregate($aggregateName, $column->aggregate ?? 'count', $columnName, $column->name); + return null; } diff --git a/tests/TestCase.php b/tests/TestCase.php index 0a2a1d45..9680e5d3 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,13 +2,13 @@ namespace Mediconesystems\LivewireDatatables\Tests; -use Illuminate\Support\Str; use Illuminate\Support\Facades\View; +use Illuminate\Support\Str; use Livewire\LivewireServiceProvider; use Maatwebsite\Excel\ExcelServiceProvider; +use Mediconesystems\LivewireDatatables\LivewireDatatablesServiceProvider; use Orchestra\Testbench\TestCase as Orchestra; use Reedware\LaravelRelationJoins\LaravelRelationJoinServiceProvider; -use Mediconesystems\LivewireDatatables\LivewireDatatablesServiceProvider; class TestCase extends Orchestra { From 098fb1b3ee03b9b52fb383bf4f3c470e92fd8e06 Mon Sep 17 00:00:00 2001 From: Herbert Maschke Date: Thu, 6 Jan 2022 22:58:27 +0100 Subject: [PATCH 024/113] obey table prefix, if defined in database connection (#365) Co-authored-by: Herbert Maschke --- src/Http/Livewire/LivewireDatatable.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 15a91e45..6a131433 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -70,6 +70,8 @@ class LivewireDatatable extends Component public $persistFilters = true; public $row = 1; + public $tablePrefix = ''; + /** * @var array List your groups and the corresponding label (or translation) here. * The label can be a i18n placeholder like 'app.my_string' and it will be automatically translated via __(). @@ -1112,6 +1114,8 @@ public function buildDatabaseQuery($export = false) { $this->query = $this->builder(); + $this->tablePrefix = $this->query->getConnection()->getTablePrefix() ?? ''; + $this->query->addSelect( $this->getSelectStatements(true, $export) ->filter() @@ -1234,10 +1238,10 @@ public function addGlobalSearch() foreach ($this->getColumnFilterStatement($i) as $column) { $query->when(is_array($column), function ($query) use ($search, $column) { foreach ($column as $col) { - $query->orWhereRaw('LOWER(' . $col . ') like ?', '%' . mb_strtolower($search) . '%'); + $query->orWhereRaw('LOWER(' . $this->tablePrefix . $col . ') like ?', '%' . mb_strtolower($search) . '%'); } }, function ($query) use ($search, $column) { - $query->orWhereRaw('LOWER(' . $column . ') like ?', '%' . mb_strtolower($search) . '%'); + $query->orWhereRaw('LOWER(' . $this->tablePrefix . $column . ') like ?', '%' . mb_strtolower($search) . '%'); }); } }); @@ -1280,14 +1284,14 @@ public function addSelectFilters() if ($this->freshColumns[$index]['type'] === 'json') { $query->where(function ($query) use ($value, $index) { foreach ($this->getColumnFilterStatement($index) as $column) { - $query->whereRaw('LOWER(' . $column . ') like ?', [mb_strtolower("%$value%")]); + $query->whereRaw('LOWER(' . $this->tablePrefix . $column . ') like ?', [mb_strtolower("%$value%")]); } }); } else { $query->orWhere(function ($query) use ($value, $index) { foreach ($this->getColumnFilterStatement($index) as $column) { if (Str::contains(mb_strtolower($column), 'concat')) { - $query->orWhereRaw('LOWER(' . $column . ') like ?', [mb_strtolower("%$value%")]); + $query->orWhereRaw('LOWER(' . $this->tablePrefix . $column . ') like ?', [mb_strtolower("%$value%")]); } else { $query->orWhereRaw($column . ' = ?', $value); } @@ -1355,7 +1359,7 @@ public function addTextFilters() $query->orWhere(function ($query) use ($index, $value) { foreach ($this->getColumnFilterStatement($index) as $column) { $column = is_array($column) ? $column[0] : $column; - $query->orWhereRaw('LOWER(' . $column . ') like ?', [mb_strtolower("%$value%")]); + $query->orWhereRaw('LOWER(' . $this->tablePrefix . $column . ') like ?', [mb_strtolower("%$value%")]); } }); } From 5a60a95f61232cd5c273fb2755a69623967a6c02 Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Fri, 7 Jan 2022 10:53:51 +0000 Subject: [PATCH 025/113] bugfix --- src/Http/Livewire/LivewireDatatable.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index b2f151ad..7dd0faff 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -355,7 +355,6 @@ public function getSelectStatements($withAlias = false, $export = false) if ($column->isEditable()) { $column->select = $this->resolveEditableColumnName($column); } - // ray($column)->red(); return $column; })->when($withAlias, function ($columns) { @@ -396,7 +395,7 @@ protected function resolveColumnName($column, $additional = null) $aggregate = Str::after(($additional ?: $column->name), ':'); if (! method_exists($this->query->getModel(), $relations[0])) { - return $column->name; + return ($additional ?: $column->name); } $columnName = array_pop($relations); From 2696ff22bb772931c9153b3072f9aa33fbcee004 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Fri, 7 Jan 2022 10:54:11 +0000 Subject: [PATCH 026/113] Apply fixes from StyleCI --- src/Http/Livewire/LivewireDatatable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 7dd0faff..5bf08e87 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -395,7 +395,7 @@ protected function resolveColumnName($column, $additional = null) $aggregate = Str::after(($additional ?: $column->name), ':'); if (! method_exists($this->query->getModel(), $relations[0])) { - return ($additional ?: $column->name); + return $additional ?: $column->name; } $columnName = array_pop($relations); From c8fe8c89f828b3c27c2e9684d75441953a3492aa Mon Sep 17 00:00:00 2001 From: Rik Thomas Date: Tue, 11 Jan 2022 21:11:48 +0000 Subject: [PATCH 027/113] Get $model from builder() method (#379) * Fix applied * StyleCI * Bit neater Co-authored-by: Rik Thomas --- src/Http/Livewire/LivewireDatatable.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 6a131433..32d5c269 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -184,7 +184,7 @@ public function updatedSearch() } public function mount( - $model = null, + $model = false, $include = [], $exclude = [], $hide = [], @@ -231,6 +231,7 @@ public function mount( $this->initialiseFilters(); $this->initialisePerPage(); $this->initialiseColumnGroups(); + $this->model = $this->model ?: get_class($this->builder()->getModel()); } // save settings From c80d5af18ccbf6b83dd83c456cb07a23cf42f9ea Mon Sep 17 00:00:00 2001 From: Alberto Peripolli Date: Thu, 20 Jan 2022 17:49:44 +0100 Subject: [PATCH 028/113] datatable --- .../livewire/datatables/datatable.blade.php | 18 +++++++++++------- src/Http/Livewire/LivewireDatatable.php | 3 +++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/resources/views/livewire/datatables/datatable.blade.php b/resources/views/livewire/datatables/datatable.blade.php index bd854f69..b733b853 100644 --- a/resources/views/livewire/datatables/datatable.blade.php +++ b/resources/views/livewire/datatables/datatable.blade.php @@ -27,7 +27,7 @@
@if($this->activeFilters) - FILTERS ACTIVE + @lang('Filter active') @endif
@@ -58,6 +58,10 @@ class="px-3 py-2 border border-green-400 rounded-md bg-white text-green-500 text {{ isset($this->groupLabels[$name]) ? __($this->groupLabels[$name]) : __('Toggle :group', ['group' => $name]) }} @endforeach + + @if($buttonsSlot) + @include($buttonsSlot) + @endif
@@ -74,7 +78,7 @@ class="px-3 py-2 border border-green-400 rounded-md bg-white text-green-500 text @endif -
+
@unless($this->hideHeader) @@ -84,7 +88,7 @@ class="px-3 py-2 border border-green-400 rounded-md bg-white text-green-500 text @include('datatables::header-inline-hide', ['column' => $column, 'sort' => $sort]) @elseif($column['type'] === 'checkbox') @unless($column['hidden']) -
+
{{ count($selected) }}
@@ -96,11 +100,11 @@ class="px-3 py-2 border border-green-400 rounded-md bg-white text-green-500 text @endforeach
-
+
@foreach($this->columns as $index => $column) @if($column['hidden']) @if($hideable === 'inline') -
+
@endif @elseif($column['type'] === 'checkbox')
@@ -159,7 +163,7 @@ class="px-3 py-2 border border-green-400 rounded-md bg-white text-green-500 text
@unless($this->hidePagination) -
+
{{-- check if there is any data --}} @if(count($this->results)) @@ -193,7 +197,7 @@ class="px-3 py-2 border border-green-400 rounded-md bg-white text-green-500 text
@if($complex) -
+
@endif diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 32d5c269..30412129 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -56,6 +56,7 @@ class LivewireDatatable extends Component public $selected = []; public $beforeTableSlot; public $afterTableSlot; + public $buttonsSlot; public $complex; public $complexQuery; public $title; @@ -200,6 +201,7 @@ public function mount( $hideable = false, $beforeTableSlot = false, $afterTableSlot = false, + $buttonsSlot = false, $params = [] ) { foreach ([ @@ -217,6 +219,7 @@ public function mount( 'hideable', 'beforeTableSlot', 'afterTableSlot', + 'buttonsSlot', ] as $property) { $this->$property = $this->$property ?? $$property; } From 72a99902bb177ba2cfb93966b3a6e73189547a6f Mon Sep 17 00:00:00 2001 From: Herbert Maschke Date: Thu, 3 Feb 2022 15:25:25 +0100 Subject: [PATCH 029/113] improvements of table reset --- src/Http/Livewire/LivewireDatatable.php | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 6d76e41d..b7c0a808 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -113,9 +113,7 @@ public function applyToTable($options) if (isset($options['hiddenColumns']) && is_array($options['hiddenColumns'])) { // first display all columns, - foreach ($this->columns as $key => $column) { - $this->columns[$key]['hidden'] = false; - } + $this->resetHiddenColumns(); // then hide all columns that should be hidden: foreach ($options['hiddenColumns'] as $columnToHide) { @@ -140,6 +138,7 @@ public function applyToTable($options) public function resetTable() { $this->perPage = config('livewire-datatables.default_per_page', 10); + $this->sort = $this->defaultSort(); $this->search = null; $this->page = 1; $this->activeSelectFilters = []; @@ -149,9 +148,21 @@ public function resetTable() $this->activeBooleanFilters = []; $this->activeNumberFilters = []; $this->hide = null; + $this->resetHiddenColumns(); $this->selected = []; } + /** + * Display all columns, also those that are currently hidden. + * Should get called when resetting the table. + */ + public function resetHiddenColumns() + { + foreach ($this->columns as $key => $column) { + $this->columns[$key]['hidden'] = false; + } + } + public function updatedSearch() { $this->page = 1; From b0db5d93931a02b2f672dd97014c2d3c35f70c42 Mon Sep 17 00:00:00 2001 From: Tom Shaw Date: Thu, 3 Feb 2022 10:24:56 -0600 Subject: [PATCH 030/113] Update LivewireDatatable.php --- src/Http/Livewire/LivewireDatatable.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 3f8056f8..54db4046 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -237,7 +237,6 @@ public function mount( $this->params = $params; $this->columns = $this->getViewColumns(); - $this->actions = $this->getMassActions(); $this->initialiseSearch(); From 4ee6daa7cf1c8e1184f17adab9869ca5d6af8d3f Mon Sep 17 00:00:00 2001 From: Tom Shaw Date: Thu, 3 Feb 2022 10:32:07 -0600 Subject: [PATCH 031/113] Update LivewireDatatable.php --- src/Http/Livewire/LivewireDatatable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 54db4046..cdf1b808 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -238,7 +238,7 @@ public function mount( $this->columns = $this->getViewColumns(); $this->actions = $this->getMassActions(); - + $this->initialiseSearch(); $this->initialiseSort(); $this->initialiseHiddenColumns(); From 5680aa18078567739bf990c7df54b2a2c4b0a5af Mon Sep 17 00:00:00 2001 From: Tom Shaw Date: Thu, 3 Feb 2022 10:33:41 -0600 Subject: [PATCH 032/113] Update LivewireDatatable.php --- src/Http/Livewire/LivewireDatatable.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index cdf1b808..fa37c12b 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -238,7 +238,6 @@ public function mount( $this->columns = $this->getViewColumns(); $this->actions = $this->getMassActions(); - $this->initialiseSearch(); $this->initialiseSort(); $this->initialiseHiddenColumns(); From 432996ef6095d12fb97f0a4fcdee4b17e7b4ed7b Mon Sep 17 00:00:00 2001 From: Shift Date: Sun, 6 Feb 2022 23:27:16 +0000 Subject: [PATCH 033/113] Bump dependencies for Laravel 9 --- composer.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 963137f4..1ab56fca 100644 --- a/composer.json +++ b/composer.json @@ -17,14 +17,14 @@ ], "require": { "php": "^7.2.5|^8.0", - "illuminate/support": "^7.0|^8.0", + "illuminate/support": "^7.0|^8.0|^9.0", "livewire/livewire": "^2.4.4", "maatwebsite/excel": "^3.1" }, "require-dev": { "laravel/legacy-factories": "^1.0.4", - "orchestra/testbench": "^4.0|5.0|6.0", - "phpunit/phpunit": "^8.0|9.0" + "orchestra/testbench": "^4.0|5.0|6.0|^7.0", + "phpunit/phpunit": "^8.0|9.0|^9.5.10" }, "autoload": { "psr-4": { @@ -39,7 +39,6 @@ "scripts": { "test": "vendor/bin/phpunit", "test-coverage": "vendor/bin/phpunit --coverage-html coverage" - }, "config": { "sort-packages": true From 8ac87b7cef316da14e2792e11bb836a07badd5ae Mon Sep 17 00:00:00 2001 From: Herbert Maschke Date: Wed, 16 Feb 2022 16:23:33 +0100 Subject: [PATCH 034/113] rename livewire:datatable to make:livewire-datatable as it is the canon of other make: commands in laravel --- README.md | 6 +++--- src/Commands/MakeDatatableCommand.php | 2 +- tests/MakeDatatableCommandTest.php | 16 ++++++++-------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index fc8bbb10..941cb16d 100644 --- a/README.md +++ b/README.md @@ -94,12 +94,12 @@ somewhere in your CSS ## Component Syntax ### Create a livewire component that extends ```Mediconesystems\LivewireDatatables\LivewireDatatable``` -> ```php artisan livewire:datatable foo``` --> 'app/Http/Livewire/Foo.php' +> ```php artisan make:livewire-datatable foo``` --> 'app/Http/Livewire/Foo.php' -> ```php artisan livewire:datatable tables.bar``` --> 'app/Http/Livewire/Tables/Bar.php' +> ```php artisan make:livewire-datatable tables.bar``` --> 'app/Http/Livewire/Tables/Bar.php' ### Provide a datasource by declaring public property ```$model``` **OR** public method ```builder()``` that returns an instance of ```Illuminate\Database\Eloquent\Builder``` -> ```php artisan livewire:datatable users-table --model=user``` --> 'app/Http/Livewire/UsersTable.php' with ```public $model = User::class``` +> ```php artisan make:livewire-datatable users-table --model=user``` --> 'app/Http/Livewire/UsersTable.php' with ```public $model = User::class``` ### Declare a public method ```columns``` that returns an array containing one or more ```Mediconesystems\LivewireDatatables\Column``` diff --git a/src/Commands/MakeDatatableCommand.php b/src/Commands/MakeDatatableCommand.php index 6be53786..b323b1be 100644 --- a/src/Commands/MakeDatatableCommand.php +++ b/src/Commands/MakeDatatableCommand.php @@ -8,7 +8,7 @@ class MakeDatatableCommand extends FileManipulationCommand { - protected $signature = 'livewire:datatable {name} {--model=}'; + protected $signature = 'make:livewire-datatable {name} {--model=}'; protected $desciption = 'Create a new Livewire Datatable'; diff --git a/tests/MakeDatatableCommandTest.php b/tests/MakeDatatableCommandTest.php index 578aa243..4414f268 100644 --- a/tests/MakeDatatableCommandTest.php +++ b/tests/MakeDatatableCommandTest.php @@ -12,7 +12,7 @@ class MakeDatatableCommandTest extends TestCase /** @test */ public function component_is_created_by_make_command() { - Artisan::call('livewire:datatable', ['name' => 'foo']); + Artisan::call('make:livewire-datatable', ['name' => 'foo']); $this->assertTrue(File::exists($this->livewireClassesPath('Foo.php'))); } @@ -20,7 +20,7 @@ public function component_is_created_by_make_command() /** @test */ public function dot_nested_component_is_created_by_make_command() { - Artisan::call('livewire:datatable', ['name' => 'foo.bar']); + Artisan::call('make:livewire-datatable', ['name' => 'foo.bar']); $this->assertTrue(File::exists($this->livewireClassesPath('Foo/Bar.php'))); } @@ -28,7 +28,7 @@ public function dot_nested_component_is_created_by_make_command() /** @test */ public function forward_slash_nested_component_is_created_by_make_command() { - Artisan::call('livewire:datatable', ['name' => 'foo/bar']); + Artisan::call('make:livewire-datatable', ['name' => 'foo/bar']); $this->assertTrue(File::exists($this->livewireClassesPath('Foo/Bar.php'))); } @@ -36,7 +36,7 @@ public function forward_slash_nested_component_is_created_by_make_command() /** @test */ public function multiword_component_is_created_by_make_command() { - Artisan::call('livewire:datatable', ['name' => 'foo-bar']); + Artisan::call('make:livewire-datatable', ['name' => 'foo-bar']); $this->assertTrue(File::exists($this->livewireClassesPath('FooBar.php'))); } @@ -44,7 +44,7 @@ public function multiword_component_is_created_by_make_command() /** @test */ public function pascal_case_component_is_automatically_converted_by_make_command() { - Artisan::call('livewire:datatable', ['name' => 'FooBar.FooBar']); + Artisan::call('make:livewire-datatable', ['name' => 'FooBar.FooBar']); $this->assertTrue(File::exists($this->livewireClassesPath('FooBar/FooBar.php'))); } @@ -52,7 +52,7 @@ public function pascal_case_component_is_automatically_converted_by_make_command /** @test */ public function snake_case_component_is_automatically_converted_by_make_command() { - Artisan::call('livewire:datatable', ['name' => 'text_replace']); + Artisan::call('make:livewire-datatable', ['name' => 'text_replace']); $this->assertTrue(File::exists($this->livewireClassesPath('TextReplace.php'))); } @@ -60,7 +60,7 @@ public function snake_case_component_is_automatically_converted_by_make_command( /** @test */ public function snake_case_component_is_automatically_converted_by_make_command_on_nested_component() { - Artisan::call('livewire:datatable', ['name' => 'TextManager.text_replace']); + Artisan::call('make:livewire-datatable', ['name' => 'TextManager.text_replace']); $this->assertTrue(File::exists($this->livewireClassesPath('TextManager/TextReplace.php'))); } @@ -79,7 +79,7 @@ public function new_component_model_name_matches_option() /** @test */ public function a_component_is_not_created_with_a_reserved_class_name() { - Artisan::call('livewire:datatable', ['name' => 'component']); + Artisan::call('make:livewire-datatable', ['name' => 'component']); $this->assertFalse(File::exists($this->livewireClassesPath('Component.php'))); } From e179ab4206194df2f0f1509c3d4c88888d0989ac Mon Sep 17 00:00:00 2001 From: Herbert Maschke Date: Thu, 17 Feb 2022 08:44:52 +0100 Subject: [PATCH 035/113] move checkbox to separate view file --- .../livewire/datatables/datatable.blade.php | 7 +- .../datatables/filters/checkbox.blade.php | 6 + src/ColumnSet.php.old.ctp | 228 ------------------ 3 files changed, 7 insertions(+), 234 deletions(-) create mode 100644 resources/views/livewire/datatables/filters/checkbox.blade.php delete mode 100644 src/ColumnSet.php.old.ctp diff --git a/resources/views/livewire/datatables/datatable.blade.php b/resources/views/livewire/datatables/datatable.blade.php index c3497e72..662b32b9 100644 --- a/resources/views/livewire/datatables/datatable.blade.php +++ b/resources/views/livewire/datatables/datatable.blade.php @@ -103,12 +103,7 @@ class="px-3 py-2 border border-green-400 rounded-md bg-white text-green-500 text
@endif @elseif($column['type'] === 'checkbox') -
-
SELECT ALL
-
- results->total()) checked @endif class="form-checkbox mt-1 h-4 w-4 text-blue-600 transition duration-150 ease-in-out" /> -
-
+ @include('datatables::filters.checkbox') @elseif($column['type'] === 'label')
{{ $column['label'] ?? '' }} diff --git a/resources/views/livewire/datatables/filters/checkbox.blade.php b/resources/views/livewire/datatables/filters/checkbox.blade.php new file mode 100644 index 00000000..2ac3c617 --- /dev/null +++ b/resources/views/livewire/datatables/filters/checkbox.blade.php @@ -0,0 +1,6 @@ +
+
SELECT ALL
+
+ results->total()) checked @endif class="w-4 h-4 mt-1 text-blue-600 form-checkbox transition duration-150 ease-in-out" /> +
+
diff --git a/src/ColumnSet.php.old.ctp b/src/ColumnSet.php.old.ctp deleted file mode 100644 index f63b5868..00000000 --- a/src/ColumnSet.php.old.ctp +++ /dev/null @@ -1,228 +0,0 @@ -columns = $columns; - } - - public static function build($input) - { - return is_array($input) - ? self::fromArray($input) - : self::fromModelInstance($input); - } - - public static function fromModelInstance($model) - { - return new static( - collect($model->getAttributes())->keys()->reject(function ($name) use ($model) { - return in_array($name, $model->getHidden()); - })->map(function ($attribute) use ($model) { - return Column::name($attribute); - }) - ); - } - - public static function fromArray($columns) - { - return new static(collect($columns)); - } - - public function include($include) - { - if (!$include) { - return $this; - } - - $include = collect(is_array($include) ? $include : array_map('trim', explode(',', $include))); - $this->columns = $include->map(function ($column) { - return Str::contains($column, '|') - ? Column::name(Str::before($column, '|'))->label(Str::after($column, '|')) - : Column::name($column); - }); - - return $this; - } - - public function exclude($exclude) - { - if (!$exclude) { - return $this; - } - - $exclude = is_array($exclude) ? $exclude : array_map('trim', explode(',', $exclude)); - - $this->columns = $this->columns->reject(function ($column) use ($exclude) { - return in_array(Str::after($column->name, '.'), $exclude); - }); - - return $this; - } - - public function hide($hidden) - { - if (!$hidden) { - return $this; - } - $hidden = is_array($hidden) ? $hidden : array_map('trim', explode(',', $hidden)); - $this->columns->each(function ($column) use ($hidden) { - $column->hidden = in_array(Str::after($column->name, '.'), $hidden); - }); - - return $this; - } - - public function formatDates($dates) - { - $dates = is_array($dates) ? $dates : array_map('trim', explode(',', $dates)); - - $this->columns = $this->columns->map(function ($column) use ($dates) { - foreach ($dates as $date) { - if ($column->name === Str::before($date, '|')) { - $format = Str::of($date)->contains('|') ? Str::after($date, '|') : null; - - return DateColumn::name($column->name)->format($format); - } - } - return $column; - }); - - return $this; - } - - public function formatTimes($times) - { - $times = is_array($times) ? $times : array_map('trim', explode(',', $times)); - - $this->columns = $this->columns->map(function ($column) use ($times) { - foreach ($times as $time) { - if (Str::after($column->name, '.') === Str::before($time, '|')) { - $format = Str::of($time)->contains('|') ? Str::after($time, '|') : null; - return TimeColumn::name($column->name)->format($format); - } - } - return $column; - }); - - return $this; - } - - public function search($searchable) - { - if (!$searchable) { - return $this; - } - - $searchable = is_array($searchable) ? $searchable : array_map('trim', explode(',', $searchable)); - $this->columns->each(function ($column) use ($searchable) { - $column->searchable = in_array($column->name, $searchable); - }); - - return $this; - } - - public function sort($sort) - { - if ($sort && $column = $this->columns->first(function ($column) use ($sort) { - return Str::after($column->name, '.') === Str::before($sort, '|'); - })) { - $column->defaultSort(Str::of($sort)->contains('|') ? Str::after($sort, '|') : null); - } - return $this; - } - - public function columns() - { - return collect($this->columns); - } - - public function columnsArray() - { - return $this->columns()->map->toArray()->toArray(); - } - - public function processForBuilder($builder) - { - $this->columns = $this->columns->map(function ($column) use ($builder) { - - foreach (array_merge([$column->base ?? $column->name], $column->additionalSelects) as $name) { - - if (!Str::contains($name, '.')) { - if (!Str::startsWith($name, 'callback_')) { - $selects[] = $builder->getModel()->getTable() . '.' . $name; - if ($column->isEditable()) { - $selects[] = $builder->getModel()->getTable() . '.' . $builder->getModel()->getKeyName() . ' AS ' . $builder->getModel()->getTable() . '.' . $builder->getModel()->getKeyName(); - } - } - } - - $parent = $builder; - foreach (explode('.', Str::beforeLast($name, '.')) as $join) { - - if (method_exists($parent->getModel(), $join)) { - $relation = $parent->getRelation($join); - // dump($parent, $join, $relation); - if ($relation instanceof HasOne || $relation instanceof BelongsTo) { - $column->joins[] = [ - $relation->getRelated()->getTable(), - $relation instanceof HasOne ? $relation->getQualifiedForeignKeyName() : $relation->getQualifiedOwnerKeyName(), - $relation instanceof HasOne ? $relation->getQualifiedParentKeyName() : $relation->getQualifiedForeignKeyName() - ]; - - $parent = $relation; - - $selects = [$parent->getRelated()->getTable() . '.' . Str::afterLast($name, '.') . ($name === $column->name - ? ' AS ' . $name - : '')]; - } - - if ($relation instanceof HasMany || $relation instanceof BelongsToMany) { - $name = explode('.', $name); - $column->aggregates[] = [$name[0], $column->aggregate(), $name[1]]; - } - } - } - } - - if (count($selects) > 1) { - if ($column->callback && !$column->isEditable()) { - - $column->additionalSelects = []; - $column->select = DB::raw('CONCAT_WS("' . static::SEPARATOR . '" ,' . - collect($selects)->map(function ($select) { - return "COALESCE($select, '')"; - })->join(', ') . ')' . ' AS `' . $column->name . '`'); - } else { - $column->select = array_shift($selects); - $column->additionalSelects = $selects; - } - } else if (count($selects)) { - foreach ($selects as $select) { - $column->select = $select . ($column->callback ? ' AS ' . $column->name : ''); - } - } - - return $column; - }); - - // dd($this->columns); - return $this; - } -} From f1340a29b4b669e48566d616a0ebfd1ba84650ae Mon Sep 17 00:00:00 2001 From: Fabian Mangelsdorf <> Date: Fri, 18 Feb 2022 17:06:58 +0100 Subject: [PATCH 036/113] can set tooltip to column --- src/Column.php | 15 +++++++++++++-- src/Http/Livewire/LivewireDatatable.php | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/Column.php b/src/Column.php index d8133a1f..8a25fa6e 100644 --- a/src/Column.php +++ b/src/Column.php @@ -11,6 +11,7 @@ class Column public $type = 'string'; public $index = 0; public $label; + public $tooltip; public $name; public $select; public $joins; @@ -144,9 +145,9 @@ public function enableSummary() public function disableSummary() { - $this->summary = false; + $this->summary = false; - return $this; + return $this; } public function setIndex($index) @@ -155,6 +156,16 @@ public function setIndex($index) return $this; } + + public function tooltip($text, $label = null) + { + $this->tooltip = [ + 'text' => $text, + 'label' => $label + ]; + + return $this; + } public function sortBy($column) { diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 6060b34c..fb5cfdef 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -267,6 +267,7 @@ public function getViewColumns() 'index', 'hidden', 'label', + 'tooltip', 'group', 'summary', 'content', @@ -1087,7 +1088,6 @@ public function getPaginationControlsProperty() public function getResultsProperty() { $this->row = 1; - return $this->mapCallbacks( $this->getQuery()->paginate($this->perPage) ); From 1af1524308c269b25def962271063e72f4054cd3 Mon Sep 17 00:00:00 2001 From: Herbert Maschke Date: Fri, 18 Feb 2022 18:55:07 +0100 Subject: [PATCH 037/113] code style improvements --- src/Column.php | 8 ++++---- src/Http/Livewire/LivewireDatatable.php | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Column.php b/src/Column.php index 8a25fa6e..defc1d15 100644 --- a/src/Column.php +++ b/src/Column.php @@ -145,9 +145,9 @@ public function enableSummary() public function disableSummary() { - $this->summary = false; + $this->summary = false; - return $this; + return $this; } public function setIndex($index) @@ -156,12 +156,12 @@ public function setIndex($index) return $this; } - + public function tooltip($text, $label = null) { $this->tooltip = [ 'text' => $text, - 'label' => $label + 'label' => $label, ]; return $this; diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index fb5cfdef..0b91900b 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -1088,6 +1088,7 @@ public function getPaginationControlsProperty() public function getResultsProperty() { $this->row = 1; + return $this->mapCallbacks( $this->getQuery()->paginate($this->perPage) ); From 203315a611a3a29622f0d423f2ffdb3dfcfe1040 Mon Sep 17 00:00:00 2001 From: Herbert Maschke Date: Mon, 21 Feb 2022 10:04:18 +0100 Subject: [PATCH 038/113] add tooltip to views --- .../livewire/datatables/datatable.blade.php | 46 ++++++++++--------- .../datatables/header-no-hide.blade.php | 8 ++-- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/resources/views/livewire/datatables/datatable.blade.php b/resources/views/livewire/datatables/datatable.blade.php index c3497e72..8ac88315 100644 --- a/resources/views/livewire/datatables/datatable.blade.php +++ b/resources/views/livewire/datatables/datatable.blade.php @@ -5,20 +5,20 @@
@endif
-
-
+
+
@if($this->searchableColumns()->count()) -
+
-
- +
+
- -
+ +
@@ -31,10 +31,10 @@ @endif
- + @if($this->activeFilters) - @endif @@ -43,7 +43,7 @@
-
@endif @@ -54,7 +54,7 @@ @foreach ($columnGroups as $name => $group) @endforeach @@ -76,7 +76,7 @@ class="px-3 py-2 border border-green-400 rounded-md bg-white text-green-500 text
-
+
@unless($this->hideHeader)
@foreach($this->columns as $index => $column) @@ -84,7 +84,7 @@ class="px-3 py-2 border border-green-400 rounded-md bg-white text-green-500 text @include('datatables::header-inline-hide', ['column' => $column, 'sort' => $sort]) @elseif($column['type'] === 'checkbox') @unless($column['hidden']) -
+
{{ count($selected) }}
@@ -96,17 +96,19 @@ class="px-3 py-2 border border-green-400 rounded-md bg-white text-green-500 text @endforeach
-
+
@foreach($this->columns as $index => $column) @if($column['hidden']) @if($hideable === 'inline')
@endif @elseif($column['type'] === 'checkbox') -
+
SELECT ALL
- results->total()) checked @endif class="form-checkbox mt-1 h-4 w-4 text-blue-600 transition duration-150 ease-in-out" /> + results->total()) checked @endif class="w-4 h-4 mt-1 text-blue-600 form-checkbox transition duration-150 ease-in-out" />
@elseif($column['type'] === 'label') @@ -174,11 +176,11 @@ class="px-3 py-2 border border-green-400 rounded-md bg-white text-green-500 text @unless($this->hidePagination)
-
+
{{-- check if there is any data --}} @if(count($this->results)) -
- @foreach(config('livewire-datatables.per_page_options', [ 10, 25, 50, 100 ]) as $per_page_option) @endforeach @@ -191,7 +193,7 @@ class="px-3 py-2 border border-green-400 rounded-md bg-white text-green-500 text {{ $this->results->links('datatables::tailwind-simple-pagination') }}
- @@ -217,5 +219,5 @@ class="px-3 py-2 border border-green-400 rounded-md bg-white text-green-500 text @include($afterTableSlot)
@endif - +
diff --git a/resources/views/livewire/datatables/header-no-hide.blade.php b/resources/views/livewire/datatables/header-no-hide.blade.php index 36b0cee0..7ea5ea3d 100644 --- a/resources/views/livewire/datatables/header-no-hide.blade.php +++ b/resources/views/livewire/datatables/header-no-hide.blade.php @@ -1,5 +1,7 @@ @unless($column['hidden']) -
+
@if($column['unsortable'])
{{ str_replace('_', ' ', $column['label']) }} @@ -10,9 +12,9 @@ @if($sort === $index) @if($direction) - + @else - + @endif @endif From b9e378bde61d133d5f624c179d2ad3acfb9a05a8 Mon Sep 17 00:00:00 2001 From: Herbert Maschke Date: Mon, 21 Feb 2022 11:03:46 +0100 Subject: [PATCH 039/113] add some documentation for the mass action feature --- README.md | 58 ++++++++++++-- .../livewire/datatables/datatable.blade.php | 79 ++++++++++--------- 2 files changed, 93 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 58ccb9ed..32512d45 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ - Filter using booleans, times, dates, selects or free text - Create complex combined filters using the [complex query builder](#complex-query-builder) - Show / hide columns +- Column groups +- Mass Action (Bulk) Support ## [Live Demo App](https://livewire-datatables.com) @@ -18,7 +20,7 @@ ![screenshot](resources/images/screenshot.png "Screenshot") ## Requirements -- [Laravel 7](https://laravel.com/docs/7.x) +- [Laravel 7, 8 or 9](https://laravel.com/docs/9.x) - [Livewire](https://laravel-livewire.com/) - [Tailwind](https://tailwindcss.com/) - [Alpine JS](https://github.com/alpinejs/alpine) @@ -52,7 +54,7 @@ somewhere in your CSS ```html ... - + ... ``` @@ -62,14 +64,11 @@ somewhere in your CSS ```html ``` -- *Attention*: Please note that having multiple datatables on the same page _or_ more than one datatable of the same type on different pages needs to have a unique `name` attribute assigned to each one so they do not conflict with each other as in the example above. - ### Props | Property | Arguments | Result | Example | |----|----|----|----| @@ -160,7 +159,10 @@ class ComplexDemoTable extends LivewireDatatable (new LabelColumn()) ->label('My custom heading') - ->content('This fixed string appears in every row') + ->content('This fixed string appears in every row'), + + NumberColumn::name('dollars_spent') + ->enableSummary(), ]; } } @@ -240,6 +242,50 @@ public function columns() ->label('Planet'), ``` +### Summary row +If you need to summarize all cells of a specific column, you can use `enableSummary()`: + +```php +public function columns() +{ + return [ + Column::name('dollars_spent') + ->label('Expenses in Dollar') + ->enableSummary(), + + Column::name('euro_spent') + ->label('Expenses in Euro') + ->enableSummary(), +``` + +### Mass (Bulk) Action + +If you want to be able to act upon several records at once, you can use the `buildActions()` method in your Table: + +```php +public function buildActions() + { + return [ + + Action::value('edit')->label('Edit Selected')->group('Default Options')->callback(function ($mode, $items) { + // $items contains an array with the primary keys of the selected items + }), + + Action::value('update')->label('Update Selected')->group('Default Options')->callback(function ($mode, $items) { + // $items contains an array with the primary keys of the selected items + }), + + Action::groupBy('Export Options', function () { + return [ + Action::value('csv')->label('Export CSV')->export('SalesOrders.csv'), + Action::value('html')->label('Export HTML')->export('SalesOrders.html'), + Action::value('xlsx')->label('Export XLSX')->export('SalesOrders.xlsx')->styles($this->exportStyles)->widths($this->exportWidths) + ]; + }), + ]; + } +``` + ### Custom column names It is still possible to take full control over your table, you can define a ```builder``` method using whatever query you like, using your own joins, groups whatever, and then name your columns using your normal SQL syntax: diff --git a/resources/views/livewire/datatables/datatable.blade.php b/resources/views/livewire/datatables/datatable.blade.php index d49374ec..49b1f12f 100644 --- a/resources/views/livewire/datatables/datatable.blade.php +++ b/resources/views/livewire/datatables/datatable.blade.php @@ -5,20 +5,20 @@
@endif
-
-
+
+
@if($this->searchableColumns()->count()) -
+
-
- +
+
- -
+ +
@@ -31,34 +31,37 @@ @endif
- + @if($this->activeFilters) - @endif @if(count($this->massActionsOptions)) -
- - + @foreach($this->massActionsOptions as $group => $items) - @if(!$group) - @foreach($items as $item) - - @endforeach - @else - - @foreach($items as $item) - - @endforeach - - @endif + @if(!$group) + @foreach($items as $item) + + @endforeach + @else + + @foreach($items as $item) + + @endforeach + + @endif @endforeach - +
@endif @@ -66,7 +69,7 @@
-
@endif @@ -77,7 +80,7 @@ @foreach ($columnGroups as $name => $group) @endforeach @@ -99,7 +102,7 @@ class="px-3 py-2 border border-green-400 rounded-md bg-white text-green-500 text
-
+
@unless($this->hideHeader)
@foreach($this->columns as $index => $column) @@ -107,7 +110,7 @@ class="px-3 py-2 border border-green-400 rounded-md bg-white text-green-500 text @include('datatables::header-inline-hide', ['column' => $column, 'sort' => $sort]) @elseif($column['type'] === 'checkbox') @unless($column['hidden']) -
+
{{ count($selected) }}
@@ -119,17 +122,17 @@ class="px-3 py-2 border border-green-400 rounded-md bg-white text-green-500 text @endforeach
-
+
@foreach($this->columns as $index => $column) @if($column['hidden']) @if($hideable === 'inline')
@endif @elseif($column['type'] === 'checkbox') -
+
SELECT ALL
- results->total()) checked @endif class="form-checkbox mt-1 h-4 w-4 text-blue-600 transition duration-150 ease-in-out" /> + results->total()) checked @endif class="w-4 h-4 mt-1 text-blue-600 form-checkbox transition duration-150 ease-in-out" />
@elseif($column['type'] === 'label') @@ -183,11 +186,11 @@ class="px-3 py-2 border border-green-400 rounded-md bg-white text-green-500 text @unless($this->hidePagination)
-
+
{{-- check if there is any data --}} @if(count($this->results)) -
- @foreach(config('livewire-datatables.per_page_options', [ 10, 25, 50, 100 ]) as $per_page_option) @endforeach @@ -200,7 +203,7 @@ class="px-3 py-2 border border-green-400 rounded-md bg-white text-green-500 text {{ $this->results->links('datatables::tailwind-simple-pagination') }}
- @@ -226,5 +229,5 @@ class="px-3 py-2 border border-green-400 rounded-md bg-white text-green-500 text @include($afterTableSlot)
@endif - +
From 823a141d11339ee05a19cb3d8ab7aaa817330804 Mon Sep 17 00:00:00 2001 From: Herbert Maschke Date: Mon, 21 Feb 2022 11:08:32 +0100 Subject: [PATCH 040/113] re-add accidentally lost hint in README.md --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 32512d45..836fdefe 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ somewhere in your CSS ```html ... - + ... ``` @@ -64,11 +64,16 @@ somewhere in your CSS ```html ``` +*Attention*: Please note that having multiple datatables on the same page _or_ more than one datatable of the same +type on different pages needs to have a unique `name` attribute assigned to each one so they do not conflict with each +other as in the example above. + ### Props | Property | Arguments | Result | Example | |----|----|----|----| From 3e666b6398009e440053974ef58f39ccc0f6b75b Mon Sep 17 00:00:00 2001 From: Fabian Mangelsdorf <> Date: Mon, 21 Feb 2022 12:32:21 +0100 Subject: [PATCH 041/113] fix: use set sort direction --- src/Http/Livewire/LivewireDatatable.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 0b91900b..98a79a8b 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -770,11 +770,14 @@ public function sort($index, $direction = null) if ($direction === null) { // toggle direction $this->direction = ! $this->direction; } else { - $this->direction = $direction === 'desc' ? false : true; + $this->direction = $direction === 'asc' ? true : false; } } else { $this->sort = (int) $index; } + if ($direction !== null) { + $this->direction = $direction === 'asc' ? true : false; + } $this->setPage(1); session()->put([ From e185f06740d8ed63ac3a4bdc5088bff192361e24 Mon Sep 17 00:00:00 2001 From: Herbert Maschke Date: Tue, 22 Feb 2022 12:01:15 +0100 Subject: [PATCH 042/113] add documentation about bulk export styling possibility --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.md b/README.md index 836fdefe..b2ed01d2 100644 --- a/README.md +++ b/README.md @@ -291,6 +291,29 @@ public function buildActions() } ``` +### Mass Action Style + +If you only have small style adjustments to the Bulk Action Dropdown you can adjust some settings here: + +```php +public function getExportStylesProperty() + { + return [ + '1' => ['font' => ['bold' => true]], + 'B2' => ['font' => ['italic' => true]], + 'C' => ['font' => ['size' => 16]], + ]; + } + + public function getExportWidthsProperty() + { + return [ + 'A' => 55, + 'B' => 45, + ]; + } +``` + ### Custom column names It is still possible to take full control over your table, you can define a ```builder``` method using whatever query you like, using your own joins, groups whatever, and then name your columns using your normal SQL syntax: From 413654c13e8838c1c1156b709b9eacf7928c3dfb Mon Sep 17 00:00:00 2001 From: Herbert Maschke Date: Wed, 23 Feb 2022 16:42:57 +0100 Subject: [PATCH 043/113] introduce ability to pin specific records --- README.md | 25 +++++++- .../livewire/datatables/checkbox.blade.php | 8 ++- .../livewire/datatables/datatable.blade.php | 18 +----- src/Http/Livewire/LivewireDatatable.php | 12 +++- src/Traits/CanPinRecords.php | 61 +++++++++++++++++++ 5 files changed, 104 insertions(+), 20 deletions(-) create mode 100644 src/Traits/CanPinRecords.php diff --git a/README.md b/README.md index b2ed01d2..6bc4e366 100644 --- a/README.md +++ b/README.md @@ -314,6 +314,30 @@ public function getExportStylesProperty() } ``` +### Pin Records + +If you want to give your users the ability to pin specific records to be able to, for example, compare +them with each other, you can use the CanPinRecords trait. Ensure to have at least one Checkbox Column +so the user can select records: + +```php +use Mediconesystems\LivewireDatatables\Traits\CanPinRecords; + +class RecordTable extends LivewireDatatable +{ + use CanPinRecords; + + public $model = Record::class; + + public function columns() + { + return [ + Column::checkbox(), + + // ... + +``` + ### Custom column names It is still possible to take full control over your table, you can define a ```builder``` method using whatever query you like, using your own joins, groups whatever, and then name your columns using your normal SQL syntax: @@ -343,7 +367,6 @@ public function columns() ``` - ### Callbacks Callbacks give you the freedom to perform any mutations you like on the data before displaying in the table. - The callbacks are performed on the paginated results of the database query, so shouldn't use a ton of memory diff --git a/resources/views/livewire/datatables/checkbox.blade.php b/resources/views/livewire/datatables/checkbox.blade.php index ba58dcab..35617c37 100644 --- a/resources/views/livewire/datatables/checkbox.blade.php +++ b/resources/views/livewire/datatables/checkbox.blade.php @@ -1,3 +1,9 @@
- + pinnedRecords)) checked @endif + class="w-4 h-4 mt-1 text-blue-600 form-checkbox transition duration-150 ease-in-out" + />
diff --git a/resources/views/livewire/datatables/datatable.blade.php b/resources/views/livewire/datatables/datatable.blade.php index 8c10f349..e683b709 100644 --- a/resources/views/livewire/datatables/datatable.blade.php +++ b/resources/views/livewire/datatables/datatable.blade.php @@ -121,22 +121,7 @@ class="px-3 py-2 text-xs font-medium tracking-wider text-green-500 uppercase bg- @endif @endforeach
- -
- @foreach($this->columns as $index => $column) - @if($column['hidden']) - @if($hideable === 'inline') -
- @endif - @elseif($column['type'] === 'checkbox') - @include('datatables::filters.checkbox') - @endunless - @else - @include('datatables::header-no-hide', ['column' => $column, 'sort' => $sort]) - @endif - @endforeach -
- + @endunless
@foreach($this->columns as $index => $column) @if($column['hidden']) @@ -166,7 +151,6 @@ class="px-3 py-2 text-xs font-medium tracking-wider text-green-500 uppercase bg- @endif @endforeach
- @endif @forelse($this->results as $row)
@foreach($this->columns as $column) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index cbe83fa7..cceb4872 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -151,6 +151,7 @@ public function applyToTable($options) 'activeNumberFilters', 'hide', 'selected', + 'pinnedRecords', ] as $property) { if (isset($options[$property])) { $this->$property = $options[$property]; @@ -178,6 +179,9 @@ public function resetTable() $this->hide = null; $this->resetHiddenColumns(); $this->selected = []; + if (isset($this->pinnedRecords)) { + $this->pinnedRecords = []; + } } /** @@ -245,6 +249,10 @@ public function mount( $this->initialisePerPage(); $this->initialiseColumnGroups(); $this->model = $this->model ?: get_class($this->builder()->getModel()); + + if (isset($this->pinnedRecords)) { + $this->initialisePinnedRecords(); + } } // save settings @@ -1502,6 +1510,9 @@ public function addTimeRangeFilter() public function addSort() { if (isset($this->sort) && isset($this->freshColumns[$this->sort]) && $this->freshColumns[$this->sort]['name']) { + if (isset($this->pinnedRecords) && $this->pinnedRecords) { + $this->query->orderBy(DB::raw('FIELD(id,' . implode(',', $this->pinnedRecords) . ')'), 'DESC'); + } $this->query->orderBy(DB::raw($this->getSortString()), $this->direction ? 'asc' : 'desc'); } @@ -1793,6 +1804,5 @@ public function massActionOptionHandler() } $this->massActionOption = null; - $this->selected = []; } } diff --git a/src/Traits/CanPinRecords.php b/src/Traits/CanPinRecords.php new file mode 100644 index 00000000..f345348b --- /dev/null +++ b/src/Traits/CanPinRecords.php @@ -0,0 +1,61 @@ +label(__('Pin selected Records')) + ->callback(function ($mode, $items) { + $this->pinnedRecords = array_merge($this->pinnedRecords, $items); + $this->selected = $this->pinnedRecords; + + session()->put($this->sessionKey(), $this->pinnedRecords); + }), + + Action::value('unpin') + ->label(__('Unpin selected Records')) + ->callback(function ($mode, $items) { + $this->pinnedRecords = array_diff($this->pinnedRecords, $items); + $this->selected = $this->pinnedRecords; + + session()->put($this->sessionKey(), $this->pinnedRecords); + }), + ]); + } + + protected function initialisePinnedRecords() + { + if (session()->has($this->sessionKey())) { + $this->pinnedRecords = session()->get($this->sessionKey()); + } + + $this->selected = $this->pinnedRecords; + } + + private function sessionKey(): string + { + return $this->sessionStorageKey() . $this->sessionKeyPrefix; + } +} From f2f5a408a233afed319735e3b40a6fd7e8430da1 Mon Sep 17 00:00:00 2001 From: Herbert Maschke Date: Thu, 24 Feb 2022 11:08:31 +0100 Subject: [PATCH 044/113] introduce applyPinnedRecords() --- src/Http/Livewire/LivewireDatatable.php | 4 +--- src/Traits/CanPinRecords.php | 25 +++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index cceb4872..59f9f532 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -179,9 +179,6 @@ public function resetTable() $this->hide = null; $this->resetHiddenColumns(); $this->selected = []; - if (isset($this->pinnedRecords)) { - $this->pinnedRecords = []; - } } /** @@ -1187,6 +1184,7 @@ public function buildDatabaseQuery($export = false) ->addDateRangeFilter() ->addTimeRangeFilter() ->addComplexQuery() + ->applyPinnedRecords() ->addSort(); } diff --git a/src/Traits/CanPinRecords.php b/src/Traits/CanPinRecords.php index f345348b..c695bbe7 100644 --- a/src/Traits/CanPinRecords.php +++ b/src/Traits/CanPinRecords.php @@ -20,7 +20,7 @@ trait CanPinRecords { public array $pinnedRecords = []; - public string $sessionKeyPrefix = '_pinned_records'; + public string $sessionKeyPostfix = '_pinned_records'; public function buildActions() { @@ -45,6 +45,12 @@ public function buildActions() ]); } + public function resetTable() + { + parent::resetTable(); + $this->pinnedRecords = []; + } + protected function initialisePinnedRecords() { if (session()->has($this->sessionKey())) { @@ -54,8 +60,23 @@ protected function initialisePinnedRecords() $this->selected = $this->pinnedRecords; } + /** + * This function should be called after every filter method to ensure pinned records appear + * in every possible filter combination. + * Ensures to have at least _one other_ where applied to the current query build + * to apply this orWhere() on top of that. + */ + protected function applyPinnedRecords(): self + { + if (isset($this->pinnedRecords) && $this->pinnedRecords && $this->query->getQuery()->wheres) { + $this->query->orWhereIn('id', $this->pinnedRecords); + } + + return $this; + } + private function sessionKey(): string { - return $this->sessionStorageKey() . $this->sessionKeyPrefix; + return $this->sessionStorageKey() . $this->sessionKeyPostfix; } } From 6453bf409edb2dcc80b6b95ca08708e9822da601 Mon Sep 17 00:00:00 2001 From: Herbert Maschke Date: Thu, 24 Feb 2022 12:59:14 +0100 Subject: [PATCH 045/113] only call applyPinnedRecord() when given table is using canPinRecords Trait --- src/Http/Livewire/LivewireDatatable.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 59f9f532..59855244 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -1184,8 +1184,11 @@ public function buildDatabaseQuery($export = false) ->addDateRangeFilter() ->addTimeRangeFilter() ->addComplexQuery() - ->applyPinnedRecords() ->addSort(); + + if (isset($this->pinnedRecors)) { + $this->applyPinnedRecords(); + } } public function complexQuery($rules) From f248cfdc1f79853e92a2df7d834e881247d382e5 Mon Sep 17 00:00:00 2001 From: Herbert Maschke Date: Mon, 28 Feb 2022 15:11:29 +0100 Subject: [PATCH 046/113] add hint to index column in README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index b2ed01d2..b73d4cc1 100644 --- a/README.md +++ b/README.md @@ -156,6 +156,9 @@ class ComplexDemoTable extends LivewireDatatable ->hideable() ->filterable($this->planets), + // Column that counts every line from 1 upwards, independent of content + Column::index($this); + DateColumn::name('dob') ->label('DOB') ->group('group2') From 6bdbb2229b50345b29962ce6f449bb134747058e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Romain=20GON=C3=87ALVES?= Date: Wed, 2 Mar 2022 20:16:52 +0100 Subject: [PATCH 047/113] Takes into account hidden columns when displaying summary row --- .../livewire/datatables/datatable.blade.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/resources/views/livewire/datatables/datatable.blade.php b/resources/views/livewire/datatables/datatable.blade.php index c6f242b3..c38fa6b2 100644 --- a/resources/views/livewire/datatables/datatable.blade.php +++ b/resources/views/livewire/datatables/datatable.blade.php @@ -186,13 +186,15 @@ class="flex flex-col items-center h-full px-6 py-5 overflow-hidden text-xs font- @if ($this->hasSummaryRow())
@foreach($this->columns as $column) - @if ($column['summary']) -
- {{ $this->summarize($column['name']) }} -
- @else -
- @endif + @unless($column['hidden']) + @if ($column['summary']) +
+ {{ $this->summarize($column['name']) }} +
+ @else +
+ @endif + @endunless @endforeach
@endif From 58516fdbb4bd1b9cec4374fb6508eb9f7ddbf0a7 Mon Sep 17 00:00:00 2001 From: Alberto Peripolli Date: Wed, 9 Mar 2022 09:21:50 +0100 Subject: [PATCH 048/113] simplify datatable view --- .../livewire/datatables/datatable.blade.php | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/resources/views/livewire/datatables/datatable.blade.php b/resources/views/livewire/datatables/datatable.blade.php index b733b853..938ca590 100644 --- a/resources/views/livewire/datatables/datatable.blade.php +++ b/resources/views/livewire/datatables/datatable.blade.php @@ -1,9 +1,5 @@
- @if($beforeTableSlot) -
- @include($beforeTableSlot) -
- @endif + @includeIf($beforeTableSlot)
@@ -59,9 +55,7 @@ class="px-3 py-2 border border-green-400 rounded-md bg-white text-green-500 text @endforeach - @if($buttonsSlot) - @include($buttonsSlot) - @endif + @includeIf($buttonsSlot)
@@ -202,10 +196,7 @@ class="px-3 py-2 border border-green-400 rounded-md bg-white text-green-500 text
@endif - @if($afterTableSlot) -
- @include($afterTableSlot) -
- @endif + @includeIf($afterTableSlot) +
From 7077daaf6fa0575ed4665246c8adbf4fa99851ff Mon Sep 17 00:00:00 2001 From: Alberto Peripolli Date: Wed, 9 Mar 2022 09:55:15 +0100 Subject: [PATCH 049/113] Update --- .idea/.gitignore | 8 ++++++++ .idea/livewire-datatables.iml | 12 ++++++++++++ .idea/modules.xml | 8 ++++++++ .idea/php.xml | 6 ++++++ .idea/vcs.xml | 6 ++++++ 5 files changed, 40 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/livewire-datatables.iml create mode 100644 .idea/modules.xml create mode 100644 .idea/php.xml create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..13566b81 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/livewire-datatables.iml b/.idea/livewire-datatables.iml new file mode 100644 index 00000000..3bdf18bc --- /dev/null +++ b/.idea/livewire-datatables.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..8ed3b9bb --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml new file mode 100644 index 00000000..ca82a9ee --- /dev/null +++ b/.idea/php.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..94a25f7f --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file From a0f6549cf3d287ff174e81f9821672732d63639c Mon Sep 17 00:00:00 2001 From: Alberto Peripolli Date: Wed, 9 Mar 2022 11:11:51 +0100 Subject: [PATCH 050/113] Merge --- README.md | 85 ++++++- composer.json | 7 +- .../livewire/datatables/datatable.blade.php | 230 +++++++++++------- .../datatables/header-no-hide.blade.php | 8 +- src/Action.php | 89 +++++++ src/Column.php | 31 +++ src/Exports/DatatableExport.php | 61 ++++- src/Http/Livewire/LivewireDatatable.php | 141 ++++++++++- 8 files changed, 537 insertions(+), 115 deletions(-) create mode 100644 src/Action.php diff --git a/README.md b/README.md index 58ccb9ed..b73d4cc1 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ - Filter using booleans, times, dates, selects or free text - Create complex combined filters using the [complex query builder](#complex-query-builder) - Show / hide columns +- Column groups +- Mass Action (Bulk) Support ## [Live Demo App](https://livewire-datatables.com) @@ -18,7 +20,7 @@ ![screenshot](resources/images/screenshot.png "Screenshot") ## Requirements -- [Laravel 7](https://laravel.com/docs/7.x) +- [Laravel 7, 8 or 9](https://laravel.com/docs/9.x) - [Livewire](https://laravel-livewire.com/) - [Tailwind](https://tailwindcss.com/) - [Alpine JS](https://github.com/alpinejs/alpine) @@ -52,7 +54,7 @@ somewhere in your CSS ```html ... - + ... ``` @@ -68,7 +70,9 @@ somewhere in your CSS /> ``` -- *Attention*: Please note that having multiple datatables on the same page _or_ more than one datatable of the same type on different pages needs to have a unique `name` attribute assigned to each one so they do not conflict with each other as in the example above. +*Attention*: Please note that having multiple datatables on the same page _or_ more than one datatable of the same +type on different pages needs to have a unique `name` attribute assigned to each one so they do not conflict with each +other as in the example above. ### Props | Property | Arguments | Result | Example | @@ -152,6 +156,9 @@ class ComplexDemoTable extends LivewireDatatable ->hideable() ->filterable($this->planets), + // Column that counts every line from 1 upwards, independent of content + Column::index($this); + DateColumn::name('dob') ->label('DOB') ->group('group2') @@ -160,7 +167,10 @@ class ComplexDemoTable extends LivewireDatatable (new LabelColumn()) ->label('My custom heading') - ->content('This fixed string appears in every row') + ->content('This fixed string appears in every row'), + + NumberColumn::name('dollars_spent') + ->enableSummary(), ]; } } @@ -240,6 +250,73 @@ public function columns() ->label('Planet'), ``` +### Summary row +If you need to summarize all cells of a specific column, you can use `enableSummary()`: + +```php +public function columns() +{ + return [ + Column::name('dollars_spent') + ->label('Expenses in Dollar') + ->enableSummary(), + + Column::name('euro_spent') + ->label('Expenses in Euro') + ->enableSummary(), +``` + +### Mass (Bulk) Action + +If you want to be able to act upon several records at once, you can use the `buildActions()` method in your Table: + +```php +public function buildActions() + { + return [ + + Action::value('edit')->label('Edit Selected')->group('Default Options')->callback(function ($mode, $items) { + // $items contains an array with the primary keys of the selected items + }), + + Action::value('update')->label('Update Selected')->group('Default Options')->callback(function ($mode, $items) { + // $items contains an array with the primary keys of the selected items + }), + + Action::groupBy('Export Options', function () { + return [ + Action::value('csv')->label('Export CSV')->export('SalesOrders.csv'), + Action::value('html')->label('Export HTML')->export('SalesOrders.html'), + Action::value('xlsx')->label('Export XLSX')->export('SalesOrders.xlsx')->styles($this->exportStyles)->widths($this->exportWidths) + ]; + }), + ]; + } +``` + +### Mass Action Style + +If you only have small style adjustments to the Bulk Action Dropdown you can adjust some settings here: + +```php +public function getExportStylesProperty() + { + return [ + '1' => ['font' => ['bold' => true]], + 'B2' => ['font' => ['italic' => true]], + 'C' => ['font' => ['size' => 16]], + ]; + } + + public function getExportWidthsProperty() + { + return [ + 'A' => 55, + 'B' => 45, + ]; + } +``` + ### Custom column names It is still possible to take full control over your table, you can define a ```builder``` method using whatever query you like, using your own joins, groups whatever, and then name your columns using your normal SQL syntax: diff --git a/composer.json b/composer.json index 963137f4..1ab56fca 100644 --- a/composer.json +++ b/composer.json @@ -17,14 +17,14 @@ ], "require": { "php": "^7.2.5|^8.0", - "illuminate/support": "^7.0|^8.0", + "illuminate/support": "^7.0|^8.0|^9.0", "livewire/livewire": "^2.4.4", "maatwebsite/excel": "^3.1" }, "require-dev": { "laravel/legacy-factories": "^1.0.4", - "orchestra/testbench": "^4.0|5.0|6.0", - "phpunit/phpunit": "^8.0|9.0" + "orchestra/testbench": "^4.0|5.0|6.0|^7.0", + "phpunit/phpunit": "^8.0|9.0|^9.5.10" }, "autoload": { "psr-4": { @@ -39,7 +39,6 @@ "scripts": { "test": "vendor/bin/phpunit", "test-coverage": "vendor/bin/phpunit --coverage-html coverage" - }, "config": { "sort-packages": true diff --git a/resources/views/livewire/datatables/datatable.blade.php b/resources/views/livewire/datatables/datatable.blade.php index 938ca590..953a3a93 100644 --- a/resources/views/livewire/datatables/datatable.blade.php +++ b/resources/views/livewire/datatables/datatable.blade.php @@ -1,24 +1,24 @@
@includeIf($beforeTableSlot)
-
-
+
+
@if($this->searchableColumns()->count()) -
-
-
- - - -
- -
- +
+
+
+ + + +
+ +
+ +
-
@endif
@@ -27,93 +27,119 @@ @endif
- + - @if($this->activeFilters) - - @endif + @if($this->activeFilters) + + @endif - @if($exportable) -
- -
- @endif + @if(count($this->massActionsOptions)) +
+ + + +
+ @endif - @if($hideable === 'select') - @include('datatables::hide-column-multiselect') - @endif + @if($exportable) +
+ +
+ @endif - @foreach ($columnGroups as $name => $group) - - @endforeach + @if($hideable === 'select') + @include('datatables::hide-column-multiselect') + @endif - @includeIf($buttonsSlot) + @foreach ($columnGroups as $name => $group) + + @endforeach
@if($hideable === 'buttons') -
- @foreach($this->columns as $index => $column) - @if ($column['hideable']) - - @endif - @endforeach -
+
+ @foreach($this->columns as $index => $column) + @if ($column['hideable']) + + @endif + @endforeach +
@endif
-
+
@unless($this->hideHeader) -
- @foreach($this->columns as $index => $column) - @if($hideable === 'inline') - @include('datatables::header-inline-hide', ['column' => $column, 'sort' => $sort]) - @elseif($column['type'] === 'checkbox') - @unless($column['hidden']) -
-
- {{ count($selected) }} -
-
- @endunless - @else - @include('datatables::header-no-hide', ['column' => $column, 'sort' => $sort]) - @endif - @endforeach -
- -
- @foreach($this->columns as $index => $column) - @if($column['hidden']) +
+ @foreach($this->columns as $index => $column) @if($hideable === 'inline') -
+ @include('datatables::header-inline-hide', ['column' => $column, 'sort' => $sort]) + @elseif($column['type'] === 'checkbox') + @unless($column['hidden']) +
+
+ {{ count($selected) }} +
+
+ @endunless + @else + @include('datatables::header-no-hide', ['column' => $column, 'sort' => $sort]) @endif - @elseif($column['type'] === 'checkbox') -
-
SELECT ALL
-
- results->total()) checked @endif class="form-checkbox mt-1 h-4 w-4 text-blue-600 transition duration-150 ease-in-out" /> + @endforeach +
+ +
+ @foreach($this->columns as $index => $column) + @if($column['hidden']) + @if($hideable === 'inline') +
+ @endif + @elseif($column['type'] === 'checkbox') +
+
{{ __('SELECT ALL') }}
+
+ results->total()) checked @endif class="w-4 h-4 mt-1 text-blue-600 form-checkbox transition duration-150 ease-in-out" /> +
-
- @elseif($column['type'] === 'label') -
- {{ $column['label'] ?? '' }} -
- @else -
- @isset($column['filterable']) + @elseif($column['type'] === 'label') +
+ {{ $column['label'] ?? '' }} +
+ @else +
+ @isset($column['filterable']) @if( is_iterable($column['filterable']) )
@include('datatables::filters.select', ['index' => $index, 'name' => $column['label'], 'options' => $column['filterable']]) @@ -124,17 +150,17 @@ class="px-3 py-2 border border-green-400 rounded-md bg-white text-green-500 text
@endif @endisset -
- @endif - @endforeach -
+
+ @endif + @endforeach +
@endif @forelse($this->results as $row)
@foreach($this->columns as $column) @if($column['hidden']) @if($hideable === 'inline') -
+
@endif @elseif($column['type'] === 'checkbox') @include('datatables::checkbox', ['value' => $row->checkbox_attribute]) @@ -152,17 +178,33 @@ class="px-3 py-2 border border-green-400 rounded-md bg-white text-green-500 text {{ __("There's Nothing to show at the moment") }}

@endforelse + + @if ($this->hasSummaryRow()) +
+ @foreach($this->columns as $column) + @unless($column['hidden']) + @if ($column['summary']) +
+ {{ $this->summarize($column['name']) }} +
+ @else +
+ @endif + @endunless + @endforeach +
+ @endif
@unless($this->hidePagination) -
+
{{-- check if there is any data --}} @if(count($this->results)) -
- @foreach(config('livewire-datatables.per_page_options', [ 10, 25, 50, 100 ]) as $per_page_option) @endforeach @@ -175,7 +217,7 @@ class="px-3 py-2 border border-green-400 rounded-md bg-white text-green-500 text {{ $this->results->links('datatables::tailwind-simple-pagination') }}
- diff --git a/resources/views/livewire/datatables/header-no-hide.blade.php b/resources/views/livewire/datatables/header-no-hide.blade.php index 36b0cee0..7ea5ea3d 100644 --- a/resources/views/livewire/datatables/header-no-hide.blade.php +++ b/resources/views/livewire/datatables/header-no-hide.blade.php @@ -1,5 +1,7 @@ @unless($column['hidden']) -
+
@if($column['unsortable'])
{{ str_replace('_', ' ', $column['label']) }} @@ -10,9 +12,9 @@ @if($sort === $index) @if($direction) - + @else - + @endif @endif diff --git a/src/Action.php b/src/Action.php new file mode 100644 index 00000000..91d1ff06 --- /dev/null +++ b/src/Action.php @@ -0,0 +1,89 @@ +$method, $args); + } + } + + public static function value($value) + { + $action = new static; + $action->value = $value; + + return $action; + } + + public function label($label) + { + $this->label = $label; + + return $this; + } + + public function group($group) + { + $this->group = $group; + + return $this; + } + + public static function groupBy($group, $actions) + { + if ($actions instanceof \Closure) { + return collect($actions())->each(function ($item) use ($group) { + $item->group = $group; + }); + } + } + + public function export($fileName) + { + $this->fileName = $fileName; + $this->isExport(); + + return $this; + } + + public function isExport($isExport = true) + { + $this->isExport = $isExport; + + return $this; + } + + public function styles($styles) + { + $this->styles = $styles; + + return $this; + } + + public function widths($widths) + { + $this->widths = $widths; + + return $this; + } + + public function callback($callable) + { + $this->callable = $callable; + + return $this; + } +} diff --git a/src/Column.php b/src/Column.php index 1972f94a..defc1d15 100644 --- a/src/Column.php +++ b/src/Column.php @@ -11,6 +11,7 @@ class Column public $type = 'string'; public $index = 0; public $label; + public $tooltip; public $name; public $select; public $joins; @@ -37,6 +38,12 @@ class Column public $maxWidth; public $exportCallback; + /** + * @var bool should the sum of all summarizable cells in this column be + * displayed as a summary at the bottom of the table? + */ + public $summary = false; + /** * @var string (optional) you can group your columns to let the user toggle the visibility of a group at once. */ @@ -129,6 +136,20 @@ public function label($label) return $this; } + public function enableSummary() + { + $this->summary = true; + + return $this; + } + + public function disableSummary() + { + $this->summary = false; + + return $this; + } + public function setIndex($index) { $this->index = $index; @@ -136,6 +157,16 @@ public function setIndex($index) return $this; } + public function tooltip($text, $label = null) + { + $this->tooltip = [ + 'text' => $text, + 'label' => $label, + ]; + + return $this; + } + public function sortBy($column) { $this->sort = $column; diff --git a/src/Exports/DatatableExport.php b/src/Exports/DatatableExport.php index a31179e5..7800e539 100644 --- a/src/Exports/DatatableExport.php +++ b/src/Exports/DatatableExport.php @@ -4,13 +4,21 @@ use Maatwebsite\Excel\Concerns\Exportable; use Maatwebsite\Excel\Concerns\FromCollection; +use Maatwebsite\Excel\Concerns\ShouldAutoSize; +use Maatwebsite\Excel\Concerns\WithColumnWidths; use Maatwebsite\Excel\Concerns\WithHeadings; +use Maatwebsite\Excel\Concerns\WithStyles; +use Maatwebsite\Excel\Facades\Excel; +use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; -class DatatableExport implements FromCollection, WithHeadings +class DatatableExport implements FromCollection, WithHeadings, ShouldAutoSize, WithColumnWidths, WithStyles { use Exportable; public $collection; + public $fileName = 'DatatableExport.xlsx'; + public $styles = []; + public $columnWidths = []; public function __construct($collection) { @@ -26,4 +34,55 @@ public function headings(): array { return array_keys((array) $this->collection->first()); } + + public function setFileName($fileName) + { + $this->fileName = $fileName; + + return $this; + } + + public function getFileName(): string + { + return $this->fileName; + } + + public function setColumnWidths($columnWidths) + { + $this->columnWidths = $columnWidths; + + return $this; + } + + public function getColumnWidths(): array + { + return $this->columnWidths; + } + + public function columnWidths(): array + { + return $this->getColumnWidths(); + } + + public function setStyles($styles) + { + $this->styles = $styles; + + return $this; + } + + public function getStyles(): array + { + return $this->styles; + } + + public function styles(Worksheet $sheet) + { + return $this->getStyles(); + } + + public function download() + { + return Excel::download($this, $this->getFileName()); + } } diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 30412129..b2cdd1a4 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -16,7 +16,6 @@ use Illuminate\View\View; use Livewire\Component; use Livewire\WithPagination; -use Maatwebsite\Excel\Facades\Excel; use Mediconesystems\LivewireDatatables\Column; use Mediconesystems\LivewireDatatables\ColumnSet; use Mediconesystems\LivewireDatatables\Exports\DatatableExport; @@ -73,6 +72,9 @@ class LivewireDatatable extends Component public $tablePrefix = ''; + public $actions; + public $massActionOption; + /** * @var array List your groups and the corresponding label (or translation) here. * The label can be a i18n placeholder like 'app.my_string' and it will be automatically translated via __(). @@ -127,9 +129,7 @@ public function applyToTable($options) if (isset($options['hiddenColumns']) && is_array($options['hiddenColumns'])) { // first display all columns, - foreach ($this->columns as $key => $column) { - $this->columns[$key]['hidden'] = false; - } + $this->resetHiddenColumns(); // then hide all columns that should be hidden: foreach ($options['hiddenColumns'] as $columnToHide) { @@ -167,6 +167,7 @@ public function applyToTable($options) public function resetTable() { $this->perPage = config('livewire-datatables.default_per_page', 10); + $this->sort = $this->defaultSort(); $this->search = null; $this->setPage(1); $this->activeSelectFilters = []; @@ -176,9 +177,21 @@ public function resetTable() $this->activeBooleanFilters = []; $this->activeNumberFilters = []; $this->hide = null; + $this->resetHiddenColumns(); $this->selected = []; } + /** + * Display all columns, also those that are currently hidden. + * Should get called when resetting the table. + */ + public function resetHiddenColumns() + { + foreach ($this->columns as $key => $column) { + $this->columns[$key]['hidden'] = false; + } + } + public function updatedSearch() { $this->setPage(1); @@ -227,7 +240,7 @@ public function mount( $this->params = $params; $this->columns = $this->getViewColumns(); - + $this->actions = $this->getMassActions(); $this->initialiseSearch(); $this->initialiseSort(); $this->initialiseHiddenColumns(); @@ -259,7 +272,9 @@ public function getViewColumns() 'index', 'hidden', 'label', + 'tooltip', 'group', + 'summary', 'content', 'align', 'type', @@ -705,6 +720,34 @@ public function getSortString() } } + /** + * @return bool has the user defined at least one column to display a summary row? + */ + public function hasSummaryRow() + { + foreach ($this->columns as $column) { + if ($column['summary']) { + return true; + } + } + + return false; + } + + /** + * Attempt so summarize each data cell of the given column. + * In case we have a string or any other value that is not summarizable, + * we return a empty string. + */ + public function summarize($column) + { + try { + return $this->results->sum($column); + } catch (\TypeError $e) { + return ''; + } + } + public function updatingPerPage() { $this->refreshLivewireDatatable(); @@ -732,11 +775,14 @@ public function sort($index, $direction = null) if ($direction === null) { // toggle direction $this->direction = ! $this->direction; } else { - $this->direction = $direction === 'desc' ? false : true; + $this->direction = $direction === 'asc' ? true : false; } } else { $this->sort = (int) $index; } + if ($direction !== null) { + $this->direction = $direction === 'asc' ? true : false; + } $this->setPage(1); session()->put([ @@ -1594,7 +1640,14 @@ public function export() { $this->forgetComputed(); - $results = $this->mapCallbacks( + $export = new DatatableExport($this->getExportResultsSet()); + + return $export->download(); + } + + public function getExportResultsSet() + { + return $this->mapCallbacks( $this->getQuery()->when(count($this->selected), function ($query) { return $query->havingRaw('checkbox_attribute IN (' . implode(',', $this->selected) . ')'); })->get(), @@ -1606,8 +1659,6 @@ public function export() return [$value['label'] ?? $value['name'] => $item->{$value['name']}]; })->all(); }); - - return Excel::download(new DatatableExport($results), $this->export_name ? $this->export_name . '.xlsx' : 'DatatableExport.xlsx'); } public function getQuery($export = false) @@ -1656,6 +1707,11 @@ public function getSavedQueries() // Override this method with your own method for getting saved queries } + public function buildActions() + { + // Override this method with your own method for creating mass actions + } + public function rowClasses($row, $loop) { // Override this method with your own method for adding classes to a row @@ -1675,4 +1731,71 @@ public function cellClasses($row, $column) // Override this method with your own method for adding classes to a cell return config('livewire-datatables.default_classes.cell', 'text-sm text-gray-900'); } + + public function getMassActions() + { + return collect($this->massActions)->map(function ($action) { + return collect($action)->only(['group', 'value', 'label'])->toArray(); + })->toArray(); + } + + public function getMassActionsProperty() + { + $actions = collect($this->buildActions())->flatten(); + + $duplicates = $actions->pluck('value')->duplicates(); + + if ($duplicates->count()) { + throw new Exception('Duplicate Mass Action(s): ' . implode(', ', $duplicates->toArray())); + } + + return $actions->toArray(); + } + + public function getMassActionsOptionsProperty() + { + return collect($this->actions)->groupBy(function ($item) { + return $item['group']; + }, true); + } + + public function massActionOptionHandler() + { + if (! $this->massActionOption) { + return; + } + + $option = $this->massActionOption; + + $action = collect($this->massActions)->filter(function ($item) use ($option) { + return $item->value === $option; + })->shift(); + + $collection = collect($action); + + if ($collection->get('isExport')) { + $datatableExport = new DatatableExport($this->getExportResultsSet()); + + $datatableExport->setFileName($collection->get('fileName')); + + $datatableExport->setStyles($collection->get('styles')); + + $datatableExport->setColumnWidths($collection->get('widths')); + + return $datatableExport->download(); + } + + if (! count($this->selected)) { + $this->massActionOption = null; + + return; + } + + if ($collection->has('callable') && is_callable($action->callable)) { + $action->callable($option, $this->selected); + } + + $this->massActionOption = null; + $this->selected = []; + } } From afebbaab7e0980339ea975f96038c5a8aa95d905 Mon Sep 17 00:00:00 2001 From: Alberto Peripolli Date: Wed, 9 Mar 2022 12:40:18 +0100 Subject: [PATCH 051/113] Update readme --- README.md | 35 ++++++++++--------- .../livewire/datatables/datatable.blade.php | 1 + src/Http/Livewire/LivewireDatatable.php | 6 ++-- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index b73d4cc1..da194ad5 100644 --- a/README.md +++ b/README.md @@ -75,23 +75,24 @@ type on different pages needs to have a unique `name` attribute assigned to each other as in the example above. ### Props -| Property | Arguments | Result | Example | -|----|----|----|----| -|**model**|*String* full model name|Define the base model for the table| ```model="App\Post"```| -|**include**|*String\|Array* of column definitions|specify columns to be shown in table, label can be specified by using \| delimter | ```include="name, email, dob\|Birth Date, role"```| -|**exclude**|*String\|Array* of column definitions|columns are excluded from table| ```:exclude="['created_at', 'updated_at']"```| -|**hide**|*String\|Array* of column definitions|columns are present, but start hidden|```hidden="email_verified_at"```| -|**dates**|*String\|Array* of column definitions [ and optional format in \| delimited string]|column values are formatted as per the default date format, or format can be included in string with \| separator | ```:dates="['dob\|lS F y', 'created_at']"```| -|**times**|*String\|Array* of column definitions [optional format in \| delimited string]|column values are formatted as per the default time format, or format can be included in string with \| separator | ```'bedtime\|g:i A'```| -|**searchable**|*String\|Array* of column names | Defines columns to be included in global search | ```searchable="name, email"```| -|**sort**|*String* of column definition [and optional 'asc' or 'desc' (default: 'desc') in \| delimited string]|Specifies the column and direction for initial table sort. Default is column 0 descending | ```sort="name\|asc"```| -|**hide-header**|*Boolean* default: *false*|The top row of the table including the column titles is removed if this is ```true```| | -|**hide-pagination**|*Boolean* default: *false*|Pagination controls are removed if this is ```true```| | -|**per-page**|*Integer* default: 10|Number of rows per page| ```per-page="20"``` | -|**exportable**|*Boolean* default: *false*|Allows table to be exported| `````` | -|**hideable**| _String_ | gives ability to show/hide columns, accepts strings 'inline', 'buttons', or 'select'| `````` | -|**beforeTableSlot**| _String_ |blade view to be included immediately before the table in the component, which can therefore access public properties| | -|**afterTableSlot**| _String_ |blade view to be included immediately after the table in the component, which can therefore access public properties| [demo](https://livewire-datatables.com/complex) | +| Property | Arguments | Result | Example | +|----|----|------------------------------------------------------------------------------------------------------------------------------------------------|----| +|**model**|*String* full model name| Define the base model for the table | ```model="App\Post"```| +|**include**|*String\| Array* of column definitions |specify columns to be shown in table, label can be specified by using \| delimter | ```include="name, email, dob\|Birth Date, role"```| +|**exclude**|*String\| Array* of column definitions |columns are excluded from table| ```:exclude="['created_at', 'updated_at']"```| +|**hide**|*String\| Array* of column definitions |columns are present, but start hidden|```hidden="email_verified_at"```| +|**dates**|*String\| Array* of column definitions [ and optional format in \ | delimited string]|column values are formatted as per the default date format, or format can be included in string with \| separator | ```:dates="['dob\|lS F y', 'created_at']"```| +|**times**|*String\| Array* of column definitions [optional format in \ | delimited string]|column values are formatted as per the default time format, or format can be included in string with \| separator | ```'bedtime\|g:i A'```| +|**searchable**|*String\| Array* of column names | Defines columns to be included in global search | ```searchable="name, email"```| +|**sort**|*String* of column definition [and optional 'asc' or 'desc' (default: 'desc') in \| delimited string] |Specifies the column and direction for initial table sort. Default is column 0 descending | ```sort="name\|asc"```| +|**hide-header**|*Boolean* default: *false*| The top row of the table including the column titles is removed if this is ```true``` | | +|**hide-pagination**|*Boolean* default: *false*| Pagination controls are removed if this is ```true``` | | +|**per-page**|*Integer* default: 10| Number of rows per page | ```per-page="20"``` | +|**exportable**|*Boolean* default: *false*| Allows table to be exported | `````` | +|**hideable**| _String_ | gives ability to show/hide columns, accepts strings 'inline', 'buttons', or 'select' | `````` | +|**buttonsSlot**| _String_ | blade view to be included immediately after the buttons at the top of the table in the component, which can therefore access public properties | | +|**beforeTableSlot**| _String_ | blade view to be included immediately before the table in the component, which can therefore access public properties | | +|**afterTableSlot**| _String_ | blade view to be included immediately after the table in the component, which can therefore access public properties | [demo](https://livewire-datatables.com/complex) | --- diff --git a/resources/views/livewire/datatables/datatable.blade.php b/resources/views/livewire/datatables/datatable.blade.php index 04d1593f..79099350 100644 --- a/resources/views/livewire/datatables/datatable.blade.php +++ b/resources/views/livewire/datatables/datatable.blade.php @@ -80,6 +80,7 @@ class="px-3 py-2 text-xs font-medium tracking-wider text-green-500 uppercase bg- {{ isset($this->groupLabels[$name]) ? __($this->groupLabels[$name]) : __('Toggle :group', ['group' => $name]) }} @endforeach + @includeIf($buttonsSlot)
diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index b2cdd1a4..75de19fb 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -54,8 +54,8 @@ class LivewireDatatable extends Component public $params; public $selected = []; public $beforeTableSlot; - public $afterTableSlot; public $buttonsSlot; + public $afterTableSlot; public $complex; public $complexQuery; public $title; @@ -213,8 +213,8 @@ public function mount( $export_name = null, $hideable = false, $beforeTableSlot = false, - $afterTableSlot = false, $buttonsSlot = false, + $afterTableSlot = false, $params = [] ) { foreach ([ @@ -231,8 +231,8 @@ public function mount( 'exportable', 'hideable', 'beforeTableSlot', - 'afterTableSlot', 'buttonsSlot', + 'afterTableSlot', ] as $property) { $this->$property = $this->$property ?? $$property; } From 5d648ee57ebae3cbe22fa3f6d9880ad2e2e92f8d Mon Sep 17 00:00:00 2001 From: Fabian Mangelsdorf <> Date: Wed, 16 Mar 2022 15:03:05 +0100 Subject: [PATCH 052/113] fix wrong restore sort direction --- src/Http/Livewire/LivewireDatatable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index cbe83fa7..8bf6de88 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -618,8 +618,8 @@ public function initialiseSort() return in_array($column['type'], Column::UNSORTABLE_TYPES) || $column['hidden']; })->keys()->first(); - $this->getSessionStoredSort(); $this->direction = $this->defaultSort() && $this->defaultSort()['direction'] === 'asc'; + $this->getSessionStoredSort(); } public function initialiseHiddenColumns() From a11d8d43514bbb0484f6172335bd2f8489add978 Mon Sep 17 00:00:00 2001 From: Alberto Peripolli Date: Sat, 19 Mar 2022 10:59:58 +0100 Subject: [PATCH 053/113] Move default cell class to config --- config/livewire-datatables.php | 2 +- resources/views/livewire/datatables/datatable.blade.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/livewire-datatables.php b/config/livewire-datatables.php index 0217f55b..90e1fb74 100644 --- a/config/livewire-datatables.php +++ b/config/livewire-datatables.php @@ -72,6 +72,6 @@ 'odd' => 'divide-x divide-gray-100 text-sm text-gray-900 bg-gray-50', 'selected' => 'divide-x divide-gray-100 text-sm text-gray-900 bg-yellow-100', ], - 'cell' => 'text-sm text-gray-900', + 'cell' => 'whitespace-no-wrap text-sm text-gray-900 px-6 py-2', ], ]; diff --git a/resources/views/livewire/datatables/datatable.blade.php b/resources/views/livewire/datatables/datatable.blade.php index 4a753f1a..4e355409 100644 --- a/resources/views/livewire/datatables/datatable.blade.php +++ b/resources/views/livewire/datatables/datatable.blade.php @@ -177,7 +177,7 @@ class="px-3 py-2 text-xs font-medium tracking-wider text-green-500 uppercase bg- @foreach($this->columns as $column) @unless($column['hidden']) @if ($column['summary']) -
+
{{ $this->summarize($column['name']) }}
@else From 7d71a26d382d93501a29b03246fd72d736e53113 Mon Sep 17 00:00:00 2001 From: Alberto Peripolli Date: Sat, 19 Mar 2022 11:09:43 +0100 Subject: [PATCH 054/113] Move cell classes on config --- resources/views/livewire/datatables/datatable.blade.php | 2 +- resources/views/livewire/datatables/label.blade.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/views/livewire/datatables/datatable.blade.php b/resources/views/livewire/datatables/datatable.blade.php index 4e355409..5e19200d 100644 --- a/resources/views/livewire/datatables/datatable.blade.php +++ b/resources/views/livewire/datatables/datatable.blade.php @@ -160,7 +160,7 @@ class="px-3 py-2 text-xs font-medium tracking-wider text-green-500 uppercase bg- @elseif($column['type'] === 'label') @include('datatables::label') @else -
+
{!! $row->{$column['name']} !!}
@endif diff --git a/resources/views/livewire/datatables/label.blade.php b/resources/views/livewire/datatables/label.blade.php index 7bed4d3b..2f943a6d 100644 --- a/resources/views/livewire/datatables/label.blade.php +++ b/resources/views/livewire/datatables/label.blade.php @@ -1,3 +1,3 @@ -
+
{!! $column['content'] ?? '' !!}
From 735e34a22d4b80df8a9b642fc59da30197ab1cd1 Mon Sep 17 00:00:00 2001 From: Alberto Peripolli Date: Sat, 19 Mar 2022 12:23:05 +0100 Subject: [PATCH 055/113] Fix no result text --- .../livewire/datatables/datatable.blade.php | 103 +++++++++--------- 1 file changed, 52 insertions(+), 51 deletions(-) diff --git a/resources/views/livewire/datatables/datatable.blade.php b/resources/views/livewire/datatables/datatable.blade.php index 5e19200d..fc1f8b65 100644 --- a/resources/views/livewire/datatables/datatable.blade.php +++ b/resources/views/livewire/datatables/datatable.blade.php @@ -29,58 +29,58 @@
- @if($this->activeFilters) - - @endif + @if($this->activeFilters) + + @endif - @if(count($this->massActionsOptions)) -
- - + + @foreach($this->massActionsOptions as $group => $items) + @if(!$group) + @foreach($items as $item) + + @endforeach + @else + @foreach($items as $item) @endforeach - @else - - @foreach($items as $item) - - @endforeach - - @endif - @endforeach - - -
- @endif + + @endif + @endforeach + + +
+ @endif - @if($exportable) -
window.open(link, '_blank')) } }" x-init="init"> -
- @endif +
+ @endif - @if($hideable === 'select') - @include('datatables::hide-column-multiselect') - @endif + @if($hideable === 'select') + @include('datatables::hide-column-multiselect') + @endif - @foreach ($columnGroups as $name => $group) - - @endforeach - @includeIf($buttonsSlot) + @foreach ($columnGroups as $name => $group) + + @endforeach + @includeIf($buttonsSlot)
@@ -144,11 +144,11 @@ class="px-3 py-2 text-xs font-medium tracking-wider text-green-500 uppercase bg-
@endif @endisset -
- @endif - @endforeach -
- @forelse($this->results as $row) +
+ @endif + @endforeach +
+ @foreach($this->results as $row)
@foreach($this->columns as $column) @if($column['hidden']) @@ -166,11 +166,7 @@ class="px-3 py-2 text-xs font-medium tracking-wider text-green-500 uppercase bg- @endif @endforeach
- @empty -

- {{ __("There's Nothing to show at the moment") }} -

- @endforelse + @endforeach @if ($this->hasSummaryRow())
@@ -189,6 +185,11 @@ class="px-3 py-2 text-xs font-medium tracking-wider text-green-500 uppercase bg- @endif
+ @if($this->results->isEmpty()) +

+ {{ __("There's Nothing to show at the moment") }} +

+ @endif
@unless($this->hidePagination) From f56d767c8be5cc24ede395e8ea4955a24ae2ade3 Mon Sep 17 00:00:00 2001 From: Herbert Maschke Date: Tue, 22 Mar 2022 13:09:14 +0100 Subject: [PATCH 056/113] introduce the ability to pass a custom name for a callback column --- src/Column.php | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/Column.php b/src/Column.php index defc1d15..8a676aa4 100644 --- a/src/Column.php +++ b/src/Column.php @@ -2,6 +2,7 @@ namespace Mediconesystems\LivewireDatatables; +use Closure; use Illuminate\Support\Facades\DB; use Illuminate\Support\Str; use Mediconesystems\LivewireDatatables\Http\Livewire\LivewireDatatable; @@ -92,11 +93,25 @@ public static function raw($raw) return $column; } - public static function callback($columns, $callback, $params = []) - { + /** + * Make a callback function. + * + * @param $columns string The (comma separated) columns that should be retrieved from the database. + * Is being translated directly into the `.sql`. + * @param $callback Closure A callback that defines how the retrieved columns are processed. + * @param $params Array Optional additional parameters that are passed to the given Closure. + * @param $callbackName string Optional string that defines the 'name' of the column. + * Leave empty to let livewire autogenerate a distinct value. + */ + public static function callback( + string $columns, + Closure $callback, + array $params = [], + ?string $callbackName = null + ) { $column = new static; - $column->name = 'callback_' . crc32(json_encode(func_get_args())); + $column->name = 'callback_' . ($callbackName ?? crc32(json_encode(func_get_args()))); $column->callback = $callback; $column->additionalSelects = is_array($columns) ? $columns : array_map('trim', explode(',', $columns)); $column->params = $params; From 76c20bfac049b5398a96692aee7d3bc74ed29b90 Mon Sep 17 00:00:00 2001 From: Herbert Maschke Date: Tue, 22 Mar 2022 14:29:38 +0100 Subject: [PATCH 057/113] sortable overhaul: rename 'unsortable' to 'sortable' which is more intuitive --- CHANGELOG.md | 7 ++- config/livewire-datatables.php | 10 ++++ .../datatables/header-inline-hide.blade.php | 48 +++++++++---------- .../datatables/header-no-hide.blade.php | 38 +++++++-------- src/Column.php | 16 ++++++- src/Http/Livewire/LivewireDatatable.php | 2 +- tests/ColumnTest.php | 6 +-- 7 files changed, 77 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f65bc51a..7e0db005 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to `livewire-datatables` will be documented in this file -## 1.0.0 - 201X-XX-XX +## 1.0.0 - 201X-XX-XX ( to be released in the future... ) - initial release + +## 0.9.0 ( 2022-03-22 ) + +- Breaking Change: 'unsortable' has been renamed to 'sortable', which is more intuitive. Please adjust your overwritten +- views, if any (thyseus). diff --git a/config/livewire-datatables.php b/config/livewire-datatables.php index 0217f55b..5b89ef74 100644 --- a/config/livewire-datatables.php +++ b/config/livewire-datatables.php @@ -56,6 +56,16 @@ 'model_namespace' => 'App', + /* + |-------------------------------------------------------------------------- + | Default Sortable + |-------------------------------------------------------------------------- + | Should a column of a datatable be sortable by default ? + | + */ + + 'default_sortable' => true, + /* |-------------------------------------------------------------------------- | Default CSS classes diff --git a/resources/views/livewire/datatables/header-inline-hide.blade.php b/resources/views/livewire/datatables/header-inline-hide.blade.php index 59cf268c..528b33a1 100644 --- a/resources/views/livewire/datatables/header-inline-hide.blade.php +++ b/resources/views/livewire/datatables/header-inline-hide.blade.php @@ -1,43 +1,43 @@ @endif diff --git a/src/Column.php b/src/Column.php index defc1d15..14c53dd4 100644 --- a/src/Column.php +++ b/src/Column.php @@ -18,11 +18,11 @@ class Column public $base; public $raw; public $searchable; + public $sortable; public $filterOn; public $filterable; public $hideable; public $sort; - public $unsortable; public $defaultSort; public $callback; public $hidden; @@ -52,6 +52,11 @@ class Column /** @var array list all column types that are not sortable by SQL here */ public const UNSORTABLE_TYPES = ['label', 'checkbox']; + public function __construct() + { + $this->sortable = config('livewire-datatables.default_sortable', true); + } + public static function name($name) { $column = new static; @@ -188,9 +193,16 @@ public function hideable() return $this; } + public function sortable() + { + $this->sortable = true; + + return $this; + } + public function unsortable() { - $this->unsortable = true; + $this->sortable = false; return $this; } diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 0d200792..9b246f58 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -282,6 +282,7 @@ public function getViewColumns() 'type', 'filterable', 'hideable', + 'sortable', 'complex', 'filterView', 'name', @@ -289,7 +290,6 @@ public function getViewColumns() 'width', 'minWidth', 'maxWidth', - 'unsortable', 'preventExport', ])->toArray(); })->toArray(); diff --git a/tests/ColumnTest.php b/tests/ColumnTest.php index c779ac4c..a417a91a 100644 --- a/tests/ColumnTest.php +++ b/tests/ColumnTest.php @@ -81,6 +81,7 @@ public function it_returns_an_array_from_column() 'sort' => null, 'defaultSort' => null, 'searchable' => null, + 'sortable' => null, 'params' => [], 'additionalSelects' => [], 'scope' => null, @@ -92,7 +93,6 @@ public function it_returns_an_array_from_column() 'align' => 'left', 'preventExport' => null, 'width' => null, - 'unsortable' => null, 'exportCallback' => function () { }, 'filterOn' => null, @@ -126,6 +126,7 @@ public function it_returns_an_array_from_raw() 'sort' => 'SELECT column FROM table', 'defaultSort' => 'asc', 'searchable' => null, + 'sortable' => null, 'params' => [], 'additionalSelects' => [], 'scope' => null, @@ -136,7 +137,6 @@ public function it_returns_an_array_from_raw() 'align' => 'left', 'preventExport' => null, 'width' => null, - 'unsortable' => null, 'exportCallback' => null, 'filterOn' => null, 'group' => null, @@ -167,6 +167,7 @@ public function it_returns_width_property_from_column() 'sort' => null, 'defaultSort' => null, 'searchable' => null, + 'sortable' => null, 'params' => [], 'additionalSelects' => [], 'scope' => null, @@ -178,7 +179,6 @@ public function it_returns_width_property_from_column() 'align' => 'left', 'preventExport' => null, 'width' => '1em', - 'unsortable' => null, 'exportCallback' => null, 'filterOn' => null, 'group' => null, From 6a71b4907bbc0bd3ed5b229f7b5beaf7ac661dce Mon Sep 17 00:00:00 2001 From: Herbert Maschke Date: Tue, 22 Mar 2022 14:50:11 +0100 Subject: [PATCH 058/113] introduce wrap() and nowrap() in Column --- README.md | 1 + .../livewire/datatables/datatable.blade.php | 4 ++-- src/Column.php | 19 +++++++++++++++++++ src/Http/Livewire/LivewireDatatable.php | 1 + 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f969adc4..b1c435e9 100644 --- a/README.md +++ b/README.md @@ -198,6 +198,7 @@ class ComplexDemoTable extends LivewireDatatable |**searchable**| |Includes the column in the global search|```Column::name('name')->searchable()```| |**hideable**| |The user is able to toggle the visibility of this column|```Column::name('name')->hideable()```| |**filterable**|[*Array* $options], [*String* $filterScope]|Adds a filter to the column, according to Column type. If an array of options is passed it wil be used to populate a select input. If the column is a scope column then the name of the filter scope must also be passed|```Column::name('allegiance')->filterable(['Rebellion', 'Empire'])```| +|**unwrap**| | Prevents the content of the column from being wrapped in multiple lines |```Column::name('oneliner')->unwrap()```| |**filterOn**|*String/Array* $statement|Allows you to specify a column name or sql statement upon which to perform the filter (must use SQL syntax, not Eloquent eg. ```'users.name'``` instead of ```'user.name'```). Useful if using a callback to modify the displayed values. Can pass a single string or array of strings which will be combined with ```OR```|```Column::callback(['name', 'allegiance'], function ($name, $allegiance) { return "$name is allied to $allegiance"; })->filterable(['Rebellion', 'Empire'])->filterOn('users.allegiance')```| |**view**|*String* $viewName| Passes the column value, whole row of values, and any additional parameters to a view template | _(see below)_| |**editable**| | Marks the column as editable | _(see below)_| diff --git a/resources/views/livewire/datatables/datatable.blade.php b/resources/views/livewire/datatables/datatable.blade.php index 611a2333..74cafa36 100644 --- a/resources/views/livewire/datatables/datatable.blade.php +++ b/resources/views/livewire/datatables/datatable.blade.php @@ -156,14 +156,14 @@ class="px-3 py-2 text-xs font-medium tracking-wider text-green-500 uppercase bg- @foreach($this->columns as $column) @if($column['hidden']) @if($hideable === 'inline') -
+
@endif @elseif($column['type'] === 'checkbox') @include('datatables::checkbox', ['value' => $row->checkbox_attribute]) @elseif($column['type'] === 'label') @include('datatables::label') @else -
+
{!! $row->{$column['name']} !!}
@endif diff --git a/src/Column.php b/src/Column.php index defc1d15..a343b8de 100644 --- a/src/Column.php +++ b/src/Column.php @@ -44,6 +44,11 @@ class Column */ public $summary = false; + /** + * @var bool allow the content of the column to wrap into multiple lines. + */ + public $wrappable = true; + /** * @var string (optional) you can group your columns to let the user toggle the visibility of a group at once. */ @@ -136,6 +141,20 @@ public function label($label) return $this; } + public function wrap() + { + $this->wrappable = true; + + return $this; + } + + public function unwrap() + { + $this->wrappable = false; + + return $this; + } + public function enableSummary() { $this->summary = true; diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 0d200792..8b435d8a 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -286,6 +286,7 @@ public function getViewColumns() 'filterView', 'name', 'params', + 'wrappable', 'width', 'minWidth', 'maxWidth', From 74400d9fd2a6f8a9bb71429a4131a263f404c99a Mon Sep 17 00:00:00 2001 From: Herbert Maschke Date: Tue, 22 Mar 2022 15:28:06 +0100 Subject: [PATCH 059/113] introduce alias (make:livewire-datatable, livewire:datatable) --- src/LivewireDatatablesServiceProvider.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/LivewireDatatablesServiceProvider.php b/src/LivewireDatatablesServiceProvider.php index 8fcaa3f2..12071741 100644 --- a/src/LivewireDatatablesServiceProvider.php +++ b/src/LivewireDatatablesServiceProvider.php @@ -11,6 +11,7 @@ use Illuminate\Support\ServiceProvider; use Illuminate\Support\Str; use Livewire\Livewire; +use Mediconesystems\LivewireDatatables\Commands\DatatableMakeCommand; use Mediconesystems\LivewireDatatables\Commands\MakeDatatableCommand; use Mediconesystems\LivewireDatatables\Http\Controllers\FileExportController; use Mediconesystems\LivewireDatatables\Http\Livewire\ComplexQuery; @@ -48,7 +49,7 @@ public function boot() __DIR__ . '/../resources/views/icons' => resource_path('views/livewire/datatables/icons'), ], 'views'); - $this->commands([MakeDatatableCommand::class]); + $this->commands([MakeDatatableCommand::class, DatatableMakeCommand::class]); } Route::get('/datatables/{filename}', [FileExportController::class, 'handle']) From 29a10771dc231c955524eb8caaf27ea17d5c5411 Mon Sep 17 00:00:00 2001 From: Herbert Maschke Date: Tue, 22 Mar 2022 15:36:44 +0100 Subject: [PATCH 060/113] code style --- src/NumberColumn.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NumberColumn.php b/src/NumberColumn.php index 49c5bccf..05b4e5e2 100644 --- a/src/NumberColumn.php +++ b/src/NumberColumn.php @@ -18,7 +18,7 @@ public function round($places = 0) return $this; } - + public function format($places = 0) { $this->callback = function ($value) use ($places) { From 5c05fd602ccdd991738fdfd3f4cf25a1ee9a4995 Mon Sep 17 00:00:00 2001 From: Herbert Maschke Date: Tue, 22 Mar 2022 15:44:12 +0100 Subject: [PATCH 061/113] add alias to make:livewire-datatable command --- src/Commands/DatatableMakeCommand.php | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/Commands/DatatableMakeCommand.php diff --git a/src/Commands/DatatableMakeCommand.php b/src/Commands/DatatableMakeCommand.php new file mode 100644 index 00000000..2e6c6bdf --- /dev/null +++ b/src/Commands/DatatableMakeCommand.php @@ -0,0 +1,8 @@ + Date: Tue, 22 Mar 2022 15:45:08 +0100 Subject: [PATCH 062/113] append truncate after whitespace-nowrap --- resources/views/livewire/datatables/datatable.blade.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/views/livewire/datatables/datatable.blade.php b/resources/views/livewire/datatables/datatable.blade.php index 74cafa36..e7f47ffa 100644 --- a/resources/views/livewire/datatables/datatable.blade.php +++ b/resources/views/livewire/datatables/datatable.blade.php @@ -156,14 +156,14 @@ class="px-3 py-2 text-xs font-medium tracking-wider text-green-500 uppercase bg- @foreach($this->columns as $column) @if($column['hidden']) @if($hideable === 'inline') -
+
@endif @elseif($column['type'] === 'checkbox') @include('datatables::checkbox', ['value' => $row->checkbox_attribute]) @elseif($column['type'] === 'label') @include('datatables::label') @else -
+
{!! $row->{$column['name']} !!}
@endif From 9b2d07ac53a640d5762c2af98f2b0de06a797fb4 Mon Sep 17 00:00:00 2001 From: Alberto Peripolli Date: Tue, 22 Mar 2022 15:56:58 +0100 Subject: [PATCH 063/113] Remove .idea --- .gitignore | 1 + .idea/.gitignore | 8 -------- .idea/livewire-datatables.iml | 12 ------------ .idea/modules.xml | 8 -------- .idea/php.xml | 6 ------ .idea/vcs.xml | 6 ------ 6 files changed, 1 insertion(+), 40 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/livewire-datatables.iml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/php.xml delete mode 100644 .idea/vcs.xml diff --git a/.gitignore b/.gitignore index 00e71ec7..a9c0e146 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ vendor coverage .DS_Store .vscode/ +.idea/ diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b81..00000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/livewire-datatables.iml b/.idea/livewire-datatables.iml deleted file mode 100644 index 3bdf18bc..00000000 --- a/.idea/livewire-datatables.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 8ed3b9bb..00000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml deleted file mode 100644 index ca82a9ee..00000000 --- a/.idea/php.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7f..00000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 3dc9809b58b0936fcd1698aabb3968748d964944 Mon Sep 17 00:00:00 2001 From: Herbert Maschke Date: Tue, 22 Mar 2022 16:01:04 +0100 Subject: [PATCH 064/113] introduce headerAlign...() and contentAlign...() --- CHANGELOG.md | 4 +- README.md | 8 +++- .../livewire/datatables/datatable.blade.php | 4 +- .../datatables/header-inline-hide.blade.php | 4 +- .../datatables/header-no-hide.blade.php | 4 +- .../views/livewire/datatables/label.blade.php | 2 +- src/Column.php | 39 +++++++++++++++++-- src/Http/Livewire/LivewireDatatable.php | 3 +- src/NumberColumn.php | 7 ++-- tests/ColumnTest.php | 10 ++--- 10 files changed, 61 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e0db005..a5e189b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,5 +8,5 @@ All notable changes to `livewire-datatables` will be documented in this file ## 0.9.0 ( 2022-03-22 ) -- Breaking Change: 'unsortable' has been renamed to 'sortable', which is more intuitive. Please adjust your overwritten -- views, if any (thyseus). +- Breaking Change: 'unsortable' has been renamed to 'sortable', which is more intuitive. Please adjust your overwritten views, if any (thyseus). + diff --git a/README.md b/README.md index a3c2b3b3..8de0d16c 100644 --- a/README.md +++ b/README.md @@ -202,8 +202,12 @@ class ComplexDemoTable extends LivewireDatatable |**filterOn**|*String/Array* $statement|Allows you to specify a column name or sql statement upon which to perform the filter (must use SQL syntax, not Eloquent eg. ```'users.name'``` instead of ```'user.name'```). Useful if using a callback to modify the displayed values. Can pass a single string or array of strings which will be combined with ```OR```|```Column::callback(['name', 'allegiance'], function ($name, $allegiance) { return "$name is allied to $allegiance"; })->filterable(['Rebellion', 'Empire'])->filterOn('users.allegiance')```| |**view**|*String* $viewName| Passes the column value, whole row of values, and any additional parameters to a view template | _(see below)_| |**editable**| | Marks the column as editable | _(see below)_| -|**alignCenter**| | Center-aligns column header and contents |```Column::delete()->alignCenter()```| -|**alignRight**| | Right-aligns column header and contents |```Column::delete()->alignRight()```| +|**alignCenter**| | Center-aligns column header _and_ contents |```Column::delete()->alignCenter()```| +|**alignRight**| | Right-aligns column header _and_ contents |```Column::delete()->alignRight()```| +|**contentAlignCenter**| | Center-aligns column contents |```Column::delete()->contentAlignCenter()```| +|**contentAlignRight**| | Right-aligns column contents |```Column::delete()->contentAlignRight()```| +|**headerAlignCenter**| | Center-aligns column header |```Column::delete()->headerAlignCenter()```| +|**headerAlignRight**| | Right-aligns column header |```Column::delete()->headerAlignRight()```| |**editable**| | Marks the column as editable | _(see below)_| |**exportCallback**| Closure $callback | Reformats the result when exporting | _(see below)_ | |**excludeFromExport**| | Excludes the column from export |```Column::name('email')->excludeFromExport()```| diff --git a/resources/views/livewire/datatables/datatable.blade.php b/resources/views/livewire/datatables/datatable.blade.php index e7f47ffa..04bfe9bd 100644 --- a/resources/views/livewire/datatables/datatable.blade.php +++ b/resources/views/livewire/datatables/datatable.blade.php @@ -163,7 +163,7 @@ class="px-3 py-2 text-xs font-medium tracking-wider text-green-500 uppercase bg- @elseif($column['type'] === 'label') @include('datatables::label') @else -
+
{!! $row->{$column['name']} !!}
@endif @@ -180,7 +180,7 @@ class="px-3 py-2 text-xs font-medium tracking-wider text-green-500 uppercase bg- @foreach($this->columns as $column) @unless($column['hidden']) @if ($column['summary']) -
+
{{ $this->summarize($column['name']) }}
@else diff --git a/resources/views/livewire/datatables/header-inline-hide.blade.php b/resources/views/livewire/datatables/header-inline-hide.blade.php index 528b33a1..041d71d0 100644 --- a/resources/views/livewire/datatables/header-inline-hide.blade.php +++ b/resources/views/livewire/datatables/header-inline-hide.blade.php @@ -18,7 +18,7 @@ class="w-32 hidden group-hover:inline-block absolute z-10 top-0 left-0 ml-3 bg-b @if($column['sortable']) @else
- {{ str_replace('_', ' ', $column['label']) }} + {{ str_replace('_', ' ', $column['label']) }}
@endif diff --git a/resources/views/livewire/datatables/header-no-hide.blade.php b/resources/views/livewire/datatables/header-no-hide.blade.php index da7d9fbd..3c8d6955 100644 --- a/resources/views/livewire/datatables/header-no-hide.blade.php +++ b/resources/views/livewire/datatables/header-no-hide.blade.php @@ -3,7 +3,7 @@ @if (isset($column['tooltip']['text'])) title="{{ $column['tooltip']['text'] }}" @endif class="relative table-cell h-12 overflow-hidden align-top" @include('datatables::style-width')> @if($column['sortable']) - @else -
+
{{ str_replace('_', ' ', $column['label']) }}
@endif diff --git a/resources/views/livewire/datatables/label.blade.php b/resources/views/livewire/datatables/label.blade.php index 7bed4d3b..91f0c956 100644 --- a/resources/views/livewire/datatables/label.blade.php +++ b/resources/views/livewire/datatables/label.blade.php @@ -1,3 +1,3 @@ -
+
{!! $column['content'] ?? '' !!}
diff --git a/src/Column.php b/src/Column.php index 6915c5f4..c4e73b35 100644 --- a/src/Column.php +++ b/src/Column.php @@ -32,7 +32,8 @@ class Column public $params = []; public $additionalSelects = []; public $filterView; - public $align = 'left'; + public $headerAlign = 'left'; + public $contentAlign = 'left'; public $preventExport; public $width; public $minWidth; @@ -399,14 +400,44 @@ public function hide() public function alignRight() { - $this->align = 'right'; + $this->headerAlign = 'right'; + $this->contentAlign = 'right'; return $this; } - public function alignCenter() + public function alignCener() { - $this->align = 'center'; + $this->headerAlign = 'center'; + $this->contentAlign = 'center'; + + return $this; + } + + public function headerAlignRight() + { + $this->headerAlign = 'right'; + + return $this; + } + + public function contentAlignRight() + { + $this->contentAlign = 'right'; + + return $this; + } + + public function headerAlignCenter() + { + $this->headerAlign = 'center'; + + return $this; + } + + public function contentAlignCenter() + { + $this->contentAlign = 'center'; return $this; } diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 7e370125..c75ab5d5 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -278,7 +278,8 @@ public function getViewColumns() 'group', 'summary', 'content', - 'align', + 'headerAlign', + 'contentAlign', 'type', 'filterable', 'hideable', diff --git a/src/NumberColumn.php b/src/NumberColumn.php index 05b4e5e2..7dbeb558 100644 --- a/src/NumberColumn.php +++ b/src/NumberColumn.php @@ -5,10 +5,11 @@ class NumberColumn extends Column { public $type = 'number'; - public $align = 'right'; + public $headerAlign = 'right'; + public $contentAlign = 'right'; public $round; - public function round($places = 0) + public function round($places = 0): self { $this->round = $places; @@ -19,7 +20,7 @@ public function round($places = 0) return $this; } - public function format($places = 0) + public function format(int $places = 0): self { $this->callback = function ($value) use ($places) { return number_format($value, $places, '.', ','); diff --git a/tests/ColumnTest.php b/tests/ColumnTest.php index a417a91a..20b772b6 100644 --- a/tests/ColumnTest.php +++ b/tests/ColumnTest.php @@ -90,7 +90,7 @@ public function it_returns_an_array_from_column() 'select' => null, 'joins' => null, 'aggregate' => 'group_concat', - 'align' => 'left', + 'headerAlign' => 'left', 'preventExport' => null, 'width' => null, 'exportCallback' => function () { @@ -134,7 +134,7 @@ public function it_returns_an_array_from_raw() 'filterView' => null, 'select' => DB::raw('SELECT column FROM table'), 'joins' => null, - 'align' => 'left', + 'headerAlign' => 'left', 'preventExport' => null, 'width' => null, 'exportCallback' => null, @@ -176,7 +176,7 @@ public function it_returns_width_property_from_column() 'select' => null, 'joins' => null, 'aggregate' => 'group_concat', - 'align' => 'left', + 'headerAlign' => 'left', 'preventExport' => null, 'width' => '1em', 'exportCallback' => null, @@ -216,7 +216,7 @@ public function check_invalid_width_unit_not_returning_value() 'select' => null, 'joins' => null, 'aggregate' => 'group_concat', - 'align' => 'left', + 'headerAlign' => 'left', 'preventExport' => null, 'width' => null, ], $subject); @@ -249,7 +249,7 @@ public function check_adding_px_to_numeric_width_input() 'select' => null, 'joins' => null, 'aggregate' => 'group_concat', - 'align' => 'left', + 'headerAlign' => 'left', 'preventExport' => null, 'width' => '5px', ], $subject); From 28d1b9ff9e8baf8b83552029ed3c137aa4e9859c Mon Sep 17 00:00:00 2001 From: Herbert Maschke Date: Tue, 22 Mar 2022 16:27:49 +0100 Subject: [PATCH 065/113] s/alignCener/alignCenter --- src/Column.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Column.php b/src/Column.php index c4e73b35..fee50cfb 100644 --- a/src/Column.php +++ b/src/Column.php @@ -406,7 +406,7 @@ public function alignRight() return $this; } - public function alignCener() + public function alignCenter() { $this->headerAlign = 'center'; $this->contentAlign = 'center'; From 92c663cebcaa2160fda373b4d76043817147e435 Mon Sep 17 00:00:00 2001 From: levi730 Date: Tue, 22 Mar 2022 20:56:44 -0500 Subject: [PATCH 066/113] Change to allow use of checkbox without 'CanPinRecords' --- resources/views/livewire/datatables/checkbox.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/livewire/datatables/checkbox.blade.php b/resources/views/livewire/datatables/checkbox.blade.php index 35617c37..078c4773 100644 --- a/resources/views/livewire/datatables/checkbox.blade.php +++ b/resources/views/livewire/datatables/checkbox.blade.php @@ -3,7 +3,7 @@ type="checkbox" wire:model="selected" value="{{ $value }}" - @if (in_array($value, $this->pinnedRecords)) checked @endif + @if (property_exists($this, 'pinnedRecords') && in_array($value, $this->pinnedRecords)) checked @endif class="w-4 h-4 mt-1 text-blue-600 form-checkbox transition duration-150 ease-in-out" />
From 258ed099beea44fec6b5bd84c6a054a36a914402 Mon Sep 17 00:00:00 2001 From: Barna <62602458+code23-barna@users.noreply.github.com> Date: Wed, 23 Mar 2022 18:47:36 +0100 Subject: [PATCH 067/113] Fix issue: Column::callback(): Argument #1 ($columns) must be of type string, array given --- src/Column.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Column.php b/src/Column.php index fee50cfb..7b074731 100644 --- a/src/Column.php +++ b/src/Column.php @@ -107,15 +107,15 @@ public static function raw($raw) /** * Make a callback function. * - * @param $columns string The (comma separated) columns that should be retrieved from the database. - * Is being translated directly into the `.sql`. - * @param $callback Closure A callback that defines how the retrieved columns are processed. - * @param $params Array Optional additional parameters that are passed to the given Closure. - * @param $callbackName string Optional string that defines the 'name' of the column. - * Leave empty to let livewire autogenerate a distinct value. + * @param $columns Array|string The (comma separated) columns that should be retrieved from the database. + * Is being translated directly into the `.sql`. + * @param $callback Closure A callback that defines how the retrieved columns are processed. + * @param $params Array Optional additional parameters that are passed to the given Closure. + * @param $callbackName string Optional string that defines the 'name' of the column. + * Leave empty to let livewire autogenerate a distinct value. */ public static function callback( - string $columns, + array|string $columns, Closure $callback, array $params = [], ?string $callbackName = null From 8ed023d3770a901e1f4874ab33479e69cda98c3b Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Wed, 23 Mar 2022 19:47:35 +0000 Subject: [PATCH 068/113] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 8a2b19bb..608c6261 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "illuminate/support": "^7.0|^8.0", "livewire/livewire": "^2.4.4", "maatwebsite/excel": "^3.1", - "reedware/laravel-relation-joins": "^2.4" + "reedware/laravel-relation-joins": "^2.4|^3.0" }, "require-dev": { "laravel/legacy-factories": "^1.0.4", From f22720ccff6711125c5405d3327c7135201ce9e3 Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Wed, 23 Mar 2022 19:48:23 +0000 Subject: [PATCH 069/113] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 608c6261..463a5ff8 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": "^7.2.5|^8.0", - "illuminate/support": "^7.0|^8.0", + "illuminate/support": "^7.0|^8.0|^9.0", "livewire/livewire": "^2.4.4", "maatwebsite/excel": "^3.1", "reedware/laravel-relation-joins": "^2.4|^3.0" From c79adb13eceff338fae6ad70bc76670a672359b9 Mon Sep 17 00:00:00 2001 From: Barna <62602458+code23-barna@users.noreply.github.com> Date: Thu, 24 Mar 2022 09:49:54 +0100 Subject: [PATCH 070/113] Argument #2 ($callback) must be of type Closure, string given --- src/Column.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Column.php b/src/Column.php index 7b074731..7361990b 100644 --- a/src/Column.php +++ b/src/Column.php @@ -109,14 +109,14 @@ public static function raw($raw) * * @param $columns Array|string The (comma separated) columns that should be retrieved from the database. * Is being translated directly into the `.sql`. - * @param $callback Closure A callback that defines how the retrieved columns are processed. + * @param $callback Closure|string A callback that defines how the retrieved columns are processed. * @param $params Array Optional additional parameters that are passed to the given Closure. * @param $callbackName string Optional string that defines the 'name' of the column. * Leave empty to let livewire autogenerate a distinct value. */ public static function callback( array|string $columns, - Closure $callback, + Closure|string $callback, array $params = [], ?string $callbackName = null ) { From 8571d8751d7193a9a7ed6102a5cf87194a7e565f Mon Sep 17 00:00:00 2001 From: Fabian Mangelsdorf <> Date: Mon, 28 Mar 2022 15:50:53 +0200 Subject: [PATCH 071/113] move view columns array to class property --- src/Http/Livewire/LivewireDatatable.php | 52 +++++++++++++------------ 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index e0609b2a..a85ee2bf 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -115,6 +115,31 @@ class LivewireDatatable extends Component 'does not include' => '<>', ]; + protected $viewColumns = [ + 'index', + 'hidden', + 'label', + 'tooltip', + 'group', + 'summary', + 'content', + 'headerAlign', + 'contentAlign', + 'type', + 'filterable', + 'hideable', + 'sortable', + 'complex', + 'filterView', + 'name', + 'params', + 'wrappable', + 'width', + 'minWidth', + 'maxWidth', + 'preventExport', + ]; + /** * This events allows to control the options of the datatable from foreign livewire components * by using $emit. @@ -273,30 +298,9 @@ public function columns() public function getViewColumns() { return collect($this->freshColumns)->map(function ($column) { - return collect($column)->only([ - 'index', - 'hidden', - 'label', - 'tooltip', - 'group', - 'summary', - 'content', - 'headerAlign', - 'contentAlign', - 'type', - 'filterable', - 'hideable', - 'sortable', - 'complex', - 'filterView', - 'name', - 'params', - 'wrappable', - 'width', - 'minWidth', - 'maxWidth', - 'preventExport', - ])->toArray(); + return collect($column) + ->only($this->viewColumns) + ->toArray(); })->toArray(); } From d885039b222c804d8d1fd4d9f1a0d4c84aeefa05 Mon Sep 17 00:00:00 2001 From: Shane Burrell Date: Tue, 29 Mar 2022 16:53:28 -0400 Subject: [PATCH 072/113] The backtick on the sort string doesn't seem to work on sqlserv and doesn't seem to be required for postgres. --- src/Http/Livewire/LivewireDatatable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index a85ee2bf..0ea0279f 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -726,7 +726,7 @@ public function getSortString() default: return $dbTable == 'pgsql' || $dbTable == 'sqlsrv' ? new Expression('"' . $column['name'] . '"') - : new Expression('`' . $column['name'] . '`'); + : new Expression("'" . $column['name'] . "'"); break; } } From b8bc51e67088961e46590ae9f28665651a1a5102 Mon Sep 17 00:00:00 2001 From: Renardo Date: Thu, 31 Mar 2022 10:46:49 +0200 Subject: [PATCH 073/113] Bugfix toggleSelectAll() with groupBy() in query builder If we use groupBy() in the queryBuilder, the count() method in toggleSelectAll() return a wrong value. As a result, the toggleSelectAll() doesn't work as expected selecting all checkboxes works, but deselecting all of them does not). This is an already known issue : https://github.com/laravel/framework/issues/22883 and I use the approach https://github.com/laravel/framework/issues/22883#issuecomment-438521002 to fix the bug --- src/Http/Livewire/LivewireDatatable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index a85ee2bf..949d40c4 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -1696,7 +1696,7 @@ public function checkboxQuery() public function toggleSelectAll() { - if (count($this->selected) === $this->getQuery()->count()) { + if (count($this->selected) === $this->getQuery()->getCountForPagination()) { $this->selected = []; } else { $this->selected = $this->checkboxQuery()->values()->toArray(); From 20aa884d4422500da9f14b1bca279113f47aa216 Mon Sep 17 00:00:00 2001 From: Jevinson Lim Date: Thu, 31 Mar 2022 17:00:29 +0800 Subject: [PATCH 074/113] Fix typos in README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4f344097..6a68bb26 100644 --- a/README.md +++ b/README.md @@ -470,7 +470,7 @@ Just add ```$complex = true``` to your Datatable Class and all filterable column ![image](https://user-images.githubusercontent.com/7000886/128855344-25035758-ca90-42d2-bd19-518c9de45148.png) --- -**Persisting Queries** (Requires AlpineJS v3 with $perist plugin) +**Persisting Queries** (Requires AlpineJS v3 with $persist plugin) - Add ```$persistComplexQuery = true``` to your class and queries will be stored in browser localstorage. - By default the localstorage key will be the class name. You can provide your own by setting the public property ```$persistKey``` or overriding ```getPersistKeyProperty()``` on the Datatable Class - eg: for user-specific persistence: @@ -484,7 +484,7 @@ public function getPersistKeyProperty() --- **Saving Queries** -If you want to save permanently save queries you must provide 3 methods for adding, deleting and retrieving your saved queries using whatever logic you like: +If you want to permanently save queries you must provide 3 methods for adding, deleting and retrieving your saved queries using whatever logic you like: - ```public function saveQuery(String $name, Array $rules)``` - ```public function deleteQuery(Int $id)``` @@ -528,7 +528,7 @@ class ComplexQuery extends BaseModel /* Datatable Class */ -class TableWithSaving extends Livewire Datatable +class TableWithSaving extends LivewireDatatable { ... From 1b9b5211846b0fd89ad30375313a66959fcf0ca3 Mon Sep 17 00:00:00 2001 From: Herbert Maschke Date: Mon, 4 Apr 2022 14:59:48 +0200 Subject: [PATCH 075/113] improve the way to-be-exported columns are discovered --- src/Http/Livewire/LivewireDatatable.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index a85ee2bf..fdab6243 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -1670,10 +1670,10 @@ public function getExportResultsSet() })->get(), true )->map(function ($item) { - return collect($this->columns)->reject(function ($value, $key) { - return $value['preventExport'] == true || $value['hidden'] == true; + return collect($this->columns())->reject(function ($value, $key) { + return $value->preventExport == true || $value->hidden == true; })->mapWithKeys(function ($value, $key) use ($item) { - return [$value['label'] ?? $value['name'] => $item->{$value['name']}]; + return [$value->label ?? $value->name => $item->{$value->name}]; })->all(); }); } From db88019ab3e2797252b4867a25af619c0a0f5143 Mon Sep 17 00:00:00 2001 From: Herbert Maschke Date: Mon, 4 Apr 2022 15:10:47 +0200 Subject: [PATCH 076/113] introduce ability to provide custom file name of exported file --- src/Http/Livewire/LivewireDatatable.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index fdab6243..7cbb9bb6 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -49,7 +49,6 @@ class LivewireDatatable extends Component public $times; public $searchable; public $exportable; - public $export_name; public $hideable; public $params; public $selected = []; @@ -236,7 +235,6 @@ public function mount( $hidePagination = null, $perPage = null, $exportable = false, - $export_name = null, $hideable = false, $beforeTableSlot = false, $buttonsSlot = false, @@ -1653,11 +1651,12 @@ public function render() return view('datatables::datatable')->layoutData(['title' => $this->title]); } - public function export() + public function export(string $filename = 'DatatableExport.xlsx') { $this->forgetComputed(); $export = new DatatableExport($this->getExportResultsSet()); + $export->setFilename($filename); return $export->download(); } From ad757f50a0738a6696a9ad0a2aa89bc7bdd307d0 Mon Sep 17 00:00:00 2001 From: David Grosse Date: Mon, 9 May 2022 15:11:13 +0200 Subject: [PATCH 077/113] Column: introduce ability to pass custom data when using view() --- src/Column.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Column.php b/src/Column.php index 7361990b..58389957 100644 --- a/src/Column.php +++ b/src/Column.php @@ -350,10 +350,10 @@ public function round($precision = 0) return $this; } - public function view($view) + public function view($view, $data = []) { - $this->callback = function ($value, $row) use ($view) { - return view($view, ['value' => $value, 'row' => $row]); + $this->callback = function ($value, $row) use ($view, $data) { + return view($view, ['value' => $value, 'row' => $row, ...$data]); }; $this->exportCallback = function ($value) { From 230de7a95dabc288740e52f7a5225b761a6a4726 Mon Sep 17 00:00:00 2001 From: Shane Burrell Date: Wed, 11 May 2022 06:14:28 -0400 Subject: [PATCH 078/113] Add support to set default date filter start and end to support other Date/DateTime types. (#450) * Add support to set start and end date filter default. This is required to be changed to work with smalldatetime in MS SQL Server. * Added proper support for database type detection for sort order. Co-authored-by: Mark Salmon Co-authored-by: Herbert Maschke Co-authored-by: Shane Burrell --- composer.json | 15 +- config/livewire-datatables.php | 17 ++ src/Column.php | 2 +- src/Http/Livewire/LivewireDatatable.php | 167 ++++++-------------- tests/LivewireDatatableQueryBuilderTest.php | 10 +- tests/TestCase.php | 2 + 6 files changed, 81 insertions(+), 132 deletions(-) diff --git a/composer.json b/composer.json index 1ab56fca..cb2d4030 100644 --- a/composer.json +++ b/composer.json @@ -1,9 +1,8 @@ { - "name": "mediconesystems/livewire-datatables", - "description": "", + "name": "shaneburrell/livewire-datatables", + "description": "Fork of the livewire-datatables package. This has several modifications and additions.", "keywords": [ - "mediconesystems", - "livewire-datatables" + "livewire-datatables-fork" ], "homepage": "https://github.com/mediconesystems/livewire-datatables", "license": "MIT", @@ -13,13 +12,19 @@ "name": "Mark Salmon", "email": "mark.salmon@mediconesystems.com", "role": "Developer" + }, + { + "name": "Shane Burrell", + "email": "shane@shaneburrell.com", + "role": "Developer" } ], "require": { "php": "^7.2.5|^8.0", "illuminate/support": "^7.0|^8.0|^9.0", "livewire/livewire": "^2.4.4", - "maatwebsite/excel": "^3.1" + "maatwebsite/excel": "^3.1", + "reedware/laravel-relation-joins": "^2.4|^3.0" }, "require-dev": { "laravel/legacy-factories": "^1.0.4", diff --git a/config/livewire-datatables.php b/config/livewire-datatables.php index 84b072a2..10a028a4 100644 --- a/config/livewire-datatables.php +++ b/config/livewire-datatables.php @@ -14,6 +14,23 @@ 'default_time_format' => 'H:i', 'default_date_format' => 'd/m/Y', + /* + |-------------------------------------------------------------------------- + | Default Carbon Formats + |-------------------------------------------------------------------------- + | The default formats that are used for TimeColumn & DateColumn. + | You can use the formatting characters from the PHP DateTime class. + | More info: https://www.php.net/manual/en/datetime.format.php + | + */ + + 'default_time_start' => '0000-00-00', + 'default_time_end' => '9999-12-31', + + // Defaults that work with smalldatetime in SQL Server + // 'default_time_start' => '1900-01-01', + // 'default_time_end' => '2079-06-06', + /* |-------------------------------------------------------------------------- | Surpress Search Highlights diff --git a/src/Column.php b/src/Column.php index 58389957..0c2d51a7 100644 --- a/src/Column.php +++ b/src/Column.php @@ -461,7 +461,7 @@ public function aggregate() public function isBaseColumn() { - return ! Str::contains($this->name, '.') && ! $this->raw; + return ! Str::startsWith($this->name, 'callback_') && ! Str::contains($this->name, '.') && ! $this->raw; } public function field() diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index b6f9ef7d..22ce08b8 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -3,12 +3,9 @@ namespace Mediconesystems\LivewireDatatables\Http\Livewire; use Exception; -use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasManyThrough; -use Illuminate\Database\Eloquent\Relations\HasOne; -use Illuminate\Database\Eloquent\Relations\HasOneThrough; use Illuminate\Database\Query\Expression; use Illuminate\Support\Arr; use Illuminate\Support\Facades\DB; @@ -343,37 +340,19 @@ public function getProcessedColumnsProperty() ->sort($this->sort); } - public function resolveColumnName($column) - { - return $column->isBaseColumn() - ? $this->query->getModel()->getTable() . '.' . ($column->base ?? Str::before($column->name, ':')) - : $column->select ?? $this->resolveRelationColumn($column->base ?? $column->name, $column->aggregate); - } - - public function resolveCheckboxColumnName($column) - { - $column = is_object($column) - ? $column->toArray() - : $column; - - return Str::contains($column['base'], '.') - ? $this->resolveRelationColumn($column['base'], $column['aggregate']) - : $this->query->getModel()->getTable() . '.' . $column['base']; - } - public function resolveAdditionalSelects($column) { $selects = collect($column->additionalSelects)->map(function ($select) use ($column) { return Str::contains($select, '.') - ? $this->resolveRelationColumn($select, Str::contains($select, ':') ? Str::after($select, ':') : null, $column->name) + ? $this->resolveColumnName($column, $select) : $this->query->getModel()->getTable() . '.' . $select; }); return $selects->count() > 1 ? new Expression("CONCAT_WS('" . static::SEPARATOR . "' ," . - collect($selects)->map(function ($select) { - return "COALESCE($select, '')"; - })->join(', ') . ')') + collect($selects)->map(function ($select) { + return "COALESCE($select, '')"; + })->join(', ') . ')') : $selects->first(); } @@ -381,7 +360,7 @@ public function resolveEditableColumnName($column) { return [ $column->select, - $this->query->getModel()->getTable() . '.' . $this->query->getModel()->getKeyName(), + $this->query->getModel()->getTable() . '.' . $this->query->getModel()->getKeyName() . ' AS ' . $column->name . '_edit_id', ]; } @@ -395,12 +374,6 @@ public function getSelectStatements($withAlias = false, $export = false) return $column; } - if ($column->isType('checkbox')) { - $column->select = $this->resolveCheckboxColumnName($column); - - return $column; - } - if (Str::startsWith($column->name, 'callback_')) { $column->select = $this->resolveAdditionalSelects($column); @@ -442,94 +415,41 @@ public function getSelectStatements($withAlias = false, $export = false) }); } - protected function resolveRelationColumn($name, $aggregate = null, $alias = null) - { - $parts = explode('.', Str::before($name, ':')); - $columnName = array_pop($parts); - $relation = implode('.', $parts); - - return method_exists($this->query->getModel(), $parts[0]) - ? $this->joinRelation($relation, $columnName, $aggregate, $alias ?? $name) - : $name; - } - - protected function joinRelation($relation, $relationColumn, $aggregate = null, $alias = null) + protected function resolveColumnName($column, $additional = null) { - $table = ''; - $model = ''; - $lastQuery = $this->query; - foreach (explode('.', $relation) as $eachRelation) { - $model = $lastQuery->getRelation($eachRelation); + if ($column->isBaseColumn()) { + return $this->query->getModel()->getTable() . '.' . ($column->base ?? Str::before($column->name, ':')); + } - switch (true) { - case $model instanceof HasOne: - $table = $model->getRelated()->getTable(); - $foreign = $model->getQualifiedForeignKeyName(); - $other = $model->getQualifiedParentKeyName(); - break; + $relations = explode('.', Str::before(($additional ?: $column->name), ':')); + $aggregate = Str::after(($additional ?: $column->name), ':'); - case $model instanceof HasMany: - $this->query->customWithAggregate($relation, $aggregate ?? 'count', $relationColumn, $alias); - $table = null; - break; + if (! method_exists($this->query->getModel(), $relations[0])) { + return $additional ?: $column->name; + } - case $model instanceof BelongsTo: - $table = $model->getRelated()->getTable(); - $foreign = $model->getQualifiedForeignKeyName(); - $other = $model->getQualifiedOwnerKeyName(); - break; + $columnName = array_pop($relations); + $aggregateName = implode('.', $relations); - case $model instanceof BelongsToMany: - $this->query->customWithAggregate($relation, $aggregate ?? 'count', $relationColumn, $alias); - $table = null; - break; + $relatedQuery = $this->query; - case $model instanceof HasOneThrough: - $pivot = explode('.', $model->getQualifiedParentKeyName())[0]; - $pivotPK = $model->getQualifiedFirstKeyName(); - $pivotFK = $model->getQualifiedLocalKeyName(); - $this->performJoin($pivot, $pivotPK, $pivotFK); + while (count($relations) > 0) { + $relation = array_shift($relations); - $related = $model->getRelated(); - $table = $related->getTable(); - $tablePK = $related->getForeignKey(); - $foreign = $pivot . '.' . $tablePK; - $other = $related->getQualifiedKeyName(); + $useThrough = collect($this->query->getQuery()->joins) + ->pluck('table') + ->contains($relatedQuery->getRelation($relation)->getRelated()->getTable()); - break; + if ($relatedQuery->getRelation($relation) instanceof HasMany || $relatedQuery->getRelation($relation) instanceof HasManyThrough || $relatedQuery->getRelation($relation) instanceof BelongsToMany) { + $this->query->customWithAggregate($aggregateName, $column->aggregate ?? 'count', $columnName, $column->name); - default: - $this->query->customWithAggregate($relation, $aggregate ?? 'count', $relationColumn, $alias); + return null; } - if ($table) { - $this->performJoin($table, $foreign, $other); - } - $lastQuery = $model->getQuery(); - } - if ($model instanceof HasOne || $model instanceof BelongsTo || $model instanceof HasOneThrough) { - return $table . '.' . $relationColumn; + $relatedQuery = $this->query->joinRelation($relation, null, 'left', $useThrough, $relatedQuery); } - if ($model instanceof HasMany) { - return; - } - - if ($model instanceof BelongsToMany) { - return; - } - } - - protected function performJoin($table, $foreign, $other, $type = 'left') - { - $joins = []; - foreach ((array) $this->query->getQuery()->joins as $key => $join) { - $joins[] = $join->table; - } - - if (! in_array($table, $joins)) { - $this->query->join($table, $foreign, '=', $other, $type); - } + return $relatedQuery->getQuery()->from . '.' . $columnName; } public function getFreshColumnsProperty() @@ -699,10 +619,9 @@ public function defaultSort() ] : null; } - public function getSortString() + public function getSortString($dbtable) { $column = $this->freshColumns[$this->sort]; - $dbTable = DB::connection()->getPDO()->getAttribute(\PDO::ATTR_DRIVER_NAME); switch (true) { case $column['sort']: @@ -722,10 +641,20 @@ public function getSortString() break; default: - return $dbTable == 'pgsql' || $dbTable == 'sqlsrv' - ? new Expression('"' . $column['name'] . '"') - : new Expression("'" . $column['name'] . "'"); - break; + + switch ($dbtable) { + case 'mysql': + return new Expression('`' . $column['name'] . '`'); + break; + case 'pgsql': + return new Expression('"' . $column['name'] . '"'); + break; + case 'sqlsrv': + return new Expression("'" . $column['name'] . "'"); + break; + default: + return new Expression("'" . $column['name'] . "'"); + } } } @@ -1475,8 +1404,8 @@ public function addDateRangeFilter() break; } $query->whereBetween($this->getColumnFilterStatement($index)[0], [ - isset($filter['start']) && $filter['start'] != '' ? $filter['start'] : '0000-00-00', - isset($filter['end']) && $filter['end'] != '' ? $filter['end'] : '9999-12-31', + isset($filter['start']) && $filter['start'] != '' ? $filter['start'] : config('livewire-datatables.default_time_start', '0000-00-00'), + isset($filter['end']) && $filter['end'] != '' ? $filter['end'] : config('livewire-datatables.default_time_end', '9999-12-31'), ]); } }); @@ -1521,7 +1450,7 @@ public function addSort() if (isset($this->pinnedRecords) && $this->pinnedRecords) { $this->query->orderBy(DB::raw('FIELD(id,' . implode(',', $this->pinnedRecords) . ')'), 'DESC'); } - $this->query->orderBy(DB::raw($this->getSortString()), $this->direction ? 'asc' : 'desc'); + $this->query->orderBy(DB::raw($this->getSortString($this->query->getConnection()->getPDO()->getAttribute(\PDO::ATTR_DRIVER_NAME))), $this->direction ? 'asc' : 'desc'); } return $this; @@ -1565,10 +1494,8 @@ public function mapCallbacks($paginatedCollection, $export = false) 'value' => $value, 'key' => $this->builder()->getModel()->getQualifiedKeyName(), 'column' => Str::after($name, '.'), - 'rowId' => $row->{$this->builder()->getModel()->getTable() . '.' . $this->builder()->getModel()->getKeyName()} ?? $row->{$this->builder()->getModel()->getKeyName()}, + 'rowId' => $row->{$name . '_edit_id'}, ]); - } elseif ($export && isset($this->export_callbacks[$name])) { - $row->$name = $this->export_callbacks[$name]($value, $row); } elseif (isset($this->callbacks[$name]) && is_string($this->callbacks[$name])) { $row->$name = $this->{$this->callbacks[$name]}($value, $row); } elseif (Str::startsWith($name, 'callback_')) { @@ -1686,8 +1613,6 @@ public function getQuery($export = false) public function checkboxQuery() { - $this->resolveCheckboxColumnName(collect($this->freshColumns)->firstWhere('type', 'checkbox')); - return $this->query->reorder()->get()->map(function ($row) { return (string) $row->checkbox_attribute; }); diff --git a/tests/LivewireDatatableQueryBuilderTest.php b/tests/LivewireDatatableQueryBuilderTest.php index 6d839891..725b5c6f 100644 --- a/tests/LivewireDatatableQueryBuilderTest.php +++ b/tests/LivewireDatatableQueryBuilderTest.php @@ -123,17 +123,17 @@ public function it_creates_a_query_builder_for_belongs_to_relation_columns() $subject = new LivewireDatatable(1); $subject->mount(DummyHasManyModel::class, ['id', 'dummy_model.name']); - $this->assertEquals('select "dummy_has_many_models"."id" as "id", "dummy_models"."name" as "dummy_model.name" from "dummy_has_many_models" left join "dummy_models" on "dummy_has_many_models"."dummy_model_id" = "dummy_models"."id" order by `id` desc', $subject->getQuery()->toSql()); + $this->assertEquals('select "dummy_has_many_models"."id" as "id", "dummy_models"."name" as "dummy_model.name" from "dummy_has_many_models" left join "dummy_models" on "dummy_models"."id" = "dummy_has_many_models"."dummy_model_id" order by `id` desc', $subject->getQuery()->toSql()); $subject->sort(1); $subject->forgetComputed(); - $this->assertEquals('select "dummy_has_many_models"."id" as "id", "dummy_models"."name" as "dummy_model.name" from "dummy_has_many_models" left join "dummy_models" on "dummy_has_many_models"."dummy_model_id" = "dummy_models"."id" order by dummy_models.name desc', $subject->getQuery()->toSql()); + $this->assertEquals('select "dummy_has_many_models"."id" as "id", "dummy_models"."name" as "dummy_model.name" from "dummy_has_many_models" left join "dummy_models" on "dummy_models"."id" = "dummy_has_many_models"."dummy_model_id" order by dummy_models.name desc', $subject->getQuery()->toSql()); $subject->sort(1); $subject->forgetComputed(); - $this->assertEquals('select "dummy_has_many_models"."id" as "id", "dummy_models"."name" as "dummy_model.name" from "dummy_has_many_models" left join "dummy_models" on "dummy_has_many_models"."dummy_model_id" = "dummy_models"."id" order by dummy_models.name asc', $subject->getQuery()->toSql()); + $this->assertEquals('select "dummy_has_many_models"."id" as "id", "dummy_models"."name" as "dummy_model.name" from "dummy_has_many_models" left join "dummy_models" on "dummy_models"."id" = "dummy_has_many_models"."dummy_model_id" order by dummy_models.name asc', $subject->getQuery()->toSql()); } /** @test */ @@ -148,7 +148,7 @@ public function it_creates_a_where_query_for_belongs_to_relation_columns() // $subject->doNumberFilterEnd(1, 456); $subject->forgetComputed(); - $this->assertEquals('select "dummy_has_many_models"."id" as "id", "dummy_models"."name" as "dummy_model.name" from "dummy_has_many_models" left join "dummy_models" on "dummy_has_many_models"."dummy_model_id" = "dummy_models"."id" where (dummy_models.name >= ?) order by dummy_has_many_models.id desc', $subject->getQuery()->toSql()); + $this->assertEquals('select "dummy_has_many_models"."id" as "id", "dummy_models"."name" as "dummy_model.name" from "dummy_has_many_models" left join "dummy_models" on "dummy_models"."id" = "dummy_has_many_models"."dummy_model_id" where (dummy_models.name >= ?) order by dummy_has_many_models.id desc', $subject->getQuery()->toSql()); $this->assertEquals([123], $subject->getQuery()->getBindings()); @@ -163,7 +163,7 @@ public function it_creates_a_where_query_for_belongs_to_relation_columns() $subject->doNumberFilterEnd(1, null); $subject->forgetComputed(); - $this->assertEquals('select "dummy_has_many_models"."id" as "id", "dummy_models"."name" as "dummy_model.name" from "dummy_has_many_models" left join "dummy_models" on "dummy_has_many_models"."dummy_model_id" = "dummy_models"."id" order by dummy_has_many_models.id desc', $subject->getQuery()->toSql()); + $this->assertEquals('select "dummy_has_many_models"."id" as "id", "dummy_models"."name" as "dummy_model.name" from "dummy_has_many_models" left join "dummy_models" on "dummy_models"."id" = "dummy_has_many_models"."dummy_model_id" order by dummy_has_many_models.id desc', $subject->getQuery()->toSql()); $this->assertEquals([], $subject->getQuery()->getBindings()); } diff --git a/tests/TestCase.php b/tests/TestCase.php index 7f834513..9680e5d3 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -8,6 +8,7 @@ use Maatwebsite\Excel\ExcelServiceProvider; use Mediconesystems\LivewireDatatables\LivewireDatatablesServiceProvider; use Orchestra\Testbench\TestCase as Orchestra; +use Reedware\LaravelRelationJoins\LaravelRelationJoinServiceProvider; class TestCase extends Orchestra { @@ -26,6 +27,7 @@ protected function getPackageProviders($app) LivewireServiceProvider::class, LivewireDatatablesServiceProvider::class, ExcelServiceProvider::class, + LaravelRelationJoinServiceProvider::class, ]; } From d3559c08217294df648f42fd1d818ba28c2df9bd Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Wed, 18 May 2022 11:34:04 +0100 Subject: [PATCH 079/113] fix boolean complex query --- src/Http/Livewire/LivewireDatatable.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 7dd0faff..0bb0feb8 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -1111,9 +1111,9 @@ public function processNested($rules = null, $query = null, $logic = 'and') $query->whereNotNull($column); } elseif ($this->columns[$rule['content']['column']]['type'] === 'boolean') { if ($rule['content']['value'] === 'true') { - $query->whereNotNull(Str::contains($column, '(') ? DB::raw($column) : $column); + $query->where(Str::contains($column, '(') ? DB::raw($column) : $column, 1); } else { - $query->whereNull(Str::contains($column, '(') ? DB::raw($column) : $column); + $query->where(Str::contains($column, '(') ? DB::raw($column) : $column, '<>', 1); } } else { $col = (isset($this->freshColumns[$rule['content']['column']]['round']) && $this->freshColumns[$rule['content']['column']]['round'] !== null) From e8de7c886864002148ef0ebf10de9ed0001d2b71 Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Wed, 18 May 2022 12:08:30 +0100 Subject: [PATCH 080/113] try again --- src/Http/Livewire/LivewireDatatable.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index cb579c68..8b4a9eaa 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -395,7 +395,7 @@ protected function resolveColumnName($column, $additional = null) $aggregate = Str::after(($additional ?: $column->name), ':'); if (! method_exists($this->query->getModel(), $relations[0])) { - return $additional ?: $column->name; + return ($additional ?: $column->name); } $columnName = array_pop($relations); @@ -1111,9 +1111,15 @@ public function processNested($rules = null, $query = null, $logic = 'and') $query->whereNotNull($column); } elseif ($this->columns[$rule['content']['column']]['type'] === 'boolean') { if ($rule['content']['value'] === 'true') { - $query->where(Str::contains($column, '(') ? DB::raw($column) : $column, 1); + $query->where(function ($query) use ($column) { + $query->whereNotNull(Str::contains($column, '(') ? DB::raw($column) : $column) + ->where($column, '<>', 0); + }); } else { - $query->where(Str::contains($column, '(') ? DB::raw($column) : $column, '<>', 1); + $query->where(function ($query) use ($column) { + $query->whereNull(Str::contains($column, '(') ? DB::raw($column) : $column) + ->orWhere(Str::contains($column, '(') ? DB::raw($column) : $column, 0); + }); } } else { $col = (isset($this->freshColumns[$rule['content']['column']]['round']) && $this->freshColumns[$rule['content']['column']]['round'] !== null) From c502ce9762e944bb1a7e50f9a7257c23f548fca3 Mon Sep 17 00:00:00 2001 From: Amilkar Dominguez Date: Sun, 29 May 2022 01:59:58 -0400 Subject: [PATCH 081/113] Update README.md (#466) Add fix to possible error when install on laravel 9 composer require psr/simple-cache:^1.0 maatwebsite/excel --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 6a68bb26..d6329bf9 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,11 @@ You can install the package via composer: ```bash composer require mediconesystems/livewire-datatables ``` +If you use laravel 9 first execute +```bash +composer require psr/simple-cache:^1.0 maatwebsite/excel +``` + ### Optional You don't need to, but if you like you can publish the config file and blade template assets: ```bash From 5fbaeb4683f0ca261e0652c7f8620fd3dfb394ef Mon Sep 17 00:00:00 2001 From: Amro Khaled <9614340+amrography@users.noreply.github.com> Date: Sun, 29 May 2022 08:00:17 +0200 Subject: [PATCH 082/113] php 8 is required (#465) --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index cb2d4030..c09a9c3e 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ } ], "require": { - "php": "^7.2.5|^8.0", + "php": "^8.0", "illuminate/support": "^7.0|^8.0|^9.0", "livewire/livewire": "^2.4.4", "maatwebsite/excel": "^3.1", From c2931fb62f0e7d482dc73976fd87f3c0eb44905b Mon Sep 17 00:00:00 2001 From: bleuren Date: Thu, 16 Jun 2022 19:36:38 +0800 Subject: [PATCH 083/113] fix: tablePrefix problem (#473) --- src/Http/Livewire/LivewireDatatable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 22ce08b8..14165a41 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -351,7 +351,7 @@ public function resolveAdditionalSelects($column) return $selects->count() > 1 ? new Expression("CONCAT_WS('" . static::SEPARATOR . "' ," . collect($selects)->map(function ($select) { - return "COALESCE($select, '')"; + return 'COALESCE(' . $this->tablePrefix . $select . ', \'\')'; })->join(', ') . ')') : $selects->first(); } From 232b2b1f125516bd57f63dd954dd6e1c1b5cbeb5 Mon Sep 17 00:00:00 2001 From: sha-hin Date: Wed, 6 Jul 2022 13:40:13 +0200 Subject: [PATCH 084/113] Add column argument to 'fieldEdited' event. (#479) * Added column to 'fieldEdited' event * Added column check to 'fieldEdited' event * Change alpine key to wire key --- resources/views/livewire/datatables/editable.blade.php | 6 +++--- src/Traits/WithCallbacks.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/views/livewire/datatables/editable.blade.php b/resources/views/livewire/datatables/editable.blade.php index 925cc050..3faad52d 100644 --- a/resources/views/livewire/datatables/editable.blade.php +++ b/resources/views/livewire/datatables/editable.blade.php @@ -2,8 +2,8 @@ edit: false, edited: false, init() { - window.livewire.on('fieldEdited', (id) => { - if (id === '{{ $rowId }}') { + window.livewire.on('fieldEdited', (id, column) => { + if (id === '{{ $rowId }}' && column === '{{ $column }}') { this.edited = true setTimeout(() => { this.edited = false @@ -11,7 +11,7 @@ } }) } -}" x-init="init()" :key="{{ $rowId }}"> +}" x-init="init()" wire:key="{{ $rowId }}_{{ $column }}"> diff --git a/src/Traits/WithCallbacks.php b/src/Traits/WithCallbacks.php index 5a72d64a..540fa8f9 100644 --- a/src/Traits/WithCallbacks.php +++ b/src/Traits/WithCallbacks.php @@ -13,6 +13,6 @@ public function edited($value, $key, $column, $rowId) ->where(Str::after($key, '.'), $rowId) ->update([$column => $value]); - $this->emit('fieldEdited', $rowId); + $this->emit('fieldEdited', $rowId, $column); } } From 08644005ce95870ebf4b18a046c36e68655b851b Mon Sep 17 00:00:00 2001 From: Josef Behr Date: Mon, 25 Jul 2022 12:57:58 +0200 Subject: [PATCH 085/113] add single quotes around id in call to delete function so it works with values containing eg. "-" (#486) --- resources/views/livewire/datatables/delete.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/livewire/datatables/delete.blade.php b/resources/views/livewire/datatables/delete.blade.php index 56aff510..55fd679c 100644 --- a/resources/views/livewire/datatables/delete.blade.php +++ b/resources/views/livewire/datatables/delete.blade.php @@ -44,7 +44,7 @@ class="text-gray-400 hover:text-gray-500 focus:outline-none focus:text-gray-500 - From 89721347d0cc24b4596ef04c109e429f94c4319f Mon Sep 17 00:00:00 2001 From: Rick Dubiel Date: Fri, 14 Oct 2022 01:30:58 -0500 Subject: [PATCH 086/113] Adds hideable functionality to column select (#508) --- .../hide-column-multiselect.blade.php | 40 ++++++++++--------- src/Column.php | 4 +- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/resources/views/livewire/datatables/hide-column-multiselect.blade.php b/resources/views/livewire/datatables/hide-column-multiselect.blade.php index 04ccb743..d4c5770c 100644 --- a/resources/views/livewire/datatables/hide-column-multiselect.blade.php +++ b/resources/views/livewire/datatables/hide-column-multiselect.blade.php @@ -8,28 +8,30 @@
@foreach($this->columns as $index => $column) -
- diff --git a/src/Column.php b/src/Column.php index 0c2d51a7..5208b0f6 100644 --- a/src/Column.php +++ b/src/Column.php @@ -221,9 +221,9 @@ public function defaultSort($direction = true) return $this; } - public function hideable() + public function hideable($hideable = true) { - $this->hideable = true; + $this->hideable = $hideable; return $this; } From e6cc450a9ef72f137b90dd5a64c53eebab772774 Mon Sep 17 00:00:00 2001 From: renardudezert Date: Fri, 14 Oct 2022 14:34:21 +0200 Subject: [PATCH 087/113] Bugfix for relations in resolveColumnName method (#509) - Set variable $useThrough AFTER checking if relation is an instance of HasMany, HasManyThrough or BelongsToMany. Otherwise, it will return an error because no joins property exists for $this->query->getQuery() --- src/Http/Livewire/LivewireDatatable.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 14165a41..2e60478d 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -436,16 +436,16 @@ protected function resolveColumnName($column, $additional = null) while (count($relations) > 0) { $relation = array_shift($relations); - $useThrough = collect($this->query->getQuery()->joins) - ->pluck('table') - ->contains($relatedQuery->getRelation($relation)->getRelated()->getTable()); - if ($relatedQuery->getRelation($relation) instanceof HasMany || $relatedQuery->getRelation($relation) instanceof HasManyThrough || $relatedQuery->getRelation($relation) instanceof BelongsToMany) { $this->query->customWithAggregate($aggregateName, $column->aggregate ?? 'count', $columnName, $column->name); return null; } + $useThrough = collect($this->query->getQuery()->joins) + ->pluck('table') + ->contains($relatedQuery->getRelation($relation)->getRelated()->getTable()); + $relatedQuery = $this->query->joinRelation($relation, null, 'left', $useThrough, $relatedQuery); } From e76e00a6e944af5ca773718a39094c45d0684964 Mon Sep 17 00:00:00 2001 From: Giorgio Date: Tue, 18 Oct 2022 14:17:00 +0200 Subject: [PATCH 088/113] fix customWithAggregate and hasAggregate macros (#510) Add table prefix to the table variable in the customWithAggregate and hasAggregate EloquentBuilder macros registerd by the service provider. --- src/LivewireDatatablesServiceProvider.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/LivewireDatatablesServiceProvider.php b/src/LivewireDatatablesServiceProvider.php index 553b90e9..f63dde05 100644 --- a/src/LivewireDatatablesServiceProvider.php +++ b/src/LivewireDatatablesServiceProvider.php @@ -95,7 +95,7 @@ public function loadEloquentBuilderMacros() $table = $relation->getRelated()->newQuery()->getQuery()->from === $this->getQuery()->from ? $relation->getRelationCountHashWithoutIncrementing() - : $relation->getRelated()->getTable(); + : ($this->query->getConnection()->getTablePrefix() ?? '') . $relation->getRelated()->getTable(); $query = $relation->getRelationExistenceAggregatesQuery( $relation->getRelated()->newQuery(), @@ -125,7 +125,7 @@ public function loadEloquentBuilderMacros() $table = $relation->getRelated()->newQuery()->getQuery()->from === $this->getQuery()->from ? $relation->getRelationCountHashWithoutIncrementing() - : $relation->getRelated()->getTable(); + : ($this->query->getConnection()->getTablePrefix() ?? '') . $relation->getRelated()->getTable(); $hasQuery = $relation->getRelationExistenceAggregatesQuery( $relation->getRelated()->newQueryWithoutRelationships(), From 0d39c09094fbe18df56265234c38311750fe31c9 Mon Sep 17 00:00:00 2001 From: Herbert Maschke Date: Sat, 12 Nov 2022 10:37:33 +0100 Subject: [PATCH 089/113] fix project description in composer.json --- composer.json | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index c09a9c3e..f970fae2 100644 --- a/composer.json +++ b/composer.json @@ -1,8 +1,10 @@ { - "name": "shaneburrell/livewire-datatables", - "description": "Fork of the livewire-datatables package. This has several modifications and additions.", + "name": "mediconesystems/livewire-datatables", + "description": "Advanced datatables using Laravel, Livewire, Tailwind CSS and Alpine JS", "keywords": [ - "livewire-datatables-fork" + "php", + "laravel", + "livewire" ], "homepage": "https://github.com/mediconesystems/livewire-datatables", "license": "MIT", @@ -17,6 +19,11 @@ "name": "Shane Burrell", "email": "shane@shaneburrell.com", "role": "Developer" + }, + { + "name": "Herbert Maschke", + "email": "thyseus@pm.me", + "role": "Developer" } ], "require": { From c13a4d0a71e237c8adaa37df5d268e96ab67fa1a Mon Sep 17 00:00:00 2001 From: Cosnavel <42392570+Cosnavel@users.noreply.github.com> Date: Sun, 27 Nov 2022 09:06:09 +0100 Subject: [PATCH 090/113] Added defaultFilters to Livewire Datatables (#527) Added defaultFilters to Livewire Datatables --- README.md | 28 +++++++++ src/Http/Livewire/LivewireDatatable.php | 76 +++++++++++++++++++++++-- 2 files changed, 98 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d6329bf9..ff4e45af 100644 --- a/README.md +++ b/README.md @@ -413,6 +413,34 @@ class CallbackDemoTable extends LivewireDatatable } ``` +### Default Filters + +If you want to have a default filter applied to your table, you can use the `defaultFilters` property. The `defaultFilter` should be an Array of column names and the default filter value to use for. When a persisted filter (`$this->persistFilters` is true and session values are available) is available, it will override the default filters. + +In the example below, the table will by default be filtered by rows where the _deleted_at_ column is false. If the user has a persisted filter for the _deleted_at_ column, the default filter will be ignored. + +```php +class CallbackDemoTable extends LivewireDatatable +{ + public $defaultFilters = [ + 'deleted_at' => '0', + ]; + + public function builder() + { + return User::query()->withTrashed(); + } + + public function columns() + { + return [ + Column::name('id'), + BooleanColumn::name('deleted_at')->filterable(), + ]; + } +} +``` + ### Views You can specify that a column's output is piped directly into a separate blade view template. - Template is specified using ususal laravel view helper syntax diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 2e60478d..49b13989 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -36,6 +36,7 @@ class LivewireDatatable extends Component public $activeBooleanFilters = []; public $activeTextFilters = []; public $activeNumberFilters = []; + public $defaultFilters = []; public $hideHeader; public $hidePagination; public $perPage; @@ -265,6 +266,7 @@ public function mount( $this->initialiseSearch(); $this->initialiseSort(); $this->initialiseHiddenColumns(); + $this->initialiseDefaultFilters(); $this->initialiseFilters(); $this->initialisePerPage(); $this->initialiseColumnGroups(); @@ -587,6 +589,51 @@ public function initialiseColumnGroups() }, $this->columns); } + public function initialiseDefaultFilters() + { + if (! $this->defaultFilters || ! is_array($this->defaultFilters) || count($this->defaultFilters) === 0) { + return; + } + + $columns = collect($this->columns); + + foreach ($this->defaultFilters as $columnName => $value) { + $columnIndex = $columns->search(function ($column) use ($columnName) { + return $column['name'] === $columnName; + }); + + if ($columnIndex === false) { + continue; + } + + $column = $columns[$columnIndex]; + + if ($column['type'] === 'string') { + $this->activeTextFilters[$columnIndex] = $value; + } + + if ($column['type'] === 'boolean') { + $this->activeBooleanFilters[$columnIndex] = $value; + } + + if ($column['type'] === 'select') { + $this->activeSelectFilters[$columnIndex] = $value; + } + + if ($column['type'] === 'date') { + $this->activeDateFilters[$columnIndex] = $value; + } + + if ($column['type'] === 'time') { + $this->activeTimeFilters[$columnIndex] = $value; + } + + if ($column['type'] === 'number') { + $this->activeNumberFilters[$columnIndex] = $value; + } + } + } + public function initialiseFilters() { if (! $this->persistFilters) { @@ -595,12 +642,29 @@ public function initialiseFilters() $filters = session()->get($this->sessionStorageKey() . '_filter'); - $this->activeBooleanFilters = $filters['boolean'] ?? []; - $this->activeSelectFilters = $filters['select'] ?? []; - $this->activeTextFilters = $filters['text'] ?? []; - $this->activeDateFilters = $filters['date'] ?? []; - $this->activeTimeFilters = $filters['time'] ?? []; - $this->activeNumberFilters = $filters['number'] ?? []; + if (! empty($filters['text'])) { + $this->activeTextFilters = $filters['text']; + } + + if (! empty($filters['boolean'])) { + $this->activeBooleanFilters = $filters['boolean']; + } + + if (! empty($filters['select'])) { + $this->activeSelectFilters = $filters['select']; + } + + if (! empty($filters['date'])) { + $this->activeDateFilters = $filters['date']; + } + + if (! empty($filters['time'])) { + $this->activeTimeFilters = $filters['time']; + } + + if (! empty($filters['number'])) { + $this->activeNumberFilters = $filters['number']; + } if (isset($filters['search'])) { $this->search = $filters['search']; From f140158c67f0d6b269226e4b249e90aa08438d1e Mon Sep 17 00:00:00 2001 From: renardudezert Date: Sun, 27 Nov 2022 13:38:59 +0100 Subject: [PATCH 091/113] Bugfix checkboxes behavior when search or filters are active (#522) Bugfix checkboxes behavior when search or filters are active - If the search or filter is active, we must display the count of checked rows only for "visible" items (results of search or filtered results). Therefore, the toggleSelectAll() method must also check or uncheck all "visible" items when the search or filters are active. --- .../livewire/datatables/datatable.blade.php | 2 +- .../datatables/filters/checkbox.blade.php | 2 +- src/Http/Livewire/LivewireDatatable.php | 88 +++++++++++++++---- 3 files changed, 71 insertions(+), 21 deletions(-) diff --git a/resources/views/livewire/datatables/datatable.blade.php b/resources/views/livewire/datatables/datatable.blade.php index be0b94ea..a7c2e628 100644 --- a/resources/views/livewire/datatables/datatable.blade.php +++ b/resources/views/livewire/datatables/datatable.blade.php @@ -109,7 +109,7 @@ class="px-3 py-2 text-xs font-medium tracking-wider text-green-500 uppercase bg- @unless($column['hidden'])
- {{ count($selected) }} + {{ count($visibleSelected) }}
@endunless diff --git a/resources/views/livewire/datatables/filters/checkbox.blade.php b/resources/views/livewire/datatables/filters/checkbox.blade.php index 15dcd6ab..87f5a871 100644 --- a/resources/views/livewire/datatables/filters/checkbox.blade.php +++ b/resources/views/livewire/datatables/filters/checkbox.blade.php @@ -7,7 +7,7 @@ class="flex flex-col items-center h-full px-6 py-5 overflow-hidden text-xs font- type="checkbox" wire:click="toggleSelectAll" class="w-4 h-4 mt-1 text-blue-600 form-checkbox transition duration-150 ease-in-out" - @if(count($selected) === $this->results->total()) checked @endif + @if($this->results->total() === count($visibleSelected)) checked @endif />
diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 49b13989..38289dc4 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -65,6 +65,7 @@ class LivewireDatatable extends Component public $persistSort = true; public $persistPerPage = true; public $persistFilters = true; + public $visibleSelected = []; public $row = 1; public $tablePrefix = ''; @@ -217,6 +218,7 @@ public function resetHiddenColumns() public function updatedSearch() { + $this->visibleSelected = ($this->search) ? array_intersect($this->getQuery()->get()->pluck('checkbox_attribute')->toArray(), $this->selected) : $this->selected; $this->setPage(1); } @@ -704,21 +706,21 @@ public function getSortString($dbtable) return Str::before($column['select'], ' AS '); break; - default: - - switch ($dbtable) { - case 'mysql': - return new Expression('`' . $column['name'] . '`'); - break; - case 'pgsql': - return new Expression('"' . $column['name'] . '"'); - break; - case 'sqlsrv': - return new Expression("'" . $column['name'] . "'"); - break; - default: - return new Expression("'" . $column['name'] . "'"); - } + default: + + switch ($dbtable) { + case 'mysql': + return new Expression('`' . $column['name'] . '`'); + break; + case 'pgsql': + return new Expression('"' . $column['name'] . '"'); + break; + case 'sqlsrv': + return new Expression("'" . $column['name'] . "'"); + break; + default: + return new Expression("'" . $column['name'] . "'"); + } } } @@ -870,6 +872,7 @@ public function isGroupHidden($group) public function doBooleanFilter($index, $value) { $this->activeBooleanFilters[$index] = $value; + $this->setVisibleSelected(); $this->setPage(1); $this->setSessionStoredFilters(); } @@ -877,6 +880,7 @@ public function doBooleanFilter($index, $value) public function doSelectFilter($index, $value) { $this->activeSelectFilters[$index][] = $value; + $this->setVisibleSelected(); $this->setPage(1); $this->setSessionStoredFilters(); } @@ -886,7 +890,7 @@ public function doTextFilter($index, $value) foreach (explode(' ', $value) as $val) { $this->activeTextFilters[$index][] = $val; } - + $this->setVisibleSelected(); $this->setPage(1); $this->setSessionStoredFilters(); } @@ -894,6 +898,7 @@ public function doTextFilter($index, $value) public function doDateFilterStart($index, $start) { $this->activeDateFilters[$index]['start'] = $start; + $this->setVisibleSelected(); $this->setPage(1); $this->setSessionStoredFilters(); } @@ -901,6 +906,7 @@ public function doDateFilterStart($index, $start) public function doDateFilterEnd($index, $end) { $this->activeDateFilters[$index]['end'] = $end; + $this->setVisibleSelected(); $this->setPage(1); $this->setSessionStoredFilters(); } @@ -908,6 +914,7 @@ public function doDateFilterEnd($index, $end) public function doTimeFilterStart($index, $start) { $this->activeTimeFilters[$index]['start'] = $start; + $this->setVisibleSelected(); $this->setPage(1); $this->setSessionStoredFilters(); } @@ -915,6 +922,7 @@ public function doTimeFilterStart($index, $start) public function doTimeFilterEnd($index, $end) { $this->activeTimeFilters[$index]['end'] = $end; + $this->setVisibleSelected(); $this->setPage(1); $this->setSessionStoredFilters(); } @@ -923,6 +931,7 @@ public function doNumberFilterStart($index, $start) { $this->activeNumberFilters[$index]['start'] = ($start != '') ? (int) $start : null; $this->clearEmptyNumberFilter($index); + $this->setVisibleSelected(); $this->setPage(1); $this->setSessionStoredFilters(); } @@ -931,6 +940,7 @@ public function doNumberFilterEnd($index, $end) { $this->activeNumberFilters[$index]['end'] = ($end != '') ? (int) $end : null; $this->clearEmptyNumberFilter($index); + $this->setVisibleSelected(); $this->setPage(1); $this->setSessionStoredFilters(); } @@ -947,6 +957,7 @@ public function clearEmptyNumberFilter($index) public function removeSelectFilter($column, $key = null) { unset($this->activeSelectFilters[$column][$key]); + $this->visibleSelected = $this->selected; if (count($this->activeSelectFilters[$column]) < 1) { unset($this->activeSelectFilters[$column]); } @@ -964,6 +975,7 @@ public function clearAllFilters() $this->activeNumberFilters = []; $this->complexQuery = null; $this->userFilter = null; + $this->visibleSelected = $this->selected; $this->setPage(1); $this->setSessionStoredFilters(); @@ -973,6 +985,7 @@ public function clearAllFilters() public function removeBooleanFilter($column) { unset($this->activeBooleanFilters[$column]); + $this->visibleSelected = $this->selected; $this->setSessionStoredFilters(); } @@ -986,12 +999,14 @@ public function removeTextFilter($column, $key = null) } else { unset($this->activeTextFilters[$column]); } + $this->visibleSelected = $this->selected; $this->setSessionStoredFilters(); } public function removeNumberFilter($column) { unset($this->activeNumberFilters[$column]); + $this->visibleSelected = $this->selected; $this->setSessionStoredFilters(); } @@ -1684,14 +1699,38 @@ public function checkboxQuery() public function toggleSelectAll() { - if (count($this->selected) === $this->getQuery()->getCountForPagination()) { - $this->selected = []; + $visible_checkboxes = $this->getQuery()->get()->pluck('checkbox_attribute')->toArray(); + $visible_checkboxes = array_map('strval', $visible_checkboxes); + if ($this->searchOrFilterActive()) { + if (count($this->visibleSelected) === count($visible_checkboxes)) { + $this->selected = array_values(array_diff($this->selected, $visible_checkboxes)); + $this->visibleSelected = []; + } else { + $this->selected = array_unique(array_merge($this->selected, $visible_checkboxes)); + sort($this->selected); + $this->visibleSelected = $visible_checkboxes; + } } else { - $this->selected = $this->checkboxQuery()->values()->toArray(); + if (count($this->selected) === $this->getQuery()->getCountForPagination()) { + $this->selected = []; + } else { + $this->selected = $this->checkboxQuery()->values()->toArray(); + } + $this->visibleSelected = $this->selected; } + $this->forgetComputed(); } + public function updatedSelected() + { + if ($this->searchOrFilterActive()) { + $this->setVisibleSelected(); + } else { + $this->visibleSelected = $this->selected; + } + } + public function rowIsSelected($row) { return isset($row->checkbox_attribute) && in_array($row->checkbox_attribute, $this->selected); @@ -1802,4 +1841,15 @@ public function massActionOptionHandler() $this->massActionOption = null; } + + private function searchOrFilterActive() + { + return ! empty($this->search) || $this->getActiveFiltersProperty(); + } + + private function setVisibleSelected() + { + $this->visibleSelected = array_intersect($this->getQuery()->get()->pluck('checkbox_attribute')->toArray(), $this->selected); + $this->visibleSelected = array_map('strval', $this->visibleSelected); + } } From 06d4a2a1b20e6622db340d7190cc098c2bc65f8a Mon Sep 17 00:00:00 2001 From: Giorgio Date: Sun, 27 Nov 2022 13:39:26 +0100 Subject: [PATCH 092/113] add SQLite dialect to resolveAdditionalSelects (#519) check which database driver is used and build the query according to its dialect. added SQLite dialect only, partially fiexs issue #183 --- src/Http/Livewire/LivewireDatatable.php | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 38289dc4..f5cf0dfb 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -352,12 +352,23 @@ public function resolveAdditionalSelects($column) : $this->query->getModel()->getTable() . '.' . $select; }); - return $selects->count() > 1 - ? new Expression("CONCAT_WS('" . static::SEPARATOR . "' ," . - collect($selects)->map(function ($select) { - return 'COALESCE(' . $this->tablePrefix . $select . ', \'\')'; - })->join(', ') . ')') - : $selects->first(); + if (DB::connection() instanceof \Illuminate\Database\SQLiteConnection) { + // SQLite dialect. + return $selects->count() > 1 + ? new Expression('(' . + collect($selects)->map(function ($select) { + return 'COALESCE(' . $this->tablePrefix . $select . ', \'\')'; + })->join(" || '" . static::SEPARATOR . "' || ") . ')') + : $selects->first(); + } else { + // Default to MySql dialect. + return $selects->count() > 1 + ? new Expression("CONCAT_WS('" . static::SEPARATOR . "' ," . + collect($selects)->map(function ($select) { + return 'COALESCE(' . $this->tablePrefix . $select . ', \'\')'; + })->join(', ') . ')') + : $selects->first(); + } } public function resolveEditableColumnName($column) From 8dc46a772b082643d0d5d98518e14b410b260cfd Mon Sep 17 00:00:00 2001 From: Giorgio Date: Sun, 27 Nov 2022 13:39:55 +0100 Subject: [PATCH 093/113] fix addGlobalSearch method (#511) Fix to prevent addGlobalSearch method to prepend tablePrefix when column contains '_concat_'. It was generating a syntax error in the query, i.e.: SQLSTATE[42000]: Syntax error or access violation: 1305 FUNCTION **DBNAME**.**TABLEPREFIX**CONCAT_WS does not exist --- src/Http/Livewire/LivewireDatatable.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index f5cf0dfb..cb0d1269 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -1320,10 +1320,10 @@ public function addGlobalSearch() foreach ($this->getColumnFilterStatement($i) as $column) { $query->when(is_array($column), function ($query) use ($search, $column) { foreach ($column as $col) { - $query->orWhereRaw('LOWER(' . $this->tablePrefix . $col . ') like ?', '%' . mb_strtolower($search) . '%'); + $query->orWhereRaw('LOWER(' . (Str::contains(mb_strtolower($column), 'concat') ? '' : $this->tablePrefix) . $col . ') like ?', '%' . mb_strtolower($search) . '%'); } }, function ($query) use ($search, $column) { - $query->orWhereRaw('LOWER(' . $this->tablePrefix . $column . ') like ?', '%' . mb_strtolower($search) . '%'); + $query->orWhereRaw('LOWER(' . (Str::contains(mb_strtolower($column), 'concat') ? '' : $this->tablePrefix) . $column . ') like ?', '%' . mb_strtolower($search) . '%'); }); } }); From 966b719637c1e64eb0c708d28db1a5d00775301b Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Fri, 7 Jan 2022 10:54:11 +0000 Subject: [PATCH 094/113] Apply fixes from StyleCI --- src/Http/Livewire/LivewireDatatable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 0bb0feb8..cb579c68 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -395,7 +395,7 @@ protected function resolveColumnName($column, $additional = null) $aggregate = Str::after(($additional ?: $column->name), ':'); if (! method_exists($this->query->getModel(), $relations[0])) { - return ($additional ?: $column->name); + return $additional ?: $column->name; } $columnName = array_pop($relations); From 1d4a02ad4db0f319b432cb5278ea4941573d97b0 Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Wed, 23 Mar 2022 19:47:35 +0000 Subject: [PATCH 095/113] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 8a2b19bb..608c6261 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "illuminate/support": "^7.0|^8.0", "livewire/livewire": "^2.4.4", "maatwebsite/excel": "^3.1", - "reedware/laravel-relation-joins": "^2.4" + "reedware/laravel-relation-joins": "^2.4|^3.0" }, "require-dev": { "laravel/legacy-factories": "^1.0.4", From 1cc392dea6bfb1755c0413c916dc33b16eb64df9 Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Wed, 23 Mar 2022 19:48:23 +0000 Subject: [PATCH 096/113] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 608c6261..463a5ff8 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": "^7.2.5|^8.0", - "illuminate/support": "^7.0|^8.0", + "illuminate/support": "^7.0|^8.0|^9.0", "livewire/livewire": "^2.4.4", "maatwebsite/excel": "^3.1", "reedware/laravel-relation-joins": "^2.4|^3.0" From 35586580a12f7e8ab92be33f5898f25c457cb243 Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Wed, 18 May 2022 12:08:30 +0100 Subject: [PATCH 097/113] try again --- src/Http/Livewire/LivewireDatatable.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index cb579c68..8b4a9eaa 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -395,7 +395,7 @@ protected function resolveColumnName($column, $additional = null) $aggregate = Str::after(($additional ?: $column->name), ':'); if (! method_exists($this->query->getModel(), $relations[0])) { - return $additional ?: $column->name; + return ($additional ?: $column->name); } $columnName = array_pop($relations); @@ -1111,9 +1111,15 @@ public function processNested($rules = null, $query = null, $logic = 'and') $query->whereNotNull($column); } elseif ($this->columns[$rule['content']['column']]['type'] === 'boolean') { if ($rule['content']['value'] === 'true') { - $query->where(Str::contains($column, '(') ? DB::raw($column) : $column, 1); + $query->where(function ($query) use ($column) { + $query->whereNotNull(Str::contains($column, '(') ? DB::raw($column) : $column) + ->where($column, '<>', 0); + }); } else { - $query->where(Str::contains($column, '(') ? DB::raw($column) : $column, '<>', 1); + $query->where(function ($query) use ($column) { + $query->whereNull(Str::contains($column, '(') ? DB::raw($column) : $column) + ->orWhere(Str::contains($column, '(') ? DB::raw($column) : $column, 0); + }); } } else { $col = (isset($this->freshColumns[$rule['content']['column']]['round']) && $this->freshColumns[$rule['content']['column']]['round'] !== null) From 7957648cc73e574d36546bf1b5428545605acf9b Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Fri, 2 Dec 2022 14:47:42 +0000 Subject: [PATCH 098/113] Apply fixes from StyleCI (#528) Co-authored-by: StyleCI Bot --- src/Http/Livewire/LivewireDatatable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 8b4a9eaa..35922a85 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -395,7 +395,7 @@ protected function resolveColumnName($column, $additional = null) $aggregate = Str::after(($additional ?: $column->name), ':'); if (! method_exists($this->query->getModel(), $relations[0])) { - return ($additional ?: $column->name); + return $additional ?: $column->name; } $columnName = array_pop($relations); From b09bb9f161eba579f80fd9856ce5b2374f8492c5 Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Thu, 6 Jan 2022 14:45:49 +0000 Subject: [PATCH 099/113] rebase master --- src/Column.php | 49 +++++++++++++++++++++++++ src/Http/Livewire/LivewireDatatable.php | 2 +- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/Column.php b/src/Column.php index 5208b0f6..3f406581 100644 --- a/src/Column.php +++ b/src/Column.php @@ -481,6 +481,55 @@ public function group($group) return $this; } + public function resolveName($query) + { + return $this->isBaseColumn() + ? $query->getModel()->getTable() . '.' . ($this->base ?? Str::before($this->name, ':')) + : $this->joinRelations($query); + } + + public function joinRelations($query) + { + $parts = explode('.', Str::before($this->name, ':')); + + $columnName = array_pop($parts); + // callsign + + $relation = implode('.', $parts); + // job.crew + + $table = ''; + $model = ''; + $lastQuery = $query; + foreach (explode('.', $relation) as $eachRelation) { + $model = $lastQuery->getRelation($eachRelation); + + ray( + $eachRelation, + $lastQuery + // collect($lastQuery->getQuery()->joins)->pluck('table'), + // $model->getQuery()->from, + // collect($lastQuery->getQuery()->joins)->pluck('table')->contains( + // $model->getQuery()->from + // ) + )->purple(); + if ( + ! collect($lastQuery->getQuery()->joins)->pluck('table')->contains( + $model->getQuery()->from + ) + ) { + ray($eachRelation, collect($lastQuery->getQuery()->joins)->pluck('table'), $model->getQuery()->from)->red(); + $query->leftJoinRelation($model); + } + + + + $lastQuery = $model->getQuery(); + } + + return $lastQuery->getQuery()->from . '.' . $columnName; + } + public function relations() { return $this->isBaseColumn() ? null : collect(explode('.', Str::beforeLast($this->name, '.'))); diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index cb0d1269..19ebf8e3 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -390,13 +390,13 @@ public function getSelectStatements($withAlias = false, $export = false) } if (Str::startsWith($column->name, 'callback_')) { + ray($column)->red(); $column->select = $this->resolveAdditionalSelects($column); return $column; } $column->select = $this->resolveColumnName($column); - if ($column->isEditable()) { $column->select = $this->resolveEditableColumnName($column); } From 1749ded34cd2a15e18ce1801460ceef90fa57fc4 Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Thu, 6 Jan 2022 15:53:38 +0000 Subject: [PATCH 100/113] wip --- tests/TestCase.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/TestCase.php b/tests/TestCase.php index 9680e5d3..0a2a1d45 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,13 +2,13 @@ namespace Mediconesystems\LivewireDatatables\Tests; -use Illuminate\Support\Facades\View; use Illuminate\Support\Str; +use Illuminate\Support\Facades\View; use Livewire\LivewireServiceProvider; use Maatwebsite\Excel\ExcelServiceProvider; -use Mediconesystems\LivewireDatatables\LivewireDatatablesServiceProvider; use Orchestra\Testbench\TestCase as Orchestra; use Reedware\LaravelRelationJoins\LaravelRelationJoinServiceProvider; +use Mediconesystems\LivewireDatatables\LivewireDatatablesServiceProvider; class TestCase extends Orchestra { From 91fc90bfab685a1f3b5d4737cac707e76fb79cfb Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Thu, 6 Jan 2022 15:56:01 +0000 Subject: [PATCH 101/113] wip --- src/Column.php | 49 ------------------------------------------------- 1 file changed, 49 deletions(-) diff --git a/src/Column.php b/src/Column.php index 3f406581..5208b0f6 100644 --- a/src/Column.php +++ b/src/Column.php @@ -481,55 +481,6 @@ public function group($group) return $this; } - public function resolveName($query) - { - return $this->isBaseColumn() - ? $query->getModel()->getTable() . '.' . ($this->base ?? Str::before($this->name, ':')) - : $this->joinRelations($query); - } - - public function joinRelations($query) - { - $parts = explode('.', Str::before($this->name, ':')); - - $columnName = array_pop($parts); - // callsign - - $relation = implode('.', $parts); - // job.crew - - $table = ''; - $model = ''; - $lastQuery = $query; - foreach (explode('.', $relation) as $eachRelation) { - $model = $lastQuery->getRelation($eachRelation); - - ray( - $eachRelation, - $lastQuery - // collect($lastQuery->getQuery()->joins)->pluck('table'), - // $model->getQuery()->from, - // collect($lastQuery->getQuery()->joins)->pluck('table')->contains( - // $model->getQuery()->from - // ) - )->purple(); - if ( - ! collect($lastQuery->getQuery()->joins)->pluck('table')->contains( - $model->getQuery()->from - ) - ) { - ray($eachRelation, collect($lastQuery->getQuery()->joins)->pluck('table'), $model->getQuery()->from)->red(); - $query->leftJoinRelation($model); - } - - - - $lastQuery = $model->getQuery(); - } - - return $lastQuery->getQuery()->from . '.' . $columnName; - } - public function relations() { return $this->isBaseColumn() ? null : collect(explode('.', Str::beforeLast($this->name, '.'))); From fad8d27a8f7067e8682eaeddf5c4664cef499f17 Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Thu, 6 Jan 2022 20:47:19 +0000 Subject: [PATCH 102/113] wip --- src/Http/Livewire/LivewireDatatable.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 19ebf8e3..662c27d7 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -390,16 +390,17 @@ public function getSelectStatements($withAlias = false, $export = false) } if (Str::startsWith($column->name, 'callback_')) { - ray($column)->red(); $column->select = $this->resolveAdditionalSelects($column); return $column; } $column->select = $this->resolveColumnName($column); + if ($column->isEditable()) { $column->select = $this->resolveEditableColumnName($column); } + // ray($column)->red(); return $column; })->when($withAlias, function ($columns) { From 7a0757b73a4b22170254ea75a1b14451de218e69 Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Thu, 6 Jan 2022 21:20:33 +0000 Subject: [PATCH 103/113] Apply fixes from StyleCI (#374) Co-authored-by: StyleCI Bot --- tests/TestCase.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/TestCase.php b/tests/TestCase.php index 0a2a1d45..9680e5d3 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,13 +2,13 @@ namespace Mediconesystems\LivewireDatatables\Tests; -use Illuminate\Support\Str; use Illuminate\Support\Facades\View; +use Illuminate\Support\Str; use Livewire\LivewireServiceProvider; use Maatwebsite\Excel\ExcelServiceProvider; +use Mediconesystems\LivewireDatatables\LivewireDatatablesServiceProvider; use Orchestra\Testbench\TestCase as Orchestra; use Reedware\LaravelRelationJoins\LaravelRelationJoinServiceProvider; -use Mediconesystems\LivewireDatatables\LivewireDatatablesServiceProvider; class TestCase extends Orchestra { From 7d1e3cfe85e71eb08fe51fd07a6e8235f36cb229 Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Fri, 7 Jan 2022 10:53:51 +0000 Subject: [PATCH 104/113] bugfix --- src/Http/Livewire/LivewireDatatable.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 662c27d7..b24d416d 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -400,7 +400,6 @@ public function getSelectStatements($withAlias = false, $export = false) if ($column->isEditable()) { $column->select = $this->resolveEditableColumnName($column); } - // ray($column)->red(); return $column; })->when($withAlias, function ($columns) { @@ -441,7 +440,7 @@ protected function resolveColumnName($column, $additional = null) $aggregate = Str::after(($additional ?: $column->name), ':'); if (! method_exists($this->query->getModel(), $relations[0])) { - return $additional ?: $column->name; + return ($additional ?: $column->name); } $columnName = array_pop($relations); From 73f9a77f034f9d2583a337613bdd3632fc29a186 Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Wed, 18 May 2022 11:34:04 +0100 Subject: [PATCH 105/113] fix boolean complex query --- src/Http/Livewire/LivewireDatatable.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index b24d416d..cfe15f3e 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -1277,9 +1277,9 @@ public function processNested($rules = null, $query = null, $logic = 'and') $query->whereNotNull($column); } elseif ($this->columns[$rule['content']['column']]['type'] === 'boolean') { if ($rule['content']['value'] === 'true') { - $query->whereNotNull(Str::contains($column, '(') ? DB::raw($column) : $column); + $query->where(Str::contains($column, '(') ? DB::raw($column) : $column, 1); } else { - $query->whereNull(Str::contains($column, '(') ? DB::raw($column) : $column); + $query->where(Str::contains($column, '(') ? DB::raw($column) : $column, '<>', 1); } } else { $col = (isset($this->freshColumns[$rule['content']['column']]['round']) && $this->freshColumns[$rule['content']['column']]['round'] !== null) From 0490d7451d4e6dce2ae5fcbcb429d37c56269bd7 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Fri, 7 Jan 2022 10:54:11 +0000 Subject: [PATCH 106/113] Apply fixes from StyleCI --- src/Http/Livewire/LivewireDatatable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index cfe15f3e..b329bf9e 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -440,7 +440,7 @@ protected function resolveColumnName($column, $additional = null) $aggregate = Str::after(($additional ?: $column->name), ':'); if (! method_exists($this->query->getModel(), $relations[0])) { - return ($additional ?: $column->name); + return $additional ?: $column->name; } $columnName = array_pop($relations); From 63233cf6d1744bf1238c1fd67e8fbed074bfa651 Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Wed, 18 May 2022 12:08:30 +0100 Subject: [PATCH 107/113] try again --- src/Http/Livewire/LivewireDatatable.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index b329bf9e..008dca5f 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -440,7 +440,7 @@ protected function resolveColumnName($column, $additional = null) $aggregate = Str::after(($additional ?: $column->name), ':'); if (! method_exists($this->query->getModel(), $relations[0])) { - return $additional ?: $column->name; + return ($additional ?: $column->name); } $columnName = array_pop($relations); @@ -1277,9 +1277,15 @@ public function processNested($rules = null, $query = null, $logic = 'and') $query->whereNotNull($column); } elseif ($this->columns[$rule['content']['column']]['type'] === 'boolean') { if ($rule['content']['value'] === 'true') { - $query->where(Str::contains($column, '(') ? DB::raw($column) : $column, 1); + $query->where(function ($query) use ($column) { + $query->whereNotNull(Str::contains($column, '(') ? DB::raw($column) : $column) + ->where($column, '<>', 0); + }); } else { - $query->where(Str::contains($column, '(') ? DB::raw($column) : $column, '<>', 1); + $query->where(function ($query) use ($column) { + $query->whereNull(Str::contains($column, '(') ? DB::raw($column) : $column) + ->orWhere(Str::contains($column, '(') ? DB::raw($column) : $column, 0); + }); } } else { $col = (isset($this->freshColumns[$rule['content']['column']]['round']) && $this->freshColumns[$rule['content']['column']]['round'] !== null) From 101861ce27260137726d6b5fa46aaead419d188e Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Fri, 7 Jan 2022 10:54:11 +0000 Subject: [PATCH 108/113] Apply fixes from StyleCI --- src/Http/Livewire/LivewireDatatable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 008dca5f..4ef8e211 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -440,7 +440,7 @@ protected function resolveColumnName($column, $additional = null) $aggregate = Str::after(($additional ?: $column->name), ':'); if (! method_exists($this->query->getModel(), $relations[0])) { - return ($additional ?: $column->name); + return $additional ?: $column->name; } $columnName = array_pop($relations); From 1e5a8dc840ceaea601d015cf900f93b9c9807c2c Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Wed, 18 May 2022 12:08:30 +0100 Subject: [PATCH 109/113] try again --- src/Http/Livewire/LivewireDatatable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 4ef8e211..008dca5f 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -440,7 +440,7 @@ protected function resolveColumnName($column, $additional = null) $aggregate = Str::after(($additional ?: $column->name), ':'); if (! method_exists($this->query->getModel(), $relations[0])) { - return $additional ?: $column->name; + return ($additional ?: $column->name); } $columnName = array_pop($relations); From f42471532ca6ea3e24a1e64220c696fba3ee8ee8 Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Fri, 2 Dec 2022 15:56:19 +0000 Subject: [PATCH 110/113] wip --- src/Http/Livewire/LivewireDatatable.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index 008dca5f..bdfc7c42 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -1692,10 +1692,10 @@ public function getExportResultsSet() })->get(), true )->map(function ($item) { - return collect($this->columns())->reject(function ($value, $key) { - return $value->preventExport == true || $value->hidden == true; - })->mapWithKeys(function ($value, $key) use ($item) { - return [$value->label ?? $value->name => $item->{$value->name}]; + return collect($this->columns)->reject(function ($value) { + return $value['preventExport'] == true || $value['hidden'] == true; + })->mapWithKeys(function ($value) use ($item) { + return [$value['label'] ?? $value['name'] => $item->{$value['name']}]; })->all(); }); } From 51388ad1c6a088a45d12c841d674b15a43ab1229 Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Fri, 2 Dec 2022 17:14:17 +0000 Subject: [PATCH 111/113] Apply fixes from StyleCI (#529) Co-authored-by: StyleCI Bot --- src/Http/Livewire/LivewireDatatable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index bdfc7c42..e56c9bbc 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -440,7 +440,7 @@ protected function resolveColumnName($column, $additional = null) $aggregate = Str::after(($additional ?: $column->name), ':'); if (! method_exists($this->query->getModel(), $relations[0])) { - return ($additional ?: $column->name); + return $additional ?: $column->name; } $columnName = array_pop($relations); From 628c06ad2cc702ab9c41f4e705b07f9a163dcfd5 Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Fri, 2 Dec 2022 17:29:35 +0000 Subject: [PATCH 112/113] wip --- .../datatables/complex-query.blade.php | 96 ++++++++++--------- 1 file changed, 51 insertions(+), 45 deletions(-) diff --git a/resources/views/livewire/datatables/complex-query.blade.php b/resources/views/livewire/datatables/complex-query.blade.php index 57542edf..af86ff30 100644 --- a/resources/views/livewire/datatables/complex-query.blade.php +++ b/resources/views/livewire/datatables/complex-query.blade.php @@ -16,58 +16,64 @@ @endif
- @if(count($this->rules[0]['content'])) -
{{ $this->rulesString }}@if($errors->any()) Invalid rules @endif
- @endif +
+ @if(count($this->rules[0]['content'])) +
{{ $this->rulesString }}@if($errors->any()) Invalid rules @endif
+ @endif +
@include('datatables::complex-query-group', ['rules' => $rules, 'parentIndex' => null])
- @if(count($this->rules[0]['content'])) - @unless($errors->any()) -
-
- {{-- --}} +
+ @if(count($this->rules[0]['content'])) + @unless($errors->any()) +
+
+ {{-- --}} +
+
+ @isset($savedQueries) +
+ + +
+ @endisset + +
-
- @isset($savedQueries) -
- -
+ +
+ @if(count($savedQueries ?? [])) +
+
Saved Queries
+
+ @foreach($savedQueries as $saved) +
+ +
- @endisset - + @endforeach
@endif - - @endif - @if(count($savedQueries ?? [])) -
-
Saved Queries
-
- @foreach($savedQueries as $saved) -
- - -
- @endforeach -
-
- @endif +
From 784e245e1447264586039fff772c0cdaef4d2795 Mon Sep 17 00:00:00 2001 From: Mark Salmon Date: Mon, 5 Dec 2022 10:36:15 +0000 Subject: [PATCH 113/113] fix pagination with complex query --- src/Http/Livewire/LivewireDatatable.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Http/Livewire/LivewireDatatable.php b/src/Http/Livewire/LivewireDatatable.php index e56c9bbc..1366cc7f 100644 --- a/src/Http/Livewire/LivewireDatatable.php +++ b/src/Http/Livewire/LivewireDatatable.php @@ -1220,6 +1220,7 @@ public function buildDatabaseQuery($export = false) public function complexQuery($rules) { $this->complexQuery = $rules; + $this->setPage(1); } public function addComplexQuery() @@ -1232,8 +1233,6 @@ public function addComplexQuery() $this->processNested($this->complexQuery, $query); }); - $this->setPage(1); - return $this; }