Skip to content

Instantly share code, notes, and snippets.

@xavier83ar
Last active August 25, 2022 21:12
Show Gist options
  • Select an option

  • Save xavier83ar/0ab7c37bc057e81271501765e58d5d06 to your computer and use it in GitHub Desktop.

Select an option

Save xavier83ar/0ab7c37bc057e81271501765e58d5d06 to your computer and use it in GitHub Desktop.
DataTables + CakePHP 2.x Ajax integración
<?php
// este es View/Items/index.ctp cambié la extensión para que el gist reconozca el tipo de archivo
?>
<h2>Listado de Items</h2>
<!-- hacemos una tabla para preparar las columnas -->
<table id="data-table" class="display" cellspacing="0" width="100%">
<thead>
<tr>
<th>Acciones</th>
<th>ID</th>
<th>Campo Uno</th>
<th>Campo Dos</th>
<th>Campo Tres</th>
<th>Related Campo Uno</th>
<th>Related Campo Dos</th>
</tr>
</thead>
<tfoot>
<tr>
<th>Acciones</th>
<th>ID</th>
<th>Campo Uno</th>
<th>Campo Dos</th>
<th>Campo Tres</th>
<th>Related Campo Uno</th>
<th>Related Campo Dos</th>
</tr>
</tfoot>
<tbody></tbody>
</table>
<?php $this->start('script') // para que se renderize en el layout ?>
<script type="application/javascript">
(function ($) {
$(function () {
var $htmlTable = $("#data-table");
// agregamos inputs de búsqueda del footer
$htmlTable.find('tfoot th').each(function () {
var title = $htmlTable.find('thead th').eq($(this).index()).text();
$(this).html('<input type=text placeholder=' + title + '>');
});
// instanciamos datatable
var dataTable = $htmlTable.DataTable({
"scrollX": true,
"dom": "Tlfrtip",
"order": [[2, 'desc'], [3, 'asc'], [5, 'asc']], // establecemos el ordenamiento por defecto
"tableTools": {
"sSwfPath": '<?= $this->Html->url("/plugins/DataTables-1.10.7/extensions/TableTools/swf/copy_csv_xls_pdf.swf") ?>', // opcional
"aButtons": []
},
// acá indicamos que la búsqueda es por ajax, y la url
"serverSide": true,
"processing": true,
"ajax": "<?= $this->Html->url([
'action' => 'ajax_search'
])?>",
"columns": [
{
"searchable": false,
"data": null,
"render": function (data, type, full, meta) {
// definimos los botones como templates con el helper de cake para tener correctamente las url's
var _template = '<?php
echo $this->Html->link('<i class="fa fa-plus"></i> Ver', ['action' => 'view', '__id__'], ['class' => 'btn btn-info btn-xs']) . " ";
echo $this->Html->link('<i class="fa fa-pencil"></i> Editar', ['action' => 'edit', '__id__'], ['class' => 'btn btn-info btn-xs']);
?>';
// esto ejecuta para cada fila generando los links correctamente reemplazando el id de cada item
return _template.replace(new RegExp('__id__', 'g'), data[1]);
}
}, // la primera (0) columan es para las acciones, no se puede buscar y definimos como se renderiza para mostrar botones
null, null, null, null, null, null // para las columnas restantes no definimos nada especial, prestar atención a la cantidad de columnas
]
});
$.fn.dataTable.ext.errMode = 'none';
$htmlTable.on('error.dt', function (e, settings, techNote, message) {
window.location.href = '<?= $this->Html->url() ?>';
});
// Búsqueda en los inputs del footer
dataTable.columns().every(function () {
var _self = this;
$('input', this.footer()).on('keyup change', function () {
_self.search(this.value).draw();
});
});
})
})(jQuery)
</script>
<?php $this->end() ?>
<?php
// controller file
class ItemsController {
public function index()
{
// nada que hacer, solo renderizar la vista
}
public function ajax_search()
{
// acá hay que mapear la posición de la columna en la tabla con el campo que representa
$_columnsMap = [
// 0 => '' en este caso no tengo posición 0 porque esa columna se usa para mostrar las acciones y no datos
1 => 'Item.id',
2 => 'Item.campo_uno',
3 => 'Item.campo_dos',
4 => 'Item.campo_tres',
5 => 'RelatedItem.campo_uno', // si consultamos sobre algun modelo relacionado
6 => 'RelatedItem.campo_dos',
];
// preparamos los parámetros de la consulta, esta parte depende de las necesidades de cada uno
$query = [
'fields' => [
'Item.*', 'RelatedItem.*'
],
'recursive' => 0,
'conditions' => [],
'order' => [],
// datatables manda los parámetros de página y cantidad de registros en el query: start y length
'offset' => intval($this->request->query('start')), // start tiene la página
'limit' => intval($this->request->query('length')), // length la cantidad de registros
'joins' => [
[
'table' => 'related_items',
'alias' => 'RelatedItems',
'type' => 'INNER',
'conditions' => ['Item.id = RelatedItem.item_id']
], // un ejemplo de join, en caso que queremos buscar sobre más de una tabla
]
];
// ordenamiento, revisamos si datatable establece algún orden particular, puede ser por más de una columna
// datatables usa el índice númerico de la columna por la que se ordena, usamos el columnsMap para obtener el campo
foreach ($this->request->query('order') as $order) {
$query['order'][$_columnsMap[intval($order['column'])]] = $order['dir'];
}
// busqueda global, en este parámetro del request->query tenemos lo que se haya ingresado en el campo de busqueda
// general del datatable, el campo común de búsqueda, configurar la búsqueda acá según las necesidades de cada uno
if ($this->request->query('search.value')) {
$val = $this->request->query('search.value');
$query['conditions'] += ['or' => [
'Item.campo_uno' => $val,
'Item.campo_dos LIKE' => "%{$val}%",
'RelatedItem.campo_uno' => "$val",
]];
}
// busqueda por columna, ahora vamos a ver si hay criterios de búsqueda por cada columna en particular
// esto es si se usaron los inputs de búsquedas de cada columna en particular
foreach ($this->request->query('columns') as $column) {
// busqueda
if (!array_key_exists(intval($column['data']), $_columnsMap) ||
$column['searchable'] !== 'true' || empty($column['search']['value'])
) {
continue;
}
$colName = $_columnsMap[intval($column['data'])];
$search = $column['search']['value'];
// el modo de búsqueda dependerá de los tipos de datos y las necesidades de cada uno, adaptar a cada caso
switch ($colName) {
case 'Item.campo_uno':
$query['conditions']['Item.campo_uno'] = $search;
break;
case 'Item.campo_dos':
$query['conditions']['Item.campo_dos LIKE'] = "{$search}%";
break;
case 'RelatedItem.campo_uno':
$query['conditions']['RelatedItem.campo_uno'] = "{$search}";
break;
}
}
$items = $this->Item->find('all', $query);
// hacemos esto para obtener la cuenta total de registros filtrados
unset($query['offset'], $query['limit']);
$countFiltered = $this->Item->find('count', $query);
// cuenta total de registros, para informar al datatables
$countTotal = $this->Item->find('count', ['recursive' => -1]);
$data = [];
foreach ($items as $item) {
// tenemos que armar los datos para enviarlos al datatables, es importante que coincida exactamente
// con el $_columnsMap
$data[] = [
'--', // primera columna, no la usamos para datos
$item['Item']['id'],
$item['Item']['campo_uno'],
$item['Item']['campo_dos'],
number_format($item['Item']['campo_tres'], 0, ',', '.'), // si querés formatear algún campo, debe hacer aquí
$item['RelatedItem']['campo_uno'],
$item['RelatedItem']['campo_dos'],
];
}
$response = [
'draw' => intval($this->request->query('draw')),
'recordsTotal' => $countTotal,
'recordsFiltered' => $countFiltered,
'data' => $data
];
$this->response->type('json');
$this->response->body(json_encode($response));
return $this->response;
}
}
<?php
// ejemplo resumido del layout, al menos para ver las inclusiones de los archivos y las versiones utilizadas
?>
<!DOCTYPE html>
<html>
<head>
<title>Ejemplo</title>
<?= $this->Html->css( [
'/plugins/font-awesome-4.2.0/css/font-awesome.min',
'/plugins/DataTables-1.10.7/media/css/jquery.dataTables.min',
'/plugins/DataTables-1.10.7/media/css/jquery.dataTables_themeroller',
'/plugins/DataTables-1.10.7/extensions/TableTools/css/dataTables.tableTools',
] ); ?>
<?= $this->fetch( 'css' ); ?>
</head>
<body>
<div class="container">
<?= $this->Session->flash( 'flash', ['element' => 'flash'] ); ?>
<?= $this->Session->flash( 'auth', ['element' => 'flash'] ); ?>
<?= $this->fetch( 'content' ); ?>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="<?= $this->Html->url('/js/jquery-1.11.1.min.js') ?>"><\/script>')</script>
<?= $this->Html->script( [
'/plugins/DataTables-1.10.7/media/js/jquery.dataTables',
'/plugins/DataTables-1.10.7/extensions/TableTools/js/dataTables.tableTools',
] ) ?>
<?= $this->fetch( 'script' ); ?>
</body>
</html>
@xavier83ar
Copy link
Author

xavier83ar commented Sep 28, 2017

Es un ejemplo genérico como para mostrar detalles de la integración. Lo he implementado y está funcionando en algunos proyectos, pero para este ejemplo utilicé un model genérico Item, cada uno lo adaptará a sus necesidades, no está pensado para usar así como está, sino para que sirva de referencia. Está hecho con CakePHP 2.x

@oscar-ol
Copy link

oscar-ol commented Dec 3, 2017

Funciona perfecto, muchas gracias!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment