Infraestructura para tablas server-side con paginación, filtros, columnas, diálogos de confirmación y exportación Excel.
Desde este repositorio (VCS), agregando en el composer.json del proyecto:
"repositories": [
{ "type": "vcs", "url": "https://github.com/AdrianRodU/QuiroSys-Datatable.git" }
]composer require quirosys/datatableQuirosys\Datatable\
quirosys/laravel— paraApiResponseyAuthHelpermaatwebsite/excel— para exportación Excel
Cada módulo define un DataTable como trait y lo usa en su controlador:
// Modules/Plan/DataTables/PlanDataTable.php
use Quirosys\Datatable\Table\{Button, ButtonBuilder, Column, ColumnBuilder, Filter, FilterBuilder};
use Quirosys\Datatable\Traits\PaginationSystemTrait;
trait PlanDataTable
{
use PaginationSystemTrait;
protected function getTableConfig(): array
{
return [
'page_title' => __('plans title page'),
'table_title' => __('plans title table'),
'table_name' => 'plans',
];
}
protected function getHeaderButtons(): array
{
return (new ButtonBuilder())
->addButton(Button::newButton())
->addButton(Button::refreshButton())
->addButtonGroup([Button::exportButton()])
->getButtons();
}
protected function getColumns(): array
{
return (new ColumnBuilder())
->addColumn(Column::make('name')->label('Nombre'))
->addColumn(Column::make('price')->label('Precio')->alignRight()->width('100px'))
->addColumn(Column::isActive())
->addColumn(Column::actions())
->getColumns();
}
protected function getFilters(): array
{
return (new FilterBuilder())
->addFilter(Filter::makeInput('search')->label('Buscar')->cssClass('col-12'))
->getFilters();
}
}// Modules/Plan/Http/Controllers/PlanController.php
use Quirosys\Datatable\Dialog\DialogAction;
use Quirosys\Datatable\Dialog\Requests\ActionRequest;
use Quirosys\Laravel\Auth\AuthHelper;
use Quirosys\Laravel\Http\ApiResponse;
class PlanController extends Controller
{
use PlanDataTable;
// GET /plans/record/{id}
public function record($id) { ... }
// POST /plans/store
public function store(PlanRequest $request) { ... }
// GET /plans/record-active/{id}
public function recordActive($id)
{
$record = Plan::findOrFail($id);
return DialogAction::getActiveRecordActionData($record, 'name', 'plan');
}
// POST /plans/store-active
public function storeActive(ActionRequest $request)
{
$password = $request->input('password');
if ($password && !AuthHelper::checkPassword($password)) {
return ApiResponse::error('La contraseña es incorrecta', 403);
}
$record = Plan::findOrFail($request->input('id'));
$record->update(['is_active' => !$record->is_active]);
return ApiResponse::success($record->is_active ? 'Activado' : 'Desactivado');
}
// GET /plans/record-delete/{id}
public function recordDelete($id)
{
$record = Plan::findOrFail($id);
return DialogAction::getDeleteRecordActionData($record, 'name', 'plan');
}
// POST /plans/delete
public function storeDelete(ActionRequest $request)
{
$password = $request->input('password');
if ($password && !AuthHelper::checkPassword($password)) {
return ApiResponse::error('La contraseña es incorrecta', 403);
}
Plan::findOrFail($request->input('id'))->delete();
return ApiResponse::success('Plan eliminado');
}
}Genera la configuración del diálogo de confirmación que el frontend renderiza.
DialogAction::getDeleteRecordActionData(
record: $record,
nameField: 'name', // campo que muestra el nombre en el diálogo
type: 'plan', // texto del tipo de registro
verifyPassword: false // true = el frontend muestra campo de contraseña
);Retorna:
{
"title": "Eliminar plan",
"description": "¿Está seguro que desea eliminar el plan <strong>Básico</strong>?",
"button_label_submit": "Eliminar",
"button_color": "red",
"icon": "triangle-exclamation",
"icon_color": "red",
"verify_password": false
}DialogAction::getActiveRecordActionData($record, 'name', 'plan');Detecta el estado actual (is_active) y retorna el texto correspondiente (Activar / Desactivar).
Para acciones personalizadas:
DialogAction::getActionData(
title: 'Sincronizar datos',
description: '¿Desea sincronizar con SUNAT?',
options: ['button_color' => 'primary', 'verify_password' => true]
);FormRequest estándar para los endpoints storeDelete y storeActive.
Reglas de validación:
| Campo | Regla |
|---|---|
id |
required |
password |
nullable, required_if:verify_password,true |
use Quirosys\Datatable\Table\Column;
Column::make('name')->label('Nombre')
Column::make('price')->label('Precio')->alignRight()->width('100px')
Column::make('date')->label('Fecha')->alignCenter()
Column::isActive() // columna estándar de estado activo/inactivo
Column::actions() // columna estándar de botones de acciónuse Quirosys\Datatable\Table\ColumnBuilder;
$columns = (new ColumnBuilder())
->addColumn(Column::make('name')->label('Nombre'))
->addColumn(Column::isActive())
->addColumn(Column::actions())
->getColumns();use Quirosys\Datatable\Table\Filter;
Filter::makeInput('search')->label('Buscar')->cssClass('col-12')
Filter::makeSelect('status')->label('Estado')->options([
['id' => 'all', 'name' => 'Todos'],
['id' => 1, 'name' => 'Activo'],
['id' => 0, 'name' => 'Inactivo'],
])use Quirosys\Datatable\Table\FilterBuilder;
$filters = (new FilterBuilder())
->addFilter(Filter::makeInput('search')->label('Buscar')->cssClass('col-24 col-sm-12'))
->getFilters();use Quirosys\Datatable\Table\{Button, ButtonBuilder};
$buttons = (new ButtonBuilder())
->addButton(Button::newButton())
->addButton(Button::refreshButton())
->addButtonGroup([Button::exportButton()])
->getButtons();| Botón | Descripción |
|---|---|
Button::newButton() |
Abre el formulario de creación |
Button::refreshButton() |
Recarga la tabla |
Button::exportButton() |
Exporta a Excel |
Para tablas en el contexto del sistema central (base de datos central).
Para tablas dentro del contexto de un tenant.
use Quirosys\Datatable\Traits\PaginationSystemTrait;
// o
use Quirosys\Datatable\Traits\PaginationTenantTrait;Ambos proveen: updatePagination(), $perPage, $sortBy, $direction, $metaAdditional.
Exportador Excel reutilizable con título, encabezados estilizados y datos.
use Quirosys\Datatable\Exports\GenericReportExport;
use Maatwebsite\Excel\Facades\Excel;
public function exportRecords(Request $request)
{
$records = Plan::all()->map(fn($r) => [$r->name, $r->price])->toArray();
return Excel::download(
new GenericReportExport(
data: $records,
headings: ['Nombre', 'Precio'],
title: 'Reporte de Planes'
),
'reporte_planes.xlsx'
);
}Formato del Excel generado:
- Fila 1: Título centrado, negrita, tamaño 18, combinado en todas las columnas
- Fila 2: Encabezados con fondo azul
#1976d2, texto blanco, negrita - Filas de datos: Bordes grises, texto envuelto, columnas auto-dimensionadas