Manejo de relaciones en bases de datos con Laravel

Clase 8 de 33Curso Avanzado de Laravel

Resumen

Domina las relaciones de Eloquent en Laravel con una guía clara y práctica. Aquí verás cómo modelar productos, categorías y usuarios, crear migraciones con claves foráneas, ajustar factories y validar el flujo con tests. Todo con un enfoque directo y listo para aplicar.

¿Cómo conectar productos, categorías y usuarios con Eloquent?

Para mapear la lógica del dominio, Eloquent permite declarar relaciones expresivas. Un producto pertenece a una categoría y una categoría tiene muchos productos. Además, un producto tiene un único usuario creador mediante el campo created_by.

¿Cómo declarar belongsTo y hasMany en los modelos?

  • En Product: relación con Category usando belongsTo.
  • En Category: relación inversa con hasMany.
  • En Product y User: relación created_by con belongsTo y hasMany.
// app/Models/Product.php class Product extends Model { public function category() { return $this->belongsTo(Category::class, 'category_id'); } public function createdBy() { return $this->belongsTo(User::class, 'created_by'); } } // app/Models/Category.php class Category extends Model { public function products() { return $this->hasMany(Product::class, 'category_id'); } } // app/Models/User.php class User extends Model { public function products() { return $this->hasMany(Product::class, 'created_by'); } }

¿Qué conceptos clave debes interiorizar?

  • Eloquent: ORM de Laravel para mapear tablas a modelos.
  • belongsTo / hasMany: relaciones 1:N expresivas y legibles.
  • created_by: campo que enlaza producto con su creador.
  • Integridad referencial: la foreign key en base de datos refuerza la relación.

¿Qué migraciones y claves foráneas necesitas en Laravel?

Primero, agrega category_id a products con un valor por defecto. Se crea una categoría “otros” y se usa su ID como default. Luego, define la foreign key y aplica un rollback para regenerarla correctamente.

¿Cómo crear la migración para category_id?

php artisan make:migration add_category_id_to_products_table
// database/migrations/xxxx_xx_xx_add_category_id_to_products_table.php return new class extends Migration { public function up(): void { // Crear categoría por defecto $defaultId = DB::table('categories')->insertGetId([ 'name' => 'otros', 'created_at' => now(), 'updated_at' => now(), ]); Schema::table('products', function (Blueprint $table) use ($defaultId) { $table->unsignedBigInteger('category_id')->default($defaultId); // Clave foránea recomendada para integridad de datos $table->foreign('category_id')->references('id')->on('categories'); }); } public function down(): void { Schema::table('products', function (Blueprint $table) { $table->dropColumn('category_id'); }); } };

Aplicación de migraciones y rollback para regenerar la foreign key:

php artisan migrate:rollback php artisan migrate

¿Cómo añadir created_by y vincular usuarios?

Se agrega la columna created_by a products con un usuario “administrador” por defecto, creado con el factory de User.

php artisan make:migration add_created_by_to_products_table
// database/migrations/xxxx_xx_xx_add_created_by_to_products_table.php return new class extends Migration { public function up(): void { // Crear usuario por defecto: administrador $adminId = User::factory()->create(['name' => 'administrador'])->id; Schema::table('products', function (Blueprint $table) use ($adminId) { $table->unsignedBigInteger('created_by')->default($adminId); }); } public function down(): void { Schema::table('products', function (Blueprint $table) { $table->dropColumn('created_by'); }); } };

¿Cómo probar y sembrar datos con factories y seeders?

El testing valida las relaciones y evita errores comunes, como olvidar el return en un método de relación. Las factories generan datos realistas; los seeders controlan el orden en que se crean.

¿Cómo ajustar tests para belongsTo category?

  • Crear Category y Product con category_id asignado.
  • Afirmar que $product->category no sea nulo y coincida el ID.
// tests/Feature/ProductTest.php public function test_product_belongs_to_category(): void { $category = Category::factory()->create(); $product = Product::factory()->create([ 'category_id' => $category->id, ]); $this->assertNotNull($product->category); $this->assertEquals($category->id, $product->category->id); }

¿Cómo configurar factories para relaciones al azar?

  • En ProductFactory, elegir categoría y usuario al azar desde la base.
  • Evitar faker para estos campos relacionales.
// database/factories/ProductFactory.php public function definition(): array { return [ 'name' => $this->faker->word(), 'category_id' => fn() => Category::inRandomOrder()->first()->id, 'created_by' => fn() => User::inRandomOrder()->first()->id, ]; }

¿Cómo ordenar los seeders para que todo funcione?

  • Crear usuarios antes que productos para resolver created_by.
// database/seeders/DatabaseSeeder.php public function run(): void { User::factory(10)->create(); Category::factory(5)->create(); Product::factory(20)->create(); }

¿Te gustaría ver más ejemplos de relaciones en Eloquent o resolver un caso específico de tu proyecto? Comparte tus dudas y cuéntame qué modelo quieres conectar a continuación.