Relaciones de 1 a N con Laravel Eloquent

  • Por
  • PHP
Cómo definir los modelos y cómo acceder a los datos de tablas relacionadas, en relaciones de uno a muchos (1 a N), usando Laravel Eloquent.

Continuamos el Manual de Laravel abordando un tema básico dentro de Eloquent, el ORM de Laravel. Veremos lo que se conoce como relaciones de uno a muchos, también llamadas relaciones de 1 a N, que son un tipo de relaciones entre tablas muy habitual.

Realizaremos la práctica de creación de modelos y luego veremos código para usar esos modelos y acceder a los datos de los modelos relacionados. Veremos que Laravel y Eloquent facilitan mucho las cosas y nos ofrecen mecanismos de muy alto nivel para acceder a la información, en ambas direcciones.

Cuándo se genera una relación de 1 a N

Empecemos por aclarar rápidamente cuándo se tiene una relación de uno a muchos en el diseño de una base de datos relacional, aunque esperamos que la mayoría tenga este conocimiento.

Las relaciones de uno a muchos ocurren cuando tenemos dos tablas en la base de datos y en una de ellas se relaciona con la otra, de tal modo que la tabla "A" tiene muchos elementos de la tabla "B" relacionados y la tabla "B" sólo se relaciona con un elemento de la tabla "A". Se ve mejor con un ejemplo.

Supongamos que tenemos una base de datos de artículos con sus autores y que cada artículo está escrito por un solo autor. Bien, aquí podemos decir que:

  • Un artículo se relaciona con un escritor.
  • Un escritor puede escribir muchos artículos.

Como ves en la anterior imagen, la N queda del lado del artículo. Podemos decir que esta relación es de 1 escritor con N artículos.

Cómo diseñar las tablas de la base de datos

En el diseño de la base de datos, para este tipo de relaciones, en la tabla de la relación que está marcada por la N, se coloca el identificador o clave primaria de la tabla que está marcada con el 1. Por tanto, uno de los campos de la tabla de artículo será el escritor_id, que tendrá el identifiicador del escritor autor de ese artículo.

Se dice que el escritor_id (o writer_id, porque ya conocemos la recomendación de escribir los nombres de las tablas en inglés en Laravel) en la tabla "articulo" (o tabla article, en inglés) es una clave foránea.

Recuerda que el tema de las migraciones para la creación de las tablas y las relaciones entre ellas es algo que ya hemos abordado en el artículo Índices y claves en migraciones Laravel. https://desarrolloweb.com/articulos/indices-claves-migraciones-laravel.html

Definición de las relaciones en los modelos con Eloquent

Ahora vamos a abordar la parte más fundamental del desarrollo Laravel para las relaciones de 1 a N, que es crear convenientemente los modelos para que informemos al framework de la existencia de esta relación. Como ya sabes crear modelos en Laravel https://desarrolloweb.com/articulos/laravel-eloquent.html vamos a ir directamente a la parte de las relaciones.

Para definir las relaciones tendremos que especificar métodos con una forma concreta, en los que informamos sobre las relaciones. Como la relación afecta a dos tablas, tendremos dos modelos con los que trabajar.

Supongamos que los nombres de las tablas los tenemos en inglés. Tenemos la tabla "articles" y la tabla "writers". Entonces tendremos dos modelos "Article" y "Writer".

Relación 1 a muchos en el modelo Writer

Comenzamos con la relación que tenemos en la parte del "1", es decir en la tabla que está relacionada con "N" elementos de la otra parte. En nuestro caso es la tabla "Writers" la que tiene el "1" y que está relacionada con "N" artículos. Decimos que 1 escritor tiene muchos artículos en su autoría.

Para definir la relación tenemos que crear un método en el modelo, con el nombre que le queramos otorgar a dicha relación, que usualmente será el nombre de la entidad que queremos relacionar, en este caso en plural, dado que un escritor puede relacionarse con muchos artículos.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Writer extends Model
{
    public function articles()
    {
        return $this->hasMany('App\Article');
    }
}

