¿Por qué son importantes las relaciones de tablas?
Las relaciones de tablas son fundamentales cuando trabajamos con bases de datos porque nos permiten vincular diferentes conjuntos de datos de manera lógica y eficiente. Esto no solo facilita la recuperación de información relacionada, sino que también mejora la integridad y coherencia de los datos. En tecnología como Laravel, estas relaciones se gestionan de manera intuitiva y potente, lo que permite construir aplicaciones más robustas y escalables.
¿Cómo se establecen las relaciones en Laravel?
En Laravel, las relaciones de tablas se establecen mediante modelos y métodos dedicados que clarifican las conexiones entre las entidades en tu base de datos.
Crear una columna de relación:
Se añade un campo a la tabla que actuará como clave foránea. En el ejemplo, el campo user_id de la tabla de posts es una referencia al campo id de la tabla de usuarios.
Modificar migraciones:
Una vez que el campo está añadido, se debe ejecutar una migración para aplicar los cambios a la base de datos.
Los datos de prueba deben ser actualizados para reflejar estas nuevas relaciones. Los Factory generan datos aleatorios, mientras que los Seeders se aseguran de que se llenen las tablas con estos datos de prueba.
La parte clave es crear métodos en los modelos que representen estas relaciones. Por ejemplo, para un usuario con múltiples posts, se utiliza el método hasMany.
Para visualizar estas relaciones en tus vistas o controladores, Laravel ofrece métodos intuitivos y potentes.
Mostrar información relacionada: Si deseas imprimir los detalles de un post junto con su autor, puedes hacerlo fácilmente utilizando las relaciones definidas.
$posts=Post::all();foreach($postsas$post){echo$post->title." by ".$post->user->name;}
Contar elementos relacionados: Para determinar cuántos posts tiene un usuario, simplemente puedes extender el uso de relaciones con funciones adicionales como count.
$users=User::withCount('posts')->get();foreach($usersas$user){echo$user->name." has ".$user->posts_count." posts.";}
Al aplicar estas técnicas, no solo mejoras la funcionalidad de tu aplicación, sino también la eficiencia de cómo accedes y manejas los datos en ella.
¿Qué problemas comunes pueden surgir y cómo resolverlos?
Es importante saber qué hacer cuando las cosas no salen como se espera.
Errores de migración: Si php artisan no detecta cambios, puede deberse a que no se han creado archivos nuevos. Puedes utilizar un comando para refrescar las migraciones y aplicar todos los cambios.
php artisan migrate:refresh --seed
Métodos Incorrectos: Asegúrate de que los nombres de los métodos coincidan exactamente con las relaciones definidas. Los métodos mal escritos pueden impedir que se carguen correctamente los datos relacionados.
$user->posts;// Correcto$user->post;// Incorrecto, método no definido
Trabajar con conceptos avanzados como relaciones de tablas puede parecer intimidante al principio. Sin embargo, tienen el potencial de transformar completamente cómo gestionas e interactúas con tus datos. Aprovechar al máximo estas funcionalidades de Laravel te permitirá desarrollar aplicaciones más eficientes y preparadas para la producción. Como siempre, la práctica es clave para dominar estas habilidades, por lo que te animo a seguir explorando y experimentando. ¡Adelante!
Para crear una relación con una tabla externa tendremos que escribir el siguiente dentro de la clase de la tabla en el método up()
// Para que asegurar que el dato siempre sea positivo$table->unsignedBigInteger('user_id');// Para crear llaves foraneas y unirse a la siguiente la tabla externa $table->foreign('user_id')->references('id')->on('users');
Ahora nos toca refrescar las tablas, para eso vamos a utilizar el siguiente comando:
php artisan migrate:refresh --seed
💚 Si les sale este error:
Illuminate\Database\QueryException
SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (SQL: drop table if exists `users`)
Tendrás que proceder poner en la tabla que estás creado una relación el siguiente código dentro del método down()
publicfunctiondown(){// El siguiente código:Schema::dropIfExists('posts');Schema::dropIfExists('users');}
😋 Este error sucede porque para crear de nuevo las tablas, Laravel necesita que primero se borre la tabla hijo antes de borrar la tabla padre. La tabla hijo en este caso sería posts perp como Laravel primero va ha leer la tabla padre debemos poner esta sentencia en esta tabla.
rand($min, $max) → Es una función que genera aleatoriamente un número dentro de los dos parámetros que le damos.
En Laravel 8x, para crear semillas de datos que van ser ejecutados por php artisan migration, tenemos que escribir la sentencia así:
💢 Recuerda que la configuración que hicimos con las migraciones solamente afectaron a la Database, no a nuestro modelo.
Tenemos que ir a los archivos de los modelos de las tablas y especificar que hicimos la relación entre esas tablas, vamos a su clase y creamos el siguiente método:
// User.php => Aquí dejaremos una sentencia que significa que// un usuario tiene muchos postspublicfunctionposts(){return$this->hasMany(Post::class);}// Post.php => Aquí dejaremos una sentencia que significa que// un post le pertence a un usuario.publicfunctionuser(){return$this->belongsTo(User::class);}
Tus aportes deberian estar mas arriba hahaha eres de mucha ayuda
Buen aporte
Aquí hay más información de** todas las relaciones posibles **usando **Eloquent **
Relaciones de tablas: trabajaremos sobre el ejemplo creado anteriormente, para ello tenemos que hacer las siguientes modificaciones:
En el archivo que se encuentra en la carpeta de migration y que hace referencia a posts añadimos lo siguiente:
publicfunctionup(){Schema::create('posts',function(Blueprint$table){$table->id();$table->unsignedBigInteger('user_id');//AGREGAMOS ESTE CAMPO PARA NUESTRA LLAVE FORÁNEA$table->string('title');/*AQUI AGREGAMOS LA DECLARACIÓN DE LA LLAVE
FORNEA (user_id) es el campo de mi tabla, id el campo de la tabla donde quiero hacer referencia mi llave foranea y users es la tabla */$table->foreign('user_id')->references('id')->on('users');$table->timestamps();});}
AQUÍ AÑADIMOS UN NUEVO CAMPO AL MODELO DE MIGRACIÓN DE NUESTRA TABLA POST
Ahora en el archivo que se encuentra dentro de nuestra carpeta factoriesañadimos lo siguiente al archivo PostFactory.php
$factory->define(Post::class,function(Faker$faker){return['user_id'=>rand(1,4),//AÑADIMOS EL CAMPO DE NUESTRA LLAVE FORANEA Y AGREGAMOS LA FUNCION RAND (el cual nos permite generar numeros random que va del 1 al 4)'title'=>$faker->sentence];});
Ahora como hicimos modificaciones en nuestro archivo de migración tenemos que reflejarlo en la base de datos, para ello ejecutamos el comando:
php artisan migrate:refresh
ejecutar este comando tiene un inconveniente, el cual radica en que si teníamos registros en nuestra base de datos estos se borraran. Para evitar esto tenemos que configurar lo siguiente:
Para evitar que se borren nuestro registros al ingresar el comando anterior configuramos unas semilla( dentro de la carpeta seeds).
Ahora podemos ejecutar nuevamente el comando para refrescar nuestra base de datos con nuestros cambios, pero debemos agregar una opción más:
php artisan migrate:refresh --seed
Ahora si tenemos nuestras tablas actualizadas y con datos llenos
Todo esto nos ayudo para crear un archivo de migración que tenga relación entre dos tablas y que podamos crear registros aleatorios en el, sin embargo laravel aún no conoce esta relación que creamos, para hacer conocer a laravel de estos cambios hacemos lo siguiente :
Vamos al modelo Post.php y agregamos la siguiente función:
//Un post pertenece a un usuariopublicfunctionuser(){return$this->belongsTo(User::class);}
Vamos al modelo User.php y agregamos la siguiente función:
//Relación : Un usuario tiene muchos postpublicfunctionposts(){return$this->hasMany(Post::class);}
Ahora si podemos empezar a realizar operaciones con nuestras tablas relacionadas (estos ejemplos se lo realizó en el archivo web.php)
Acabo de crear una tabla con sus campos propios y los de auditoria, siempre estoy acostumbrado a trabajar con CreatedBy, CreatedDate, ModifiedBy, ModifiedDate y IsDeleted. El created_at y updated_at me lo da $table->timestamps(); y el created_by y modified_by los hice de la siguiente forma, que les parece?
Excelente aporte, tal cual lo implemente, me parecio interesante que usas el metodo softDeletes(), estaba consultando y es muy practico usarlo (EL ELIMINAR ES UNA ELIMINACION LOGICA, cambia el estado del registro mas no se borra de forma definitiva)
Estoy utilizando la versión de Laravel 8 y este es el código que utilice para crear el seeder y poderlo ejecutar desde la consola una vez lo configuró como el instructor muestra
Me saltaba el error trying to get property 'name' of non-object laravel por lo que buscando en internet encontré que tenía también que especificar el nombre del campo de la clave foránea en el belongsTo() del archivo Post.php:
Con esto ya me aparecieron correctamente los registros, desconozco totalmente si esto influya en la versión de Laravel, yo uso la 7.11.0.
Espero les haya servido si a alguien le saltó el mismo error, y si saben por qué me pudo haber pasado a mi de esta forma agradecería que me lo dijeran 😄
Eso es porque Laravel por convencion usa en este caso user_id haciendo referencia a la tabla users, dado caso que cambiaste el nombre del campo fk_user_id, ahi si debes especificar el campo como segundo parametro en la relacion
Ya he realizado un par de proyectos en Laravel y pude darme cuenta de la importancia de nombrar bien las cosas jaja. Muchas gracias por la confirmación, lo escribía de esa forma porque así me habían acostumbrado en la escuela.
Hola!! Si quieren formar parte de un grupo de estudios de Devs, les dejo un link a un grupo de Telegram que está muy chulo.
También tienen una organización de GitHub, únete por aquí:
Tengo una duda quizás alguna de ustedes a pasado o pensando en el caso el tema de relaciones vía eloquent es impresionante sin embargo que tan practico es a gran escala digamos una consulta donde recepcionamos mas de 10,000 post que tanta ralentiza el proceso usar las relaciones .
¡Hola!, Eloquent tiene una cosa llamada "chunks" que nos permite manejar grandes cantidades de información para que las búsquedas sean eficientes, además también depende mucho de la estructura de la base de datos, si tienes una base de datos de solo lectura que usa índices y está optimizada para lectura, entonces las búsquedas serán muy rápidas, y también depende de la infraestructura backend que manejes, pero a nivel de solo Eloquent, puedes usar chunks para ello :D
Muchas gracias por la info
Para crear las relaciones de una llave foranea se usa de la siguiente forma:
// nombre del campo que va a contener el ID de la otra tabla a relacionar$table->unsignedBigInteger('user_id');//esto crea un Entero Grande sin signo /*
$table->foreign('campo de la table actual')
->references('campo de llave primaria de la otra tabla')
->on('tabla a relacionar');
*/$table->foreign('user_id')->references('id')->on('users');
me daba error por el use App\User; como usa, pero en la version actual de laravel toca aumentar App\Models\User
use App\Post;Route::get('eloquent',function(){ $posts =Post::where('id','>=','10')->orderBy('id','DESC')->take(3)->get();foreach($posts as $post){ echo "$post->id $post->title <br>";}});Route::get('posts',function(){ $posts =Post::get();foreach($posts as $post){ echo "$post->id
<strong>{$post->user->name}</strong> $post->title <br>";}});use App\User;Route::get('users',function(){ $users =User::get();foreach($users as $user){ echo "$user->id
<strong>{$user->name}</strong>{$user->posts->count()} posts <br>";}});
La verdad tengo problemas con los comandos y la base de datos porque ningun comando me migra ni me refresca en la base de datos, no da ningun error solo me aparece esto pero no hace absolutamente nada.. la base de datos se queda igual. estoy en xampp
Casa@DESKTOP-MBM1C0SMINGW32/c/xampp/htdocs/laravel/eloquent
$ php artisan migrate:refresh --seed
Nothing to rollback.Migrating: 2014_10_12_000000_create_users_table
Illuminate\Database\QueryExceptionSQLSTATE[42S01]:Base table or view already exists:1050Table'users' already
exists(SQL: create table `users`(`id` bigint unsigned not null auto_increment
primary key,`name`varchar(255) not null,`email`varchar(255) not null,`emai
l_verified_at` timestamp null,`password`varchar(255) not null,`remember_token
`varchar(100)null,`created_at` timestamp null,`updated_at` timestamp null) d
efault character set utf8mb4 collate 'utf8mb4_unicode_ci') at C:\xampp\htdocs\laravel\eloquent\vendor\laravel\framework\src\Illuminate\Database\Connection.php:669665|// If an exception occurs when attempting to run a query, we'll format the error
666|// message to include the bindings with SQL, which will make this exception a
667|// lot more helpful to the developer instead of just the database's errors.668|catch(Exception $e){>669|thrownewQueryException(670| $query, $this->prepareBindings($bindings), $e
671|);672|}673|1C:\xampp\htdocs\laravel\eloquent\vendor\laravel\framework\src\Illuminate\Database\Connection.php:463PDOException::("SQLSTATE[42S01]:Base table or view already exists:1050Table 'users' already exists")2C:\xampp\htdocs\laravel\eloquent\vendor\laravel\framework\src\Illuminate\Database\Connection.php:463PDOStatement::execute()
Parece que en tu base de datos ya tienes un tabla users, te lo dice el error que te da la consola en este mensaje:
SQLSTATE[42S01]: Base table or view already exists: 1050 Table 'users' already
exists
A veces te da ese error cuando no está registrada esa base de datos dentro de la tabla migraciones que maneja Laravel en la misma base de datos.
Para evitar eso debes verificar bien que en tus archivos de migraciones este agregado dentro del metodo Down() el codigo necesario para eliminar la tabla cuando se ejecute el rollback que en este caso es llamado por el refresh.
RESUMEN1"database/migrations/_Create_posts_table
$table->unsignedBigInteger("user_id");//relacionar con el id de table users$table->foreign("user_id")->references("id")->on("users");//relacion ||campo ||donde esta el campo2"TERMINAL"php artisan migrate:refresh //esto actualiza los archivos actuales3"database/seeds/DatabaseSeeder.php""repoblamos la DB cada vez que migramos"factory(App\User::class,4)->create();factory(App\Post::class,30)->create();4"database/factories/PostFactory.php"agregar la columna "user_id" a la fabrica de datos de prueba
"user_id"=>rand(1,4),"title"=>$faker->sentence
5"TERMINAL"volvemos a hacer el refresh pero con el atributo --seed
php artisan migrate:refresh --seed
6TODOMUYLINDODESDELAVISTADELABASEDEDATOSPEROAHORAHAYQUEAVISARLEALARAVEL por ende hay que ir a actualizar los models
"User.php"debemos especificar que tipo de relacion relacion:-un user puede tener muchos posts
publicfunctionposts(){return $this->hasMany(Post::class);}"Post.phpdebemos especificar que tipo de relacion relacion:-cada Post le pertenece a 1Userpublicfunctionuser(){return $this->belongsTo(User::class);}7"routes/web.php"A)IMPRIMIREMOSLOSPOSTSSUID,ELNOMBREDESUUSER,SUTITLERoute::get('/posts',function(){ $posts =Post::get();foreach($posts as $post){ echo "
$post->id
<strong>{$post->user->name}</strong>//class||method||user owner name $post->title
<br/>";}});B)IMPRIMIREMOSLOSUSERS y la cantidad de posts que tienen
Route::get('/users',function(){ $users =User::all();foreach($users as $user){ echo "
$user->id
$user->name
{$user->posts->count()} posts //class||method|| contar <br/>";}});EXPLICACION:t
enemos acceso a estos valores
POST:{$post->user->name}USER:{$user->posts->count()}PORQUEELMETODO"user" existe en el model Posty el metodo "posts" existe en el model UserMORALEJALOSMODELSSONMUYIMPORTANTESPORQUESONLAREPRESENTACIONDELATABLAENBASEDEDATOSy con los metodos que usemos podemos listar , eliminar, consultar etc
Excelente clase! Este curso se esta poniendo cada vez mejor :D
Si el curso esta bueno, pero se ha hecho interesante debido a que el profesor Italo explica de una forma muy sencilla y clara para hacer llegar ese conocimiento a uno.
para que los seeders funcionene cuando se refresca la BD se debe de usar la opcion --seed
php artisan migrate:refresh --seed
Aqui lo correcto hubiera sido haber creado una migración extra indicando que a la tabla Posts se le agregará el campo user_id.
Por lo general sería como indicas, sin embargo en este caso dado que no importa perder los datos y por un tema didáctico, se justifica hacerlo así. Es una oportunidad para probar el comando migrate:refresh, del cual no tenemos muchas oportunidades de probarlo.
Buenas , al ejercutar el comado "migrate:refresh --seed" , me ha dado el error que la tabla posts ya existia
Alguna idea que hago mal??, la he tenido que borrar y ejecutar el migrate sin refresh
Gracias
Podes usar php artisan migrate:fresh --seed
Borra todo y la vuelve a crear y la migra.
Toma en cuenta que si en una ejecución anterior esta falló entonces deberás eliminar todas las tablas y volver a ejecutar el comando.
Cómo haría una relación muchos a muchos sin crear manualmente la tabla intermedia? hay alguna forma de generar las relaciones automáticamente?
No, para las relaciones muchos a muchos se necesita sí o sí una tabla intermedia, esto es por la naturaleza de dicha relación, se explica mejor en el Curso de Fundamentos de Bases de Datos :D
Que sucede, si queremos implementar laravel en un sistema con una base de datos ya existe? ya no podemos utilizar la migración, porque las tablas ya existe... pero entiendo que si recreamos la estructura de la tabla dentro del archivo de configuración de laravel es posible realizar cambios, que y la data??? hay manera de prevenir que me borre los datos?
Los ejemplos que dan es cuando empiezas un proyecto de 0. Eso es ideal ya que tenemos buenas practicas. Pero en la realidad me a tocado trabajar con bd ya existentes, con su propia tabla por ejemplo, usuarios que tiene otras columnas .
En ese caso no te compliques y no uses las migraciones. Lo que tienes que hacer, es configurar en tu archivo .env la conexión a la bd (De prueba)
PERO OJO, SIEMPRE TRABAJA SOBRE UNA BD DE PRUEBA. Jamas sobre una en producción.
Lo que tienes q hacer una vez configurado tu .env es ir a tu modelo que creaste. Y especificar con que tabla con tu bd trabajas, asi como la llave primaria que usas. ejemplo:
<?php
namespace App;use Illuminate\Database\Eloquent\Model;classNombreDeMiModeloextendsModel{//ACA INDICO A DONDE APUNTA MI MODELO, A Q TABLA DE MI BDprotected $table ="NombreDeMiTabla";//INDICO LAS COLUMNAS QUE QUIERO TRAER, SI NO LO PONGO TRAE TODAS POR DEFECTOprotected $fillable =["NombreDeMiColumna1","NombreDeMiColumna2"];//Ahora indico cual es el nombre de la columna que utilizo como Llave primariaprotected $primaryKey='Mi_ID';//Ahora indico si es autoincremental o no, en este caso sipublic $incrementing=true;//Por defecto laravel siempre trabaja con columnas de actualizacion y creacion, pero si la tabla con la que estas trabajando no las tiene, y tampoco deseas agregarlas, tienes q especificarle que no las use porque no existen, por eso le pongo false(UPDATED AT -CREATED AT) public $timestamps =false;}}
Como ves estoy especificando manualmente como tiene q trabajar con las tablas. Ahora d3ebes tener cuidado con el archivo migrate, si es q lo creaste. Si por alguna razón lo creaste, elimina esos archivos, ya que estas trabajando sobre tablas ya existentes. X q si x ahí se te escapa un migrate te vas a volar toda la bd.
POR ESO SIEMPRE TRABAJA CON UNA BD DE PRUEBA QUE TENGAS, una vez hayas probado que todo funciona como quieres pasas a produccion. SIEMPRE SIN LOS ARCHIVOS MIGRATE SE SUPONE Q NO DEBES TENERLOS.
Listo, si el campo se llama user_id, pues automaticamente laravel sabe que es la Fk .Pero en caso de que se llame id_user, ¿en donde tendria que configurar?
Al momento de crear la relación en el Modelo, puedes indicar el nombre de la clave foránea, en caso de que no siga el "estandar" que Laravel espera: