Last active
August 25, 2022 21:12
-
-
Save xavier83ar/0ab7c37bc057e81271501765e58d5d06 to your computer and use it in GitHub Desktop.
DataTables + CakePHP 2.x Ajax integración
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?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() ?> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?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; | |
| } | |
| } | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?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> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Funciona perfecto, muchas gracias!