El método articles() es el que implementa la relación. En él tenemos que devolver el valor de retorno del método hasMany() de los modelos Eloquent. A hasMany le tenemos que informar con el nombre de la clase del modelo con el que estamos relacionando.

Laravel entenderá automáticamente que en la tabla "articles" existirá la clave foránea con el escritor que sea autor (writer_id en la tabla article) y que en la tabla local (writers), la clave primaria se llama "id". Es importante que respetemos las convenciones de nombrado de tablas y de claves foráneas, para que no tengamos que hacer más trabajo al definir las relaciones, pero si no es el caso, hasMany() también puede recibir como parámetros las personalizaciones en las tablas que sean necesarias.

Por ejemplo, si en la tabla article la clave foránea tuviera otro nombre distinto de writer_id, podríamos indicarlo así:

return $this->hasMany('App\Article', 'nombre_clave_foranea');

Y si fuera el caso que en la tabla de writers la clave primaria no se llamara "id" también podríamos indicarlo con esta llamada a hasMany():

return $this->hasMany('App\Article', 'nombre_clave_foranea', 'nombre_clave_primaria_local');

Acceso a los datos de la tabla relacionada

Una vez que tenemos en nuestro modelo definida la relación, podemos acceder a los datos de la tabla relacionada en cualquier modelo de Writer. Para ello usamos el nombre del método que hemos creado como relación, en este caso era "articles".

$articulos_de_un_autor = App\Writer::find(1)->articles;

Esto será una colección de artículos que podremos usar como cualquier otra collection de Laravel.

foreach($articulos_de_un_autor as $articulo) {
	// hacer lo que necesites para cada $articulo
}

En Laravel el acceso a los datos de las tablas relacionadas se realiza por "lazy load", lo que quiere decir que, hasta que no se acceda a estos campos relacionados, no se hará la correspondiente consulta para la relación. Pero nosotros podemos forzar a Eloquent a que nos traiga de antemano los datos relacionados.

$escritores = Writer::with('articles')->get();

La inversa de la relación 1 a N, en el modelo Article

Al definir el modelo article también podemos, si se ve necesario, definir la relación. Con ello conseguimos que sea muy sencillo acceder desde el modelo marcado con la "N" a los datos del modelo marcado con el "1". O sea, para nuestro ejemplo, acceder al escritor que ha escrito un determinado artículo.

Esto se consigue con la definición de un método en el modelo, que llevará el nombre que queramos, pero usualmente será el nombre de la entidad que queremos relacionar. En este caso en singular, ya que solo estamos relacionando con un elemento de la otra tabla. En el caso de nuestro artículo, queremos relacionar con un solo escritor.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    public function writer()
    {
        return $this->belongsTo('App\Writer');
    }
}

Como puedes ver, el método que recibe la relación 1 a N inversa contiene la devolución del método belongsTo(), en el que indicamos el nombre del modelo con el que estamos relacionando.

Del mismo modo que en el caso anterior, es importante que las tablas estén creadas con las convenciones que asume Eloquent. En la tabla article se entiende que el nombre del campo de la clave foránea se llama "writer_id" y que el nombre de la clave primaria de la tabla relacionada es "id". Si no fuera el caso tenemos que aleccionar a Eloquent indicando como parámetros los nombres que se han utilizado en la definición de las tablas.

return $this->belongsTo('App\Writer', 'nombre_clave_foranea', 'nombre_clave_otra_tabla');
Nota: Como puedes ver, las convenciones nos ahorran bastante trabajo al desarrollar los modelos, tanto colocando los nombres de las tablas en inglés y en plural, como usando "id" como clave primaria, y el nombre de la entidad seguido por "_" y luego "id" como clave foránea.

Conclusión

Este es el resumen de los conocimientos más esenciales para implementar de una manera cómoda toda la infraestructura necesaria en Eloquent para implementar las relaciones entre modelos, de uno a muchos.

Laravel te pone muy fáciles las cosas y gracias a estas relaciones será muy sencillo acceder a los datos de las tablas relacionadas de 1 a N.