martes, 30 de enero de 2018

Laravel DataTables 4

Los ejemplos básicos de Laravel DataTables (incluyendo los míos) solo tratan con tablas individuales. Es más raro encontrar qué hacer con las columnas de otras tablas tomadas por relaciones, algo la mar de normal en bases de datos.

No hay mucha complicación aquí. El nombre utilizado es el mismo que el nombre de la columna en sí, tanto en Laravel como en Javascript. De modo que si utilizamos el método Query Builder:

$datos = DB::table('tabla')

    ->select([

        'tabla1.id',

        'tabla1.nombre',

        'tabla2.origen'

    ])

    ->join('tabla2', 'tabla1.origen_id', 'tabla2.id');


Podemos mostrar las columnas: 'tabla1.id', 'tabla1.nombre' y 'tabla2.origen'.
Nada nos impide emplear alias.

$datos = DB::table('tabla')

    ->select([

        'tabla1.id AS id',

        'tabla1.nombre AS nombre',

        'tabla2.origen AS 'origen'

    ])

    ->join('tabla2', 'tabla1.origen_id', 'tabla2.id');


Sin embargo esto tiene sus limitaciones a la hora de aplicar filtros (sobre los que hablaré en el próximo capítulo). Para esto habrá que engrosar un poco la información en Javascript. El resultado sería algo así:

    $(function() {

        $('#list-table').DataTable({

            processing: true,

            serverSide: true,

            columns: [

                { data: 'id', name: 'tabla1.id' },

                { data: 'nombre', name: 'tabla1.nombre' },

                { data: 'origen', name: 'tabla2.origen' },

            ],

            ajax: 'http://sitio.net/tabla/data'

            }

        });

    });


En cada columna, el data es lo que se muestra en la tabla resultante, y el name es lo que utilizará para las búsquedas o filtros. O sea, lo que meterá en sus WHEREs. No es necesario indicar el nombre completo en columnas de la tabla principal (la del FROM), pero nunca está de más.
Por supuesto, esta distinción es importante si dos tablas tienen columnas con el mismo nombre.

Pos eso. En el próximo capítulo empiezo con los filtros y búsquedas.

lunes, 22 de enero de 2018

Laravel DataTables 3

En la entrada anterior he explicado el uso básico, y espero que haya quedado lo bastante claro, al menos para desarrolladores con algo de experiencia en Laravel. A partir de aquí podemos pasar a temas más concretos, y sobre los que costará más encontrar documentación y ejemplos.

Creo que lo primero que se necesitaría meter en una de estas tablas, al menos dentro de un panel de gestión, serían las acciones por fila. Los típicos botones o enlaces de modificar, eliminar, deshabilitar, seleccionar, etc. cada entrada.

Esta no es una información que forme parte de la base de datos. En PHP se suele meter el HTML necesario en cada bucle de la tabla, inyectándole el ID de cada entrada. ¿Laravel DataTables facilita este aspecto? ¡Y tanto!

Durante la creación del objeto DataTables podemos crear columnas al vuelo, con el método addColumn. Se le especifica un nombre de columna (uno cualquiera) y un contenido. Este puede ser HTML escrito a mano, o puede ser una vista Blade.

DataTables::of($tabla)->addColumn('acciones', 'tablas.tabla-acciones');


Dentro de la vista se pueden utilizar, como variables, las columnas de la fila actual. Sin más.

<a href="editar.php?id={{ $id }}">Editar {{ $nombre }}</a>
<a href="{{ route("tabla.informe", ["id" => $id]) }}">Informe</a> 


Un detalle bastante importante en Javascript sería excluir esta columna de las búsquedas y reordenamiento.

{ data: 'acciones', orderable: false, searchable: false }


En el próximo capítulo: qué hacer con tablas relacionadas. No se preocupe: saldrá muy pronto.

martes, 16 de enero de 2018

Laravel DataTables 2

Como indicaba en el capítulo anterior, el primer proyecto donde quería probar Laravel DataTables no estaba basado en Laravel. Pero sí había un proyecto relacionado, mucho más reciente, que utilizaba la misma base de datos y que sí tenía Laravel. Lo que hace jQuery DataTables en modo server-side no son más que llamadas AJAX. Así que el lado de Laravel es básicamente una API lista para servir.

Después de instalar Laravel DataTables (como cualquier otro paquete mediante Composer), creé un controlador exclusivamente para esto, con la función data. Como explica la demo oficial, puede bastar con retornar DataTables::of(Modelo::all())->make(true).

Desgraciadamente como este era un proyecto antiguo pre-Laravel, el acceso por modelo no me sirve. O al menos no de manera directa y fácil.
Afortunadamente Laravel DataTables puede recolectar datos en base a tres tipos de objetos: Eloquent, Collection y Query Builder.

  • Eloquent sería trabajar con los Modelos, tal cual. Si están bien definidos, con sus relaciones y todo, es lo más laraveliano. Lo ideal en muchos casos.
  • La Colección debe estar completamente formada antes de llamar a DataTables. Es útil si los datos necesitan ser tratados en PHP, o cualquier historia "chunga". Esto implica más trabajo todavía para PHP, y menor flexibilidad a la hora de aplicar filtros.
  • Query Builder... ya sabe usted, suele ser el paso previo en Laravel a get(), all(), o cualquier función que implique ejecutar la consulta / query. Puede ser tanto formada con métodos de modelos, de DB, o escribiendo manualmente el SQL.

Como este proyecto tenía unas relaciones un tanto complicadas, opté por Query Builder. No es lo ideal, pero así pude copiar las consultas que se estaban utilizando previamente y basarme en ellas. Venía a ser: return DataTables::of(DB::table('tabla')->select(['columna1', 'columna2']))->make(true);

Listos por el lado del controlador. ... ¿Que cómo se gestionan las búsquedas, paginación, etc.? La función of se encarga absolutamente de toda la interactividad, en base a parámetros auto-generados que envía el cliente con cada clic. No hay que programar nada para estos casos. Ni siquiera hace falta extender el controlador.

Lo único que falta es la ruta:
Route::name('tabla.data')->get('tabla/data', 'TablaController@data');

Como verá usted, la ruta no tiene nada de especial. El controlador se encarga de todo.

Por supuesto hay diferencias con mi proyecto: lo paso por ruta POST, en lugar de GET (preferencia personal), y el controlador lo extiendo de ApiGuard (sí, un poco de seguridad, ya que es una API entre dos proyectos, y no quiero que pueda acceder cualquiera).

En el HTML del front-end escribo la tabla solo con sus cabeceras, una por columna, y añado el script necesario:
    $(function() {
        $('#list-table').DataTable({
            processing: true,
            serverSide: true,
            columns: [
                { data: 'columna1' },
                { data: 'columna2', orderable: false, searchable: false }
            ],
            ajax: 'http://sitio.net/tabla/data'
            }
        });
    });

Y con eso ya tenemos lo básico. Creo que por ahora no necesita más explicación, aparte de que esas dos primeras propiedades siempre deben ser true (hasta donde yo sé).


A partir del próximo capítulo serán principalmente detalles y casos más específicos. Condiciones para las búsquedas, cómo tratar columnas de otras tablas, acciones para cada fila, filtro individual por columna, etc.

martes, 2 de enero de 2018

Laravel DataTables 1

En caso de que usted no lo hubiera leído ya, Laravel DataTables básicamente consiste en un componente para enviar desde Laravel datos en JSON, destinados a un jQuery DataTables en modo server-side.

Si usted había utilizado DataTables, es posible que se hubiese conformado con el modo fácil, basado en DOM: generar desde el backend una tabla HTML con absolutamente toda la información, y ejecutar DataTables encima con jQuery. De esta manera el navegador se encarga del paginado, orden, filtros, y todas las bondades que DataTables lleva años ofreciendo.

Esas bondades dejan de ser tan bonitas cuando la tabla contiene miles de entradas. El servidor tardará en procesar todo ese HTML. El cliente estará utilizando demasiada memoria. Cada interacción puede tardar más de lo debido. El usuario se impacientará cuando "solo" quiere buscar los artículos vendidos tal día por tal empleado con tal método de pago.

Ahí es cuando entra en acción el modo server-side processing. Cada vez que se pasa de página, o se utiliza un filtro, DataTables llamará por AJAX al servidor para pedirle información; solo la necesaria para rellenar la vista actual, y no toda de golpe. La diferencia de rendimiento y estabilidad empieza a ser evidente, ¿verdad?

Laravel DataTables se encarga de que el lado de servidor sea fácil. Puede bastar con crear una ruta, coger un modelo y aplicarle una función. Luego en el front-end se inicializa DataTables con unas pocas opciones, y ya está el funcionamiento básico en marcha y en toda su gloria.

Con varios de mis proyectos me he topado exactamente con el problema descrito antes. Para esto Laravel DataTables me ha parecido un rayo de esperanza (o de luz verde). Y lo he puesto en práctica hace poco.

Por supuesto no iba a ser tan fácil: este proyecto era un poco viejo, y no estaba basado en Laravel. Tuve que hacer algunos trucos para aprovechar Laravel igualmente. Y he tenido que invertir bastantes horas para implementar en el front-end exactamente los mismos filtros que tenían antes. Finalmente he conseguido que apenas se note la diferencia, excepto en que ahora funciona fantásticamente, en lugar de tardar medio minuto en abrir la tabla.

A partir del siguiente capítulo contaré los detalles de esta pequeña aventura.