Query Builder Laravel 5

  • Por
Qué es Query Builder, junto con una guía de uso en el framework PHP Laravel 5.1.

Como ya señalamos en la introducción a las bases de datos en Laravel, existen diversas vías de acceso a los datos, con distintos niveles de abstracción. Query Builder es uno de ellos. Su nivel no es tan bajo como escribir las consultas en crudo (Raw SQL), pero sí más cercano al sistema gestor de base de datos de lo que sería el ORM.

Query Builder contiene una serie de funciones listas para realizar las operaciones más comunes con una base de datos, pero sin usar el lenguaje SQL directamente, sino el API de Laravel. En este artículo vamos a ver una completa introducción a Query Builder de Laravel 5.1, que nos sirva para conocer la operativa de trabajo con el sistema. Pero antes de comenzar conviene señalar un par de características importantes.

  1. Query Builder trabaja con todos los tipos de bases de datos soportadas por Laravel. Por tanto, el código que nosotros usaremos se podrá ejecutar para cualquier gestor compatible, obteniendo los mismos resultados. Por tanto, este sistema permite abstraerse del motor de base de datos que estemos usando por abajo.
  2. Query Buider usa internamente los mecanismos de PDO, incluido el bindeo de parámetros en las consultas. Por tanto, no es necesario filtrar los datos que vamos a usar en las sentencias, ya que éstos serán filtrados automáticamente para protegernos de ataques por inyección SQL.

Como observarás, el uso del ORM Eloquent simplifica todavía más el trabajo con las bases de datos, sin embargo Query Builder consigue completar las funciones en una menor cantidad de tiempo. Para un juego de datos o número de operaciones pequeñas ese tiempo será imperceptible, pero a medida que sube la carga se podrá ver que Query Builder es un poco más rápido.

Objetos "fluent" Query Builder

Para trabajar con Query Builder seguimos requiriendo el uso de la "DB Facade", pero en este momento, en vez de usar los métodos para ejecutar consultas crudas ("Raw SQL") que vimos en el anterior artículo, usaremos el método table().

Al método table() le pasamos por parámetro el nombre de la tabla con la que pretendemos operar y nos devuelve un objeto de clase Illuminate\Database\Query\Builder, al que se conoce habitualmente con el nombre de "Fluent Query Builder" o simplemente "Fluent".

$fluent = DB::table('books');

En $fluent tenemos ahora una referencia a un objeto "Fluent Query Builder" que ha sido enlazado con la tabla "books". Ese objeto ya estará enlazado para trabajar sobre la tabla indicada y le podremos pasar mensajes (invocar sus métodos) para hacer las operaciones precisas que necesitemos en cada momento sobre esa tabla.

Nota: no te olvides de hacer el correspondiente "Use DB;" en caso que estés trabajando con esta fachada desde un namespace en el que no se conozca esa clase, por ejemplo desde un controlador.

Ahora podemos invocar otros métodos para poder realizar distintas operaciones. Por ejemplo, get() para recibir los datos de una tabla.

$libros = $fluent->get();

Eso nos devolverá un array de elementos, donde en cada casilla tendremos referencias a objetos, de clase stdClass, con los datos de un libro.

Nota: stdClass es una clase incorporada en PHP nativo que se usa cuando se convierte una variable (que no es tipo objeto) en un objeto, por ejemplo, haciendo un casting de un array asociativo a un objeto. Osea, es un objeto "genérico" por decirlo de alguna manera, que no hace ninguna cosa en concreto.

Estas dos operaciones se suelen encadenar en una única línea de código:

$libros = DB::table('books')->get();

Aparte de get(), existen otros métodos, para convertir un objeto Fluent Query Builder en una estructura de datos, por ejemplo first() te devolverá el primero de los elementos de la colección que contenga un objeto fluent.

$libro = DB::table('books')->first();

En este caso no recibes como retorno un array de libros, sino que recibes un único libro en un objeto de clase stdClass.

La diferencia entre get() y first() es que en el primero obtienes un array con un número indeterminado de elementos y en la segunda obtienes un objeto con un único elemento. Para comprobarlo, te damos dos pedazos de código realizarán la misma tarea. Solo difiere la manera de acceder al primer elemento para recuperar su nombre.

// Si uso first() accedo a las columnas del registro mediante un objeto
$libro = DB::table('books')->first();
echo $libro->name;

// si uso get() recibo un array, cuyo primer registro se indexa con [0]
$libros = DB::table('books')->get();
echo $libros[0]->name;

Esto es una pequeñísima parte de lo que se puede hacer con Query Builder, pero es el inicio de todo tipo de operaciones más complejas que vamos a ver. Sacarle todo su provecho se basa en encadenar métodos que nos sirven para hacer las cosas que necesitemos. Es esencial por tanto contar con la documentación de Query Builder en Laravel 5.1, que es la versión que manejamos en este manual.

