Support `editColumn()` and `filterColumn()` on Column
Hi,
By using the DataTables Html Plugin, we can easily declare our columns in getColumns() method, but if we need to apply custom filters or edit, we must do it in the dataTable() method.
class UsersDataTable extends DataTable
{
public function getColumns(): array
{
return [
Column::make('id'),
Column::make('name'),
Column::make('email'),
Column::make('created_at'),
Column::make('updated_at'),
];
}
public function dataTable(QueryBuilder $query): EloquentDataTable
{
return (new EloquentDataTable($query))
->setRowId('id')
->filterColumn('name', function ($query, $keyword) {
$query->where(...);
})
->editColumn('name', function ($user) {
return $user->firstName.' '.$user->lastName;
});
}
}
When we have a lot of columns, it can be a bit confusing and not easy to manage. We need to declare columns in 1 place and filter/edit then in another place.
I would like to suggest to implement new methods on the Column class to automatically register edit and filter in the datatable.
The result will look like this:
class UsersDataTable extends DataTable
{
public function getColumns(): array
{
return [
Column::make('id'),
Column::make('name')
->filter(function ($query, $keyword) {
$query->where(...);
})
->edit(function ($user) {
return $user->firstName.' '.$user->lastName;
}),
Column::make('email'),
Column::make('created_at'),
Column::make('updated_at'),
];
}
public function dataTable(QueryBuilder $query): EloquentDataTable
{
return (new EloquentDataTable($query))->setRowId('id');
}
}
What do you think?
I love the idea since this is what I am also doing now with the export functionalities via exportFormat and exportRender.
And I think we should also consider the orderColumn callback?
For edit on the other hand, I tend to use render, renderRaw, and delegate it to js/client side when possible. I think it's better to make the server-side response as small as possible for better performance.
I agree with your idea of adding these functionalities to the Column definition.
Thanks!
Yes, I forgot the orderColumn but yes, it should be included too.
I also try to use render, renderRaw when possible, but in some cases it can be more convenient to use editColumn.
That's why I would like to support it too.
I took a look on this be not sure how to implement it.
If I understand properly, there is no relationship between the HTML Builder and the DataTable classes. The column builder is only used to declare the columns used by HTML Builder the generate the related HTML/JS. And the DataTable class is retrieving columns from the request.
So it's currently not possible to get the callbacks from the column builder to apply them to the Datatable.
I was not considering the DataTable Buttons plugin because those features are not really related to buttons like the export feature. But this plugin is acting as a bridge between the HTML Builder and the DataTable classes so it should be possible to use is to init edit/order/filter to the DataTable.
Here is a quick POC for editColumn to give you an idea of possible implementation.
On the Column class,
protected array $columnDefs = [
'edit' => [],
];
public function editColumn(\Closure $callback): static
{
$this->columnDefs['edit'][] = $callback;
return $this;
}
public function columnDefs(string $key): array
{
if (! array_key_exists($key, $this->columnDefs)) {
$this->columnDefs[$key] = [];
}
return $this->columnDefs[$key];
}
and in the DataTable class
public function ajax(): JsonResponse
{
$query = null;
if (method_exists($this, 'query')) {
/** @var EloquentBuilder|QueryBuilder|EloquentRelation $query */
$query = app()->call([$this, 'query']);
$query = $this->applyScopes($query);
}
/** @var \Yajra\DataTables\DataTableAbstract $dataTable */
// @phpstan-ignore-next-line
$dataTable = app()->call([$this, 'dataTable'], compact('query'));
// Inject edit columns callbacks from the html builder
foreach ($this->html()->getColumns() as $column) {
foreach ($column->columnDefs('edit') as $callback) {
$dataTable->editColumn($column->name, $callback);
}
}
if (is_callable($this->beforeCallback)) {
app()->call($this->beforeCallback, compact('dataTable'));
}
if (is_callable($this->responseCallback)) {
$data = new Collection($dataTable->toArray());
$response = app()->call($this->responseCallback, compact('data'));
return new JsonResponse($response);
}
return $dataTable->toJson();
}
Usage
Column::make('name')->editColumn(fn (User $user) => 'Edited'),
Hope this helps, thanks!