En este artículo vamos a centrarnos en una operativa que resulta, en muchos casos, imprescindible. Se trata de modificar el script secundario para que, el resultado devuelto, incluya ciertos contenidos que, en principio, no están en la base de datos, pero que se basan en los que sí están. A ver, para entendernos. En un artículo anterior de esta serie aprendimos a modificar, en el script secundario, la fecha de ingreso, ya que, en la base de datos, se encuentra en formato
aaaa-mm-dd
(ISO 8601
), que es el habitual para manejo interno, y debíamos obtenerla en formato dd-mm-aaaa
, para que fuera legible para el usuario. En aquel momento ya nos estábamos asomando a un preprocesado de los datos antes de retornarlos al plugin DataTables del script primario. En este artículo vamos a ir un poco más allá, para seguir aprendiendo cosas que podemos hacer con este plugin.
PRERREQUISITOS
Esta vez necesitaremos contar, además de las herramientas que hemos venido usando (jQuery, jQuery UI y bootstrap) el juego de iconos de font awesome. Se trata de una colección de iconos muy práctica, que podemos incluir en nuestros diseños web. Si no estás familiarizado con font awesome, te recomiendo echar un vistazo a su web oficial (por cierto, son iconos de uso libre y gratuito). Nosotros cargaremos la biblioteca de iconos mediante CDN, por comodidad. La URL del CDN es https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css
. Por supuesto, si lo deseas, en tu proyecto puedes descargar la biblioteca e integrarla, pero en este caso no lo haremos.
LO QUE VAMOS A HACER
Eso es lo primero de todo, claro: determinar lo que vamos a hacer. En este ejemplo vamos a añadir una columna más a la tabla, a la que llamaremos Operaciones
. En cada fila, esta última casilla tendrá tres iconos para enviar un correo electrónico a la persona de esa fila, eliminarla de la lista, o ponerla una marca especial. Las operativas de estas funciones no las vamos a implementar. Por una parte, en la base de datos no tenemos correos para enviar emails, ni vamos a andar borrando o modificando datos. Lo único que queremos es ver como podemos usar DataTables para crear estos iconos y enlazarlos a funciones JavaScript que neceitemos. Luego ya sería, llegado el caso, cuestión de establecer lo que harían esas funciones.
La tabla quedará con un aspecto similar al que ves a continuación (fíjate especialmente en la columna de la derecha).
EL SCRIPT PRIMARIO
Este script sufre algunas modificaciones necesarias. Vemos el listado en articulo_09.php
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Uso de DataTables</title> <!-- El CSS de DataTables --> <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/dt/jszip-2.5.0/pdfmake-0.1.18/dt-1.10.12/af-2.1.2/b-1.2.2/b-colvis-1.2.2/b-flash-1.2.2/b-html5-1.2.2/b-print-1.2.2/cr-1.3.2/fc-3.2.2/fh-3.1.2/kt-2.1.3/r-2.1.0/rr-1.1.2/sc-1.4.2/se-1.2.0/datatables.min.css"/> <!-- El CSS de Bootstrap --> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> <!--<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css">--> <!-- El CSS de Font Awesome --> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"> </head> <body> <div class="container"> <br> <div class="row"> <div class="col-sm-12"> <table id="tabla_de_personal" class="table display table-striped table-bordered"> <thead> <tr> <th>NOMBRE</th> <th>APELLIDO</th> <th>CARGO</th> <th>CIUDAD</th> <th>INGRESO</th> <th>SALARIO</th> <th>OPERACIONES</th> </tr> </thead> <tbody> </tbody> </table> </div> </div> </div> <!-- jQuery --> <script language="javascript" src="https://code.jquery.com/jquery-3.1.1.min.js"></script> <!-- El JavaScript de DataTables --> <script language="javascript" type="text/javascript" src="https://cdn.datatables.net/v/dt/jszip-2.5.0/pdfmake-0.1.18/dt-1.10.12/af-2.1.2/b-1.2.2/b-colvis-1.2.2/b-flash-1.2.2/b-html5-1.2.2/b-print-1.2.2/cr-1.3.2/fc-3.2.2/fh-3.1.2/kt-2.1.3/r-2.1.0/rr-1.1.2/sc-1.4.2/se-1.2.0/datatables.min.js"></script> <!-- El JavaScript de BootStrap --> <script language="javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> <!-- El código JavaScript --> <script language="javascript"> var objetoDataTables_personal = $('#tabla_de_personal').DataTable({ "language": { "emptyTable": "No hay datos disponibles en la tabla.", "info": "Del _START_ al _END_ de _TOTAL_ ", "infoEmpty": "Mostrando 0 registros de un total de 0.", "infoFiltered": "(filtrados de un total de _MAX_ registros)", "infoPostFix": "(actualizados)", "lengthMenu": "Mostrar _MENU_ registros", "loadingRecords": "Cargando...", "processing": "Procesando...", "search": "Buscar:", "searchPlaceholder": "Dato para buscar", "zeroRecords": "No se han encontrado coincidencias.", "paginate": { "first": "Primera", "last": "Última", "next": "Siguiente", "previous": "Anterior" }, "aria": { "sortAscending": "Ordenación ascendente", "sortDescending": "Ordenación descendente" } }, "lengthMenu": [[5, 10, 20, 25, 50, -1], [5, 10, 20, 25, 50, "Todos"]], "iDisplayLength": 10, "bServerSide": true, "sAjaxSource": "datos_externos_09.php", "columns" : [ {"data": 0}, {"data": 1}, {"data": 2}, {"data": 3}, {"data": 4}, {"data": 5}, {"data": 6, 'orderable' : false} ], }); $('label').addClass('form-inline'); $('select, input[type="search"]').addClass('form-control input-sm'); function operaciones(id, op){ var mensaje = "El id es " + id + ", y la operación es " + op; alert (mensaje); } </script> </body> </html> |
En primer lugar vemos, en la línea 14
, que hemos cargado el CDN de Font Awesome, para contar con los iconos necesarios:
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
En la línea 28
vemos que le hemos añadido una cabecera más a la tabla HTML, para la última columna de la derecha:
<th>OPERACIONES</th>
En la línea 84
hemos añadido al objeto DataTables la definición de estaa última columna, con indicación expresa de ue no será ordenable, ya que solo contendrá iconos, y no tiene ningún sentido que lo sea:
{"data": 6, 'orderable' : false}
Por último, hemos incluido una pequeña función JavaScript que nos servirá para comprobar, cuando pulsemos los iconos de la columna de la derecha, comprobar que se reconoce la pulsación y que, en base a esta, podríamos programar las funcionalidades requeridas. Líneas de la 90
a la 93
:
function operaciones(id, op){
var mensaje = "El id es " + id + ", y la operación es " + op;
alert (mensaje);
}
EL SCRIPT SECUNDARIO
Este script también sufre algunas modificaciones. El listado completo es datos_externos_09.php
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
<?php // Establecemos la codificacion para las llamadas y respuestas HTTP mb_internal_encoding ('UTF-8'); /* CREAMOS LA CONEXION A LA BASE DE DATOS, O BIEN LA IMPORTAMOS DESDE UN ARCHIVO EXTERNO DE CONFIGURACION. */ $conexion = new PDO('mysql:host=localhost;dbname=datatables;charset=UTF8', 'root', ''); $conexion->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); /* RECUPERAMOS TODOS LOS PARAMETROS DE $_GET. LOS QUE NO APAREZCAN EN LA CONSULTA RECIBIRAN UN VALOR PREDETERMINADO. ESTOS DATOS SON LOS QUE SE RECIBEN CADA VEZ QUE EL PLUGIN DATATABLES LLAMA A ESTE SCRIPT. */ $datosDeLlamada = $_GET; /* SE INDICA(N) LA(S) TABLA(S) QUE SE VA(N) A USAR EN LA CONSULTA. */ $tablasDeBBDD = array( 'personal', 'ciudades', 'cargos' ); /* SE DEFINE LA LISTA DE COLUMNAS QUE SE DEVOLVERON PARA SER MOSTRADAS EN LA TABLA HTML. LOS NOMBRES DE ESTAS COLUMNAS DEBEN COINCIDIR CON LOS DE LAS COLUMNAS DE LA(S) TABLA(S) AFECTADA(S) POR LA CONSULTA. */ $columnasParaRetorno = array( $tablasDeBBDD[0].'.nombre', $tablasDeBBDD[0].'.apellido', $tablasDeBBDD[2].'.cargo', $tablasDeBBDD[1].'.ciudad', $tablasDeBBDD[0].'.fecha_de_ingreso', $tablasDeBBDD[0].'.salario_bruto_anual', $tablasDeBBDD[0].'.id' ); $numeroDeColumnas = count($columnasParaRetorno); //////////////////////////////////////////////// REGLAS DE FILTRADO //////////////////////////// /* PREPARAMOS EL FILTRADO POR COLUMNAS PARA LA CAJA DE BUSQUEDA */ $reglasDeFiltradoDeUsuario = array (); if (isset($datosDeLlamada['sSearch']) && $datosDeLlamada['sSearch'] !== "") { for($i = 0; $i < $numeroDeColumnas; $i++) { if (isset ($datosDeLlamada['bSearchable_'.$i]) && $datosDeLlamada['bSearchable_'.$i] == 'true') { $reglasDeFiltradoDeUsuario[] = $columnasParaRetorno[$i]." LIKE '%".addslashes($datosDeLlamada['sSearch'])."%'"; } } } if (!empty($reglasDeFiltradoDeUsuario)){ $reglasDeFiltradoDeUsuario = ' ('.implode(" OR ", $reglasDeFiltradoDeUsuario).') '; } else { $reglasDeFiltradoDeUsuario = ''; } /* PREPARAMOS LAS REGLAS DE FILTRADO PARA LAS BUSQUEDAS PROGRAMADAS POR METODO .search() (BÚSQUEDAS PERSONALIDAS DEL USUARIO). */ $reglasDeBusquedaPorProgramacion = array(); for($i = 0; $i < $numeroDeColumnas; $i++) { if (isset($datosDeLlamada['sSearch_'.$i]) && $datosDeLlamada['sSearch_'.$i] !== ""){ $reglasDeBusquedaPorProgramacion[] = $columnasParaRetorno[$i].$datosDeLlamada['sSearch_'.$i]; } } if (!empty($reglasDeBusquedaPorProgramacion)){ $reglasDeBusquedaPorProgramacion = ' ('.implode(" AND ", $reglasDeBusquedaPorProgramacion).') '; } else { $reglasDeBusquedaPorProgramacion = ''; } /* PREPARAMOS LAS REGLAS DE FILTRADO DE RELACIONES ENTRE TABLAS. ESTAS SE PROGRAMAN AQUI A MANO, PORQUE PUEDEN EXISTIR O NO, DEPENDIENDO DE QUE SE USE UNA TABLA O MAS DE UNA. */ $reglasDeFiltradoDeRelaciones = ''; $reglasDeFiltradoDeRelaciones .= " (".$tablasDeBBDD[1].".id = ".$tablasDeBBDD[0].".id_ciudad "; $reglasDeFiltradoDeRelaciones .= "AND ".$tablasDeBBDD[2].".id = ".$tablasDeBBDD[0].".id_cargo) "; /* SE COMPONE TODA LA REGLA DE FILTRADO. EN ESTE CASO INCLUYE LAS CLAÚSULAS DE BÚSQUEDA, Y LAS RELACIONES ENTRE TABLAS. SIGUE SIENDO UN EJEMPLO SIMPLE, PERO MÁS ELABORADO QUE EL ANTERIOR. MÁS ADELANTE VEREMOS OTROS USOS. LO IMPORTANTE AQUI ES QUE, ADEMÁS DE LAS CLAUSULAS DE BÚSQUEDA (VARIABLE $reglasDeFiltradoDeUsuario, QUE PUEDEN EXISTIR O NO) TAMBIÉN HAY UNA CLAÚSULA DE RELACIONES ENTRE LAS TABLAS. SI HAY MÁS DE UNA TABLA SIEMPRE HABRÁ UNA CLAÚSULA DE RELACIONES ($reglasDeFiltradoDeRelaciones). LAS IMPLEMENTAMOS COMO UNA MATRIZ PARA PODER COMPROBAR LAS QUE EXISTEN Y LAS QUE NO, Y LUEGO LAS UNIMOS CON EL OPERADOR AND, SI HAY MÁS DE UNA CLAÚSULA DE FILTRADO. */ $reglasDeFiltrado = array(); if ($reglasDeFiltradoDeUsuario > '') $reglasDeFiltrado[] = $reglasDeFiltradoDeUsuario; if ($reglasDeFiltradoDeRelaciones > '') $reglasDeFiltrado[] = $reglasDeFiltradoDeRelaciones; if ($reglasDeBusquedaPorProgramacion > '') $reglasDeFiltrado[] = $reglasDeBusquedaPorProgramacion; $reglasDeFiltrado = implode(" AND ", $reglasDeFiltrado); //////////////////////////////////////////// FIN DE REGLAS DE FILTRADO /////////////////////////// /////////////////////////// REGLAS DE ORDENACION //////////////////////// /* SE COMPONE LA REGLA DE ORDENACION. */ $reglasDeOrdenacion = array (); if (isset($datosDeLlamada['iSortCol_0'] )) { $columnasDeOrdenacion = intval($datosDeLlamada['iSortingCols']); for($i = 0; $i < $columnasDeOrdenacion; $i ++) { if ($datosDeLlamada['bSortable_'.intval($datosDeLlamada['iSortCol_'.$i])] == 'true') { $reglasDeOrdenacion [] = $columnasParaRetorno[intval($datosDeLlamada['iSortCol_'.$i])].($datosDeLlamada['sSortDir_'.$i] === 'asc'?' asc':' desc'); } } } if (!empty($reglasDeOrdenacion)) { $reglasDeOrdenacion = " ORDER BY ".implode(", ", $reglasDeOrdenacion); } else { $reglasDeOrdenacion = ""; } /* SE COMPONE LA REGLA DE LIMITACION DE REGISTROS. */ $reglaDeLimitacion = ($datosDeLlamada['iDisplayLength'] > 0)?' LIMIT '.$datosDeLlamada['iDisplayStart'].', '.$datosDeLlamada['iDisplayLength'].';':';'; /////////////////////////////////////// FIN DE REGLAS DE ORDENACION //////////////////// /* SE PREPARA LA CONSULTA DE RECUPERACION DEL DATASET SOLICITADO. */ $consulta = "SELECT ".implode(', ', $columnasParaRetorno)." "; $consulta .= "FROM ".implode(', ', $tablasDeBBDD)." "; $consulta .= "WHERE 1 "; if ($reglasDeFiltrado > "") $consulta .= "AND (".$reglasDeFiltrado.") "; $consulta .= $reglasDeOrdenacion; $consulta .= $reglaDeLimitacion; $hacerConsulta = $conexion->prepare($consulta); $hacerConsulta->execute(); $DataSet = $hacerConsulta->fetchAll(PDO::FETCH_ASSOC); $hacerConsulta->closeCursor(); /* SI ES NECESARIO ADAPTAR ALGUN DATO PARA PRESENTACION, SE ADAPTA AQUI. SI ES NECESARIOS AGREGAR ENLACES, REFERENCIAS A IMAGENES, O CUALQUIER OTRA COSA, SE INCLUYE EN ESTA SECCION. EN ESTE CASO, LO ÚNICO QUE VAMOS A HACER ES DARLE FORMATO AL SALARIO ANUAL. */ foreach ($DataSet as $keyDL=>$DL){ $DataSet[$keyDL]['fecha_de_ingreso'] = date("d-m-Y", strtotime($DL['fecha_de_ingreso'])); $DataSet[$keyDL]['salario_bruto_anual'] = number_format($DL['salario_bruto_anual'], 2, ",", ".").' €'; $cadenaDeColumnaFinal = ""; $cadenaDeColumnaFinal .= "<div class='text-center'>"; $cadenaDeColumnaFinal .= "<a href='javascript:operaciones("".$DL["id"]."", "Correo");' title='Correo'>"; $cadenaDeColumnaFinal .= "<i class='fa fa-envelope'></i></a> "; $cadenaDeColumnaFinal .= "<a href='javascript:operaciones("".$DL["id"]."", "Borrar");' title='Borrar'>"; $cadenaDeColumnaFinal .= "<i class='fa fa-trash-o'></i></a> "; $cadenaDeColumnaFinal .= "<a href='javascript:operaciones("".$DL["id"]."", "Borrar");' title='Marcar'>"; $cadenaDeColumnaFinal .= "<i class='fa fa-thumb-tack'></i></a>"; $cadenaDeColumnaFinal .= "</div>"; $DataSet[$keyDL]["id"] = $cadenaDeColumnaFinal; } /* CALCULO DE DATOS INFORMATIVOS DE REGISTROS. */ $numeroDeRegistrosDelDataSet = count($DataSet); /* CALCULO DEL TOTAL DE REGISTROS QUE CUMPLEN LAS REGLAS DE FILTRADO SIN ORDENACION NI LIMITACION. */ $consulta = "SELECT COUNT(".$columnasParaRetorno[0].") "; $consulta .= "FROM ".implode(', ', $tablasDeBBDD)." "; $consulta .= "WHERE 1 "; if ($reglasDeFiltrado > "") $consulta .= "AND (".$reglasDeFiltrado.") "; $hacerConsulta = $conexion->prepare($consulta); $hacerConsulta->execute(); $totalDeRegistrosConFiltrado = $hacerConsulta->fetch(PDO::FETCH_NUM)[0]; $hacerConsulta->closeCursor(); /* TOTAL DE REGISTROS DE LA TABLA PRIMARIA (O UNICA, SI SOLO HAY UNA). */ $consulta = "SELECT COUNT(".$columnasParaRetorno[0].") "; $consulta .= "FROM ".$tablasDeBBDD[0].";"; $hacerConsulta = $conexion->prepare($consulta); $hacerConsulta->execute(); $totalDeRegistrosEnBruto = $hacerConsulta->fetch(PDO::FETCH_NUM)[0]; $hacerConsulta->closeCursor(); // SE PREPARA UNA MATRIZ CON TODOS LOS DATOS NECESARIOS PARA LA SALIDA. $matrizDeSalida = array( "sEcho" => intval($datosDeLlamada['sEcho']), "iTotalRecords" => strval($totalDeRegistrosEnBruto), "iTotalDisplayRecords" => strval($totalDeRegistrosConFiltrado), "aaData" => array () ); foreach ($DataSet as $DL){ $registro = array(); foreach ($DL as $dato) $registro[] = $dato; $matrizDeSalida['aaData'][] = $registro; unset($registro); } $salidaDeDataSet = json_encode ($matrizDeSalida, JSON_HEX_QUOT); /* SE DEVUELVE LA SALIDA */ echo $salidaDeDataSet; ?> |
Cómo los iconos van a actuar sobre el registro de la persona a cuya derecha aparecen, debemos conocer el id de cada registro de la tabla. Así, en la línea 33
hemos añadido que se lea este campo:
$tablasDeBBDD[0].'.id'
Sin embargo, no queremos que en la columna en la tabla aparezca el id, sino un contenido que se diseñará en base a este id. En las líneas 133
a 143
hemos modificado el contenido para la última columna, que es el id del registro:
1 2 3 4 5 6 7 8 9 10 11 |
$cadenaDeColumnaFinal = ""; $cadenaDeColumnaFinal .= "<div class='text-center'>"; $cadenaDeColumnaFinal .= "<a href='javascript:operaciones("".$DL["id"]."", "Correo");' title='Correo'>"; $cadenaDeColumnaFinal .= "<i class='fa fa-envelope'></i></a> "; $cadenaDeColumnaFinal .= "<a href='javascript:operaciones("".$DL["id"]."", "Borrar");' title='Borrar'>"; $cadenaDeColumnaFinal .= "<i class='fa fa-trash-o'></i></a> "; $cadenaDeColumnaFinal .= "<a href='javascript:operaciones("".$DL["id"]."", "Borrar");' title='Marcar'>"; $cadenaDeColumnaFinal .= "<i class='fa fa-thumb-tack'></i></a>"; $cadenaDeColumnaFinal .= "</div>"; $DataSet[$keyDL]["id"] = $cadenaDeColumnaFinal; |
Cómo ves, hemos usado referencias a los iconos de Font Awesome, para que luego, en la tabla renderizada por el script primario, se muestren estos. Hemos creado, en base a estos iconos, unos enlaces que llaman a la función JavaScript que habíamos preparado al respecto. La pasan, como argumentos, el id del registro y el nombre de la operación a realizar.
Una vez construida la cadena con los enlaces, sustituimos el contenido original de la columna donde se alojaba el id del registro por la cadena que hemos creado.
Carga el script primario en tu página y verás como, según el icono que pulses, la función JavaScript te muestra los datos de id del registro y operación elegida en cada caso.
Los scripts y la base de datos te los puedes bajar de este enlace.