Operaciones de selección con Query Builder

Al hacer operaciones de selección tienes la opción de especificar los campos que quieres recuperar con el método select:

$libros = DB::table('books')->select('name')->get();

Si hay más de un campo que quieras seleccionar, puedes enviar un array con los campos que te interesa recuperar.

$libros = DB::table('books')->select(['name', 'author'])->get();

Veamos ahora cómo seleccionar un grupo de usuarios concreto, con el método where(), que encadenamos a la creación del objeto fluent.

$libros_mario_puzo = DB::table('books')->where('author', '=', 'Mario Puzo')->get();

En este código tenemos como resultado un objeto fluent query builder con un grupo restringido de elementos de la tabla "books". Obtenemos la tabla entera y luego le encadenamos el método where() indicando que necesitamos aquellos libros con "author" igual a "Mario Puzo".

El método where puede recibir cualquier tipo de operador, incluso el "like".

$libros = DB::table('books')->where('author', 'like', '%julio%')->get();

Si queremos hacer dos condiciones, por ejemplo que el autor sea tal cosa y que el título sea tal otra, podemos encadenar dos métodos where().

$libros = DB::table('books')->where('author', '=', 'Mario Puzo')->where('name', 'like', '%padrino%')->get();

Nota: Si quieres aumentar la legibilidad de tanto encadenamiento de mensajes, puedes escribir esto mismo en varias líneas.

$libros = DB::table('books')
	->where('author', '=', 'Mario Puzo')
	->where('name', 'like', '%padrino%')
	->get();

Si queremos combinar varios where() con la función lógica "or", por ejemplo autores que se llamen "Mario Puzo" o "Cervantes", puedes hacerlo con el método orWhere().

$libros = DB::table('books')
	->where('author', '=', 'Mario Puzo')
	->orWhere('author', '=', 'Cervantes')
	->get();

Tienes muchas otras opciones como whereNull(), whereNotNull(), wherein() whereNotin(), whereNotBetween().

Por ejemplo, el código anterior (autores que sean una cosa o la otra) podría haberse escrito:

$libros = DB::table('books')
	->wherein('author', ['Mario Puzo', 'Cervantes'])
	->get();

Joins con Query Builder

Si queremos juntar dos o más tablas con Query Builder también tenemos la posibilidad de usar las operaciones de "join", con el metodo join() invocado sobre el objeto Fluent Query Builder.

Este método recibe varios parámetros:

  • La tabla a unir
  • Campo de la tabla 1 por el que se relacionan
  • Operador de la relación, usualmente "="
  • Campo de la tabla 2 por el que se relacionan
  • Tipo de join, por defecto "inner"
  • Boolean, positivo para hacer un "join where", por defecto es false

Los parámetros clave son los 4 primeros, que veremos en uso en el siguiente ejemplo. Dada una tabla de facturas y una de clientes, donde las facturas tienen un id_cliente.

$facturasCliente = DB::table('clientes')
	->join('facturas', 'facturas.id_cliente', '=', 'clientes.id', 'inner', true)
	->select('clientes.*', 'facturas.id as id_factura', 'facturas.fecha')
	->where('clientes.email', '=', 'miguel@desarrolloweb.com')
	->get();

Se pueden unir más de dos tablas, especificando los distintos Joins. Al esquema anterior vamos a unirle una tabla de "item facturable", donde cada factura tiene un número indeterminado de items facturables, que serían los conceptos de la factura.

$facturasCliente = DB::table('clientes')
	->join('facturas', 'facturas.id_cliente', '=', 'clientes.id')
	->join('item_facturables', 'facturas.id', '=', 'item_facturables.id_factura')
	->select('clientes.*', 'facturas.id as id_factura', 'facturas.fecha', 'concepto')
	->where('clientes.email', '=', 'miguel@desarrolloweb.com')
	->get();

Existen varios tipos de join y también varios métodos para hacer joins como leftJoin(), rightJoin, joinWhere(), etc. También existe una variante del método join que permite configurar la relación por medio de una función anónima. Para todos estos usos, consultar la documentación.

Inserts con Query Builder

Para insertar uno o varios campos en registros de una tabla podemos lanzar el mensaje insert() a un objeto Fluent Query Builder. Obtenemos el objeto como hasta ahora, con el método table() de la facade DB, indicando el nombre de la tabla: DB::table('books'). Entonces encadenamos la llamada al método insert(), de la siguiente manera:

$inserted = DB::table('books')
	->insert([
		'name' => 'La Ciudad de los Prodigios',
		'author' => 'Eduardo Mendoza'
	]);

La llamada al método insert() devuelve un boleano indicando si pudo completar la inserción con éxito.

Ahora podemos ver otra alternativa de llamada a insert(), enviando como parámetro un array de arrays para insertar varios elementos a la vez.

$inserted = DB::table('books')
	->insert(
	[
		[
			'name' => 'Cien años de soledad',
			'author' => 'García Márquez'
		],
		[
			'name' => 'El Alquimista',
			'author' => 'Paulo Coelho'
		]
	]
	);

Si queremos saber el Id del registro insertado tenemos la alternativa de usar el método insertGetId(), como se puede ver en el código siguiente:

$id = DB::table('books')
	->insertGetId([
		'name' => 'La danza de la realidad',
		'author' => 'Alejandro Jodorowsky'
	]);

Updates con Query Builder

Hacer un update es bien sencillo y similar a lo que hemos realizado ya. Invocamos el método update() sobre el Query Builder, al que pasamos un array asociativo con los datos a actualizar. Este comando se suele combinar con el método Where, para restringir el registro o grupo de registros que quieres actualizar con estos datos.

$updates = DB::table('books')
	->where('id', '=', '74')
	->update([
		'name' => 'Sin noticias de Gurb',
		'author' => 'Eduardo Mendoza'
	]);

En este caso, como valor de retorno del update obtendremos el número de registros que se actualizaron al ejecutarse la consulta.

Como utilidad adicional, existen unos shortcuts para hacer una operación de incremento o decremento, aplicable en campos numéricos. En lugar de escribir a mano el código para hacer el update es realiza la llamada al método increment() o decrement(). El campo que se quiere incrementar y decrementar se envía como primer parámetro y de manera opcional se puede indicar un segundo parámetro con las unidades de incremento o decremento.

$num_updates = DB::table('books')
		->where('id', '=', '97')
		->increment('lecturas');

Deletes

Los deletes son exactamente iguales a los updates, solo que se tiene que invocar el método delete() para borrado.

$deletes = DB::table('facturas')->delete();

No te olvides colocar el método where() para restringir los registros que serán borrados! Si lo que quieres es borrar todos los elementos de una tabla, y restaurar su estado inicial sin datos, también tienes el método truncate().

Debug simple con toSql() en las sentencias Query Builder

Las sentencias de selección se puede debugear fácilmente con el método toSql(). Simplemente lo usamos en vez de generar el conjunto de registros en un array con get() o first().

toSql() nos devuelve la cadena de la sentencia en crudo (Raw SQL) que se ejecutará en el servidor, que luego podemos mostrar con una función como var_dump() o la incorporada dd() en Laravel.

$rawSql = DB::table('books')
	->select(['name', 'author'])
	->where('author', '=', 'Mario Puzo')
	->orWhere('author', '=', 'Cervantes')
	->toSql();
dd($rawSql);

Esto nos mostraría la cadena de la sentencia a ejecutar, pero antes de realizar el bindeo de los parámetros.

select `name`, `author` from `books` where `author` = ? or `author` = ?

En muchos de los casos será suficiente para obtener la información que necesitamos con el objetivo de saber si la consulta es la que se esperaba. No obstante, existen otros métodos más avanzados de debuggear la consulta que veremos más adelante cuando hablemos de escuchar eventos de consultas o debug en Laravel.

Conclusión

Hemos aprendido a manejar Query Builder en Laravel 5.1, creando objetos Fluent y encadenando todo tipo de mensajes para hacer operaciones de selección, selección con relaciones entre tablas, inserciones, actualizaciones y borrados. Gracias a las características de Query Builder, todo el código que realicemos será compatible con cualquier sistema gestor de bases de datos que soporte Laravel.

La construcción de consultas SQL con Fluent Query Builder tiene, no obstante, otras muchas cosas interesantes que no hemos llegado a ver todavía. En próximos artículos y en ejemplos prácticos seguiremos trabajando con esta alternativa al acceso a bases de datos, aunque en adelante vamos intentar centrarnos más en el ORM, Eloquent, ya que nos ofrece mayores facilidades. No obstante, tampoco debes perder de vista lo aprendido sobre Query Builder, porque la mayoría de métodos que has conocido, por no decir todos, te van a servir también cuando trabajes con Eloquent.

Recuerda que la documentación oficial también te puede aclarar puntos a los que no hemos llegado en el presente artículo.

Autor

Miguel Angel Alvarez

Miguel es fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Comenzó en el mundo del desarrollo web en el año 1997, transformando su hobby en su trabajo.

Compartir

Comentarios

Lara Santiago

18/2/2016
Estupendo
Veo en este framework muchas similitudes con symfony aunque en el tema de las bases de datos creo que las posibilidades de trabajo tiene más variantes.
Muchas gracias a DesarrolloWeb por estos artículos de Laravel

midesweb

09/5/2016
Posibilidades de Laravel y bases de datos
Lo cierto es que Laravel tiene bastantes opciones para el acceso a las bases de datos. No sé si Symfony goza de tanta variedad, pero muy probablemente sí, ya que es un framework de primer nivel.
Lo que ocurre con Symfony es que usa Doctrine, de manera predeterminada, que quizás es un poco más complejo que Eloquent.