Grillas de Datos KTDataTable Server-Side
Para soportar tablas con millones de registros sin penalizar la memoria del navegador, Mañuti prohíbe el renderizado estático de bucles @foreach en listados administrativos. Estandariza la arquitectura KTDataTable Server-Side alimentada por APIs JSON.
1. El Patrón de Controlador API
El controlador implementa un método data(Request $request) que resuelve búsqueda, ordenamiento y paginación a nivel de motor de base de datos:
// app/Http/Controllers/Admin/UserController.php
public function data(Request $request)
{
$query = Usuario::with(['roles', 'estatus']);
// 1. Búsqueda Global Servidor
if ($search = $request->input('search')) {
$query->where(function ($q) use ($search) {
$q->where('nombres', 'like', "%{$search}%")
->orWhere('correo', 'like', "%{$search}%")
->orWhere('usuario', 'like', "%{$search}%");
});
}
// 2. Ordenamiento Dinámico
$sortField = $request->input('sortField', 'created_at');
$sortOrder = $request->input('sortOrder', 'desc');
$query->orderBy($sortField === 'null' ? 'created_at' : $sortField, $sortOrder);
// 3. Paginación en Base de Datos
$perPage = (int) $request->input('pageSize', 10);
$users = $query->paginate($perPage);
// 4. Transformación de Filas
$data = $users->getCollection()->transform(function ($u) {
return [
'id' => $u->id,
'name' => $u->nombres . ' ' . $u->apellido_paterno,
'email' => $u->correo,
'roles' => $u->roles->map(fn($r) => ['name' => $r->name]),
'status' => $u->estatus?->etiqueta ?? 'Sin estatus',
'created_at' => $u->created_at->format('d/m/Y'),
];
});
return response()->json([
'data' => $data,
'totalCount' => $users->total(),
'page' => $users->currentPage(),
'pageSize' => $users->perPage(),
'totalPages' => $users->lastPage()
]);
}
2. Consumo en Frontend y Búsqueda por Enter
El script de frontend consume el endpoint desacoplado de Blade, evitando saturar el servidor en cada pulsación de tecla ejecutando la búsqueda exclusivamente al presionar Enter:
// resources/js/admin/users.js
var datatable = new KTDataTable(datatableEl, {
apiEndpoint: configEl.dataset.apiUrl,
requestMethod: 'GET',
search: { delay: 400, key: 'search' },
mapResponse: (response) => response,
columns: {
name: {
title: 'Usuario',
render: (value, row) => `
<div class="flex items-center gap-3">
<span class="font-bold text-gray-800">${value}</span>
<span class="text-xs text-gray-400">${row.email}</span>
</div>`
},
// ...
},
pageSize: 10
});
// Listener exclusivo de tecla Enter
searchInput.addEventListener('keyup', function (e) {
if (e.key === 'Enter') {
datatable.search(e.target.value);
}
});