¿Por qué Server-Side Processing?
Cuando tu tabla tiene más de 1,000 registros, cargar todos los datos en el navegador se vuelve lento e ineficiente. DataTables Server-Side solo carga los datos necesarios para la página actual, haciendo tu aplicación mucho más rápida.
💡 Ventajas del Server-Side
- Carga inicial instantánea
- Bajo consumo de memoria en cliente
- Búsqueda y ordenación en servidor
- Escalable a millones de registros
Arquitectura de 3 Capas
La implementación requiere tres componentes que trabajan juntos:
📄 1. Vista (HTML + JS)
Define la estructura de la tabla y configura DataTables para usar AJAX.
🎮 2. Controlador (PHP)
Endpoint que recibe parámetros de DataTables y devuelve JSON.
🗄️ 3. Modelo (PHP)
Consulta la base de datos con filtros, paginación y ordenación.
Paso 1: La Vista
Crea una tabla HTML con los headers y un script que inicialice DataTables:
<!-- views/productos/list.php -->
<table class="table table-striped" id="tabla-productos">
<thead>
<tr>
<th><?php echo _l('producto_id'); ?></th>
<th><?php echo _l('producto_nombre'); ?></th>
<th><?php echo _l('producto_precio'); ?></th>
<th><?php echo _l('producto_stock'); ?></th>
<th><?php echo _l('acciones'); ?></th>
</tr>
</thead>
<tbody></tbody>
</table>
<script>
$(function() {
initDataTable('#tabla-productos', admin_url + 'mi_modulo/productos_table', [
// Columna 0: ID
{
data: 'id',
name: 'id',
width: '5%'
},
// Columna 1: Nombre (con enlace)
{
data: 'nombre',
name: 'nombre',
render: function(data, type, row) {
return '<a href="' + admin_url + 'mi_modulo/producto/' + row.id + '">' + data + '</a>';
}
},
// Columna 2: Precio (formateado)
{
data: 'precio',
name: 'precio',
render: function(data) {
return format_money(data);
}
},
// Columna 3: Stock (con badge)
{
data: 'stock',
name: 'stock',
render: function(data) {
var badge = data > 10 ? 'success' : data > 0 ? 'warning' : 'danger';
return '<span class="badge bg-' + badge + '">' + data + '</span>';
}
},
// Columna 4: Acciones
{
data: 'acciones',
name: 'acciones',
orderable: false,
searchable: false
}
]);
});
</script>
Paso 2: El Controlador
Crea un método que procese la petición AJAX y devuelva los datos en el formato que DataTables espera:
<?php
// controllers/Mi_modulo.php
public function productos_table()
{
if (!$this->input->is_ajax_request()) {
show_404();
}
$this->load->model('productos_model');
// Obtener datos con el helper de Perfex
$aColumns = [
'id',
'nombre',
'precio',
'stock'
];
$sIndexColumn = 'id';
$sTable = db_prefix() . 'mi_modulo_productos';
$result = data_tables_init(
$aColumns,
$sIndexColumn,
$sTable,
[], // joins
[], // where
[ // Condiciones adicionales
'activo' => 1
]
);
$output = $result['output'];
$rResult = $result['rResult'];
foreach ($rResult as $aRow) {
$row = [];
$row[] = $aRow['id'];
$row[] = $aRow['nombre'];
$row[] = app_format_money($aRow['precio'], '');
$row[] = $aRow['stock'];
// Columna de acciones
$acciones = '';
if (has_permission('mi_modulo', '', 'edit')) {
$acciones .= '<a href="' . admin_url('mi_modulo/producto/' . $aRow['id']) . '" class="btn btn-sm btn-default">';
$acciones .= '<i class="fa fa-pencil"></i></a> ';
}
if (has_permission('mi_modulo', '', 'delete')) {
$acciones .= '<a href="' . admin_url('mi_modulo/eliminar_producto/' . $aRow['id']) . '" class="btn btn-sm btn-danger _delete">';
$acciones .= '<i class="fa fa-trash"></i></a>';
}
$row[] = $acciones;
$output['aaData'][] = $row;
}
echo json_encode($output);
}
Paso 3: Filtros Avanzados
Añade filtros personalizados que se envían junto con la petición de DataTables:
// En la vista - añadir filtros
<div class="row mbot15">
<div class="col-md-3">
<select id="filtro-categoria" class="selectpicker">
<option value="">Todas las categorías</option>
<?php foreach($categorias as $cat): ?>
<option value="<?php echo $cat['id']; ?>"><?php echo $cat['nombre']; ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-3">
<select id="filtro-stock" class="selectpicker">
<option value="">Todo el stock</option>
<option value="disponible">Con stock</option>
<option value="bajo">Stock bajo</option>
<option value="agotado">Agotado</option>
</select>
</div>
</div>
<script>
// Configuración con filtros
var tablaProductos = initDataTable('#tabla-productos', admin_url + 'mi_modulo/productos_table', columnas, columnas, 'undefined', [0, 'desc'], {
// Enviar filtros con cada petición
ajax: {
url: admin_url + 'mi_modulo/productos_table',
type: 'POST',
data: function(d) {
d.categoria = $('#filtro-categoria').val();
d.stock = $('#filtro-stock').val();
}
}
});
// Recargar tabla cuando cambian filtros
$('#filtro-categoria, #filtro-stock').on('change', function() {
tablaProductos.ajax.reload();
});
</script>
Procesar Filtros en el Controlador
// En el controlador, antes de data_tables_init()
$where = [];
// Filtro de categoría
if ($this->input->post('categoria')) {
$where[] = 'AND categoria_id = ' . $this->db->escape($this->input->post('categoria'));
}
// Filtro de stock
$stock_filter = $this->input->post('stock');
if ($stock_filter) {
switch ($stock_filter) {
case 'disponible':
$where[] = 'AND stock > 0';
break;
case 'bajo':
$where[] = 'AND stock BETWEEN 1 AND 10';
break;
case 'agotado':
$where[] = 'AND stock = 0';
break;
}
}
$result = data_tables_init(
$aColumns,
$sIndexColumn,
$sTable,
[], // joins
$where, // condiciones WHERE adicionales
['activo' => 1]
);
✅ Resultado Final
Ahora tienes una tabla que carga datos de forma eficiente, con búsqueda, ordenación y filtros personalizados, todo procesado en el servidor. Esta implementación puede manejar cientos de miles de registros sin problemas de rendimiento.