Error al utilizar el método "belongsToMany" en Laravel

Publicado por Lynn
hace 4 meses

Tengo un problema al utilizar el método "belongsToMany" en Laravel. Quiero establecer una relación muchos a muchos entre dos modelos: Producto y Tag. He definido las tablas correspondientes y he creado los modelos respectivos con las relaciones correspondientes.

Aquí está el código de mi migración de la tabla "productos":

Schema::create('productos', function (Blueprint $table) {
    $table->id();
    $table->string('nombre');
    $table->timestamps();
});

Y aquí está el código de mi migración de la tabla "tags":

Schema::create('tags', function (Blueprint $table) {
    $table->id();
    $table->string('nombre');
    $table->timestamps();
});

En mi modelo "Producto" tengo la siguiente relación:

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

Y en mi modelo "Tag" tengo la siguiente relación:

public function productos()
{
    return $this->belongsToMany(Producto::class);
}

Cuando intento obtener todos los tags de un producto utilizando el método "tags":

$producto = Producto::find(1);
$tags = $producto->tags;

Obtengo el siguiente error:

SQLSTATE[42S02]: Base table or view not found: 1146 Table 'database.producto_tag' doesn't exist (SQL: select `tags`.*, `producto_tag`.`producto_id` as `pivot_producto_id`, `producto_tag`.`tag_id` as `pivot_tag_id` from `tags` inner join `producto_tag` on `tags`.`id` = `producto_tag`.`tag_id` where `producto_tag`.`producto_id` = 1)

¿Alguien sabe cómo solucionar este error? ¿He olvidado algo en mis migraciones o en mis modelos?

Gracias de antemano por su ayuda.

Laravel relaciones Eloquent
Respuesta de Gordon Shumway
hace 4 meses

¡Hola Lynn!

Por la descripción del error parece que te has olvidado de crear la tabla intermedia necesaria para la relación muchos a muchos. En Laravel, cuando defnes una relación belongsToMany, se espera que exista una tabla pivot (intermedia) que vincule las dos tablas en la relación.

El nombre convencional de la tabla pivot es el singular de los dos nombres de los modelos implicados, en orden alfabético y separados por un guión bajo, es decir, producto_tag.

Aquí está cómo debería verse la migración para crear la tabla pivot:

Schema::create('producto_tag', function (Blueprint $table) {
    $table->foreignId('producto_id')->constrained()->onDelete('cascade');
    $table->foreignId('tag_id')->constrained()->onDelete('cascade');
    $table->primary(['producto_id', 'tag_id']);
    $table->timestamps();
});

Asegúrate de que ejecutaste las migraciones con el comando:

php artisan migrate

Con esta tabla intermedia creada, las llamadas a $producto->tags y viceversa deberían funcionar sin problemas y podrás obtener los tags relacionados con el producto.

Si decidiste nombrar tu tabla pivot de manera diferente a la convención de Laravel, entonces debes especificar el nombre de la tabla en la relación:

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

public function productos()
{
    return $this->belongsToMany(Producto::class, 'tu_tabla_personalizada');
}

Recuerda que la clave foránea y la clave relacionada deben existir en la tabla pivot y deben ser del tipo correcto para que coincidan con las claves primarias de las tablas relacionadas.

¡Espero que eso solucione tu problema!