Skip to content

10. Relaciones y Claves Foráneas (ORM)

Las relaciones entre tablas son fundamentales en cualquier base de datos relacional. Laravel proporciona una forma elegante de definir y trabajar con estas relaciones tanto a nivel de migración (estructura de la base de datos) como a nivel de modelo (ORM Eloquent).

Sintaxis Básica de Claves Foráneas (foreign)

Section titled “Sintaxis Básica de Claves Foráneas (foreign)”

Las claves foráneas en Laravel se definen dentro de las migraciones utilizando el método foreign(). Este método establece una restricción de clave foránea entre dos tablas.

Ejemplo de migración con clave foránea
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->string('title');
$table->text('content');
$table->timestamps();
// Definición básica de clave foránea
$table->foreign('user_id')
->references('id')
->on('users');
});

En este ejemplo:

  1. Primero creamos la columna user_id como unsignedBigInteger (debe coincidir con el tipo de la columna referenciada)
  2. Luego definimos la restricción de clave foránea con foreign('user_id')
  3. Especificamos la columna a la que hace referencia con references('id')
  4. Indicamos la tabla a la que hace referencia con on('users')

Una relación uno a uno significa que un registro en una tabla está relacionado con exactamente un registro en otra tabla. Por ejemplo, un usuario tiene un perfil.

Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});

Características importantes:

  • Añadimos $table->unique('user_id') para garantizar que un usuario solo pueda tener un perfil
  • Usamos onDelete('cascade') para que si se elimina un usuario, también se elimine su perfil automáticamente

Una relación uno a muchos significa que un registro en una tabla puede estar relacionado con múltiples registros en otra tabla. Por ejemplo, un usuario puede tener muchos posts.

Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamps();
});

En este caso:

  • No añadimos unique() a user_id porque un usuario puede tener múltiples posts
  • Mantenemos onDelete('cascade') para que si se elimina un usuario, se eliminen todos sus posts

Relación Muchos a Muchos en Migraciones (Tablas Pivote)

Section titled “Relación Muchos a Muchos en Migraciones (Tablas Pivote)”

Una relación muchos a muchos significa que múltiples registros en una tabla pueden estar relacionados con múltiples registros en otra tabla. Por ejemplo, un post puede tener muchas etiquetas y una etiqueta puede estar en muchos posts.

Para este tipo de relación, necesitamos una tabla pivote (o tabla intermedia).

Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content');
$table->timestamps();
});

Características importantes de la tabla pivote:

  • El nombre de la tabla sigue la convención: nombres de las tablas relacionadas en orden alfabético y en singular (post_tag)
  • Contiene claves foráneas a ambas tablas relacionadas
  • Incluye un índice único compuesto para evitar duplicados en la relación

Los métodos onDelete() y onUpdate() permiten especificar qué debe ocurrir cuando se elimina o actualiza un registro referenciado.

Opciones de onDelete y onUpdate
$table->foreign('user_id')
->references('id')
->on('users')
->onDelete('cascade')
->onUpdate('cascade');

Opciones disponibles:

OpciónDescripción
cascadeElimina o actualiza los registros relacionados automáticamente
restrictImpide la eliminación o actualización si existen registros relacionados
set nullEstablece la clave foránea como NULL (la columna debe permitir valores nulos)
no actionSimilar a restrict, pero se verifica al final de la transacción
set defaultEstablece la clave foránea al valor predeterminado de la columna

A partir de Laravel 7, se introdujo el método foreignId() que simplifica la creación de claves foráneas.

Uso de foreignId()
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id');
$table->string('title');
$table->text('content');
$table->timestamps();
// Definir la restricción de clave foránea
$table->foreign('user_id')->references('id')->on('users');
});

El método foreignId() es equivalente a $table->unsignedBigInteger(), pero comunica mejor la intención de que la columna será una clave foránea.

Uso de constrained() para relaciones automáticas

Section titled “Uso de constrained() para relaciones automáticas”

El método constrained() simplifica aún más la definición de claves foráneas al inferir automáticamente la tabla y columna referenciadas.

Uso de constrained()
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained();
$table->string('title');
$table->text('content');
$table->timestamps();
});

En este ejemplo:

  • constrained() infiere automáticamente que la clave foránea user_id hace referencia a la columna id en la tabla users
  • Laravel sigue la convención de que el nombre de la tabla es el plural del prefijo de la columna (antes de _id)

También puedes especificar la tabla y la columna si no sigues las convenciones:

// Especificar tabla
$table->foreignId('user_id')->constrained('personas');
// Especificar tabla y columna
$table->foreignId('user_id')->constrained('personas', 'persona_id');

Puedes encadenar métodos adicionales:

$table->foreignId('user_id')
->constrained()
->onUpdate('cascade')
->onDelete('cascade');

Cuando necesitas modificar o eliminar una tabla con claves foráneas, primero debes eliminar las restricciones de clave foránea.

Eliminar claves foráneas
Schema::table('posts', function (Blueprint $table) {
// Eliminar una clave foránea específica
$table->dropForeign('posts_user_id_foreign');
// Forma alternativa usando un array
$table->dropForeign(['user_id']);
});

El nombre de la restricción de clave foránea sigue el patrón: {tabla}_{columna}_foreign.

  1. Orden de las migraciones: Asegúrate de que las tablas referenciadas se creen antes que las tablas que las referencian.

  2. Consistencia de tipos: Usa el mismo tipo de datos para la clave foránea y la columna referenciada.

  3. Índices: Las claves foráneas deben estar indexadas para mejorar el rendimiento:

    $table->foreignId('user_id')->constrained()->index();
  4. Cascada con precaución: Usa onDelete('cascade') con cuidado, ya que puede eliminar datos en cascada sin confirmación adicional.

  5. Nombres descriptivos: Usa nombres de columnas descriptivos para las claves foráneas, como author_id en lugar de simplemente user_id si la relación es específica.

🐝