Todos los artículos

Relaciones BelongsToMany en Laravel Eloquent

Julian Beaujardin
Julian Beaujardin August 5th, 2024

En Laravel, Eloquent ORM proporciona una hermosa y simple implementación de ActiveRecord para trabajar con tu base de datos. Una de sus características más poderosas es la gestión de relaciones entre modelos. Este artículo se centrará en la relación BelongsToMany, que se utiliza para definir relaciones de muchos a muchos.

¿Qué es una Relación BelongsToMany?

Una relación BelongsToMany es una relación de muchos a muchos. Por ejemplo, un usuario puede tener muchos roles, y un rol puede ser asignado a muchos usuarios. Esta relación se implementa típicamente con una tabla intermedia.

Escenario de Ejemplo

Consideremos una aplicación de blog donde los posts pueden tener muchas etiquetas, y las etiquetas pueden ser asignadas a muchos posts. Esta es una relación clásica de muchos a muchos.

Configurando la Base de Datos

Primero, configuraremos nuestras tablas de base de datos. Necesitaremos tres tablas: posts, tags y post_tag (la tabla intermedia).

Migración para la Tabla de Posts

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreatePostsTable extends Migration
{
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('content');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('posts');
    }
}

Migración para la Tabla de Tags

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateTagsTable extends Migration
{
    public function up()
    {
        Schema::create('tags', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('tags');
    }
}

Migración para la Tabla Intermedia Post_Tag

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreatePostTagTable extends Migration
{
    public function up()
    {
        Schema::create('post_tag', function (Blueprint $table) {
            $table->id();
            $table->foreignId('post_id')->constrained()->onDelete('cascade');
            $table->foreignId('tag_id')->constrained()->onDelete('cascade');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('post_tag');
    }
}

Definiendo la Relación en Modelos

A continuación, definiremos la relación BelongsToMany en nuestros modelos Post y Tag.

Modelo Post

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use HasFactory;

    public function tags()
    {
        return $this->belongsToMany(Tag::class);
    }
}

Modelo Tag

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Tag extends Model
{
    use HasFactory;

    public function posts()
    {
        return $this->belongsToMany(Post::class);
    }
}

Trabajando con la Relación BelongsToMany

Ahora que nuestra relación está definida, podemos empezar a trabajar con ella.

Adjuntar Etiquetas a un Post

Para adjuntar una etiqueta a un post, puedes usar el método attach.

$post = Post::find(1);
$tag = Tag::find(1);

$post->tags()->attach($tag->id);

Desadjuntar Etiquetas de un Post

Para desadjuntar una etiqueta de un post, puedes usar el método detach.

$post->tags()->detach($tag->id);

Sincronizar Etiquetas con un Post

El método sync se puede usar para sincronizar etiquetas para un post. Esto adjuntará nuevas etiquetas y desadjuntará cualquier etiqueta que no esté incluida en el arreglo dado.

$post->tags()->sync([1, 2, 3]);

Recuperar Etiquetas de un Post

Para recuperar etiquetas de un post, puedes usar la relación tags.

$tags = Post::find(1)->tags;

foreach ($tags as $tag) {
    echo $tag->name;
}

Recuperar Posts de una Etiqueta

De manera similar, para recuperar posts de una etiqueta, puedes usar la relación posts.

$posts = Tag::find(1)->posts;

foreach ($posts as $post) {
    echo $post->title;
}

Usando Columnas de la Tabla Intermedia

A veces, puede que necesites almacenar información adicional en la tabla intermedia. Por ejemplo, puede que quieras rastrear cuándo se adjuntó una etiqueta a un post.

Añadiendo Columnas a la Tabla Intermedia

Primero, añade la nueva columna a tu migración de la tabla intermedia:

Schema::create('post_tag', function (Blueprint $table) {
    $table->id();
    $table->foreignId('post_id')->constrained()->onDelete('cascade');
    $table->foreignId('tag_id')->constrained()->onDelete('cascade');
    $table->timestamps();
    $table->timestamp('tagged_at')->nullable(); // Nueva columna
});

Accediendo a Columnas de la Tabla Intermedia

Para acceder a las columnas de la tabla intermedia, usa el método withPivot en la definición de la relación:

class Post extends Model
{
    use HasFactory;

    public function tags()
    {
        return $this->belongsToMany(Tag::class)->withPivot('tagged_at');
    }
}

Luego puedes acceder a las columnas de la tabla intermedia así:

$post = Post::find(1);
foreach ($post->tags as $tag) {
    echo $tag->pivot->tagged_at;
}

Actualizando Columnas de la Tabla Intermedia

Para actualizar una columna de la tabla intermedia, usa el método updateExistingPivot:

$post->tags()->updateExistingPivot($tag->id, ['tagged_at' => now()]);

Conclusión

La relación BelongsToMany de Eloquent proporciona una forma simple pero poderosa de gestionar relaciones de muchos a muchos en tus aplicaciones Laravel. Al entender y utilizar estas relaciones, puedes crear un código más robusto y mantenible. Este artículo cubrió lo básico, pero hay mucho más que puedes hacer con las relaciones de Eloquent. Explora la documentación oficial para un uso y técnicas más avanzadas.