Relaciones Laravel Eloquent de N a M

  • Por
  • PHP
Cómo configurar los modelos de Laravel para gestionar relaciones de N a M, muchos a muchos, con el ORM Eloquent.

En el Manual de Laravel hemos podido aprender ya a gestionar relaciones más sencillas, como las de 1 a N. No es que las relaciones de muchos a muchos sean muy complejas, pero sí que nos obligarán a hacer algún paso extra en nuestros modelos, así como la creación de la llamada tabla pivote.

En este artículo aprenderás a crear tus relaciones de muchos a muchos, de manera que tus modelos sean capaces de abstraerse de la complejidad de este tipo de relación, pasando fácilmente de un elemento de una entidad a sus elementos relacionados de otras entidades. Afortunadamente, una vez completados una serie de pasos a la hora de configurar los modelos, el manejo de estas relaciones será tan fácil como un juego de niños.

El primer paso que tendrías que hacer es conocer, al menos de manera general, los mecanismos para realizar relaciones más sencillas en Laravel. Esto es algo que ya hemos tratado en artículos anteriores del Manual de Laravel. Como es todo bastante parecido, ofreceremos menos explicaciones de las cosas que ya deberías de conocer. Te recomendamos para ello al menos la lectura de las relaciones de 1 a N. Por supuesto, también tienes que conocer y saber usar los modelos de Laravel / Eloquent.

Tabla pivote en relaciones de N a M

Primero comencemos por un pequeño refresco de nuestros conocimientos de bases de datos relacionales. Recordemos que las relaciones de muchos a muchos generan una tabla adicional, en la que tenemos los índices de los dos elementos de la relación.

Por ejemplo, tenemos la relación tag y article (etiquetas y artículos). Una etiqueta puede tener asociados muchos artículos y un artículo puede tener asociadas varias etiquetas. Lo vemos en la siguiente imagen.

En este caso tendremos, además de las tablas de "Tags" y "Articles", una tercera tabla que dará soporte a la relación de muchos a muchos. Ésta es la denominada "tabla pivote" o simplemente "pivot". Básicamente, la tabla pivot mantiene los identificadores de tag y de article, para cada elemento con su relación.

Nota: adicionalmente, si por el hecho de existir esa relación se generan más datos, podrían ir en la tabla pivote. Por ejemplo, imagina que quieres ver los artículos ordenados de una manera especial en la página de un tag. Entonces ese orden podría colocarse en la tabla pivote, como un campo extra.

En la tabla pivote no te hace falta más que los identificadores y no necesitas tener el típico autoincrement como clave primaria para la tabla, ni tampoco los timestamps de Laravel, a no ser que los necesites para alguna cosa.

Otra cosa importante es que la tabla pivote debería tener un nombre determinado, para ajustarnos a las convenciones de Eloquent, y así evitar tener que escribir más código del estrictamente necesario. El nombre simplemente es la unión de las dos entidades, separado por un guión bajo. Además, las entidades se ordenan alfabéticamente, por lo que primero nombraremos a la tabla article: "article_tag".

Para que quede más claro, coloco a continuación el código de una migración para hacer esa tabla pivote. Recuerda que tienes más información en el artículo de las migraciones de Laravel.

Schema::create('article_tag', function (Blueprint $table) {
    $table->integer('article_id')->unsigned();
    $table->integer('tag_id')->unsigned();

    $table->foreign('article_id')->references('id')->on('articles');
    $table->foreign('tag_id')->references('id')->on('tags');
});

Configuración de los modelos

En la configuración de los modelos de Laravel, colocaremos la relación con el método "belongsToMany". Es algo muy parecido a como hacíamos con relaciones anteriormente, sólo que usamos un método nuevo.

En el modelo "Tag" crearemos un método que nos devuelva los artículos relacionados, de esta manera:

public function articles() {
    return $this->belongsToMany('App\Article');
}

Para la relación inversa, desde Article a Tag, usamos exactamente el mismo método "belongsToMany". Pero caro, en el modelo de "Article" la relación es con "Tag". Nos quedará más o menos así:

public function tags() {
    return $this->belongsToMany('App\Tag');
}

Con estos sencillos pasos ya podemos desde un modelo obtener todos los registros en entidades relacionadas de muchos a muchos.

Nota: ten en cuenta que, si no hemos seguido las convenciones de Laravel para el nombrado de identificadores en las tablas, nombres de las tablas o claves foráneas, tendrías que realizar esas relaciones en los modelos enviando algunos parámetros extra. Lo ideal es seguir las convenciones, para no tener trabajo de más, aunque si tu proyecto no lo puede hacer así, por favor, revisa la documentación de Laravel.

Acceder a las relaciones desde un modelo en otro

Una vez definida la relación, lo interesante es que puedo acceder a los elementos relacionados como por arte de magia, sin tener que hacer consultas complicadas. Para ello usaremos una propiedad en el modelo, que tiene el nombre igual al método que hemos usado para definir la relación.

Por ejemplo, si tengo una instancia de un modelo Article y quiero acceder a sus tags, colocaré el código:

$article->tags

Esto nos permite acceder a una colección, que será el listado de todos los tags que tiene el article que teníamos en el modelo. Ten en cuenta que este acceso se realiza por "lazy load", de modo que Laravel no extraerá la lista de tags de un artículo hasta que no se la pidas explícitamente.

No obstante, el acceso por lazy load no siempre lo mejor. Imagina que estás haciendo un listado de artículos y tienes 25 artículos que mostrar. Entonces le pides a Eloquent esa colección de modelos y te los entrega tal cual. Pero de cada artículo quieres que se vean en ese listado todas sus etiquetas, por lo que usarás la relación para acceder a ellas. El problema de esto es que con cada acceso a los tag de un artículo se genera una nueva consulta. Para evitar esta situación podemos decirle a Laravel que nos traiga de una vez todas las etiquetas relacionadas de cada artículo encontrado.

$articlesConTags = Article::with('tags')->get();

Conclusión

Con esto conocemos lo básico de las relaciones de muchos a muchos. Esperamos que te haya resultado de utilidad y recuerda que Laravel Eloquent tiene mucha flexibilidad, por lo que existen bastantes detalles de configuración que no hemos llegado a analizar para las relaciones de N a M y que resultarán imprescindibles si es que tus tablas no siguen todas las convenciones de Laravel.