5.12 Relaciones Uno a Uno en Laravel
5.12.1 Introducción
Hasta ahora, hemos trabajado con modelos independientes, es decir, cada tabla y modelo ha estado aislado del resto. Por ejemplo: una tabla de productos, una tabla de usuarios o una tabla de notas.
Sin embargo, en una aplicación real es habitual que las tablas estén relacionadas entre sí. Laravel ofrece un sistema muy potente y sencillo para definir estas relaciones directamente desde los modelos.
Los tres tipos principales de relaciones son:
| Tipo de Relación | Descripción |
|---|---|
| Uno a Uno | Un usuario tiene un solo teléfono |
| Uno a Muchos | Un usuario tiene muchas notas |
| Muchos a Muchos | Un usuario puede tener muchos roles y cada rol muchos usuarios |
Importante
En este tema nos centraremos en el primer tipo de relación, la relación Uno a Uno.
Diagrama Visual
erDiagram
User ||--|| Phone : has
User {
int id
string name
}
Phone {
int id
int user_id
string prefix
string number
}
En este caso:
- Cada usuario tiene un teléfono asociado.
- Cada teléfono pertenece a un solo usuario.
5.12.2 Preparar Modelos y Migraciones
5.12.2.1 Crear modelos y migraciones
Vamos a preparar el modelo de Phone y su migración. El modelo User (viene con Laravel) y la migración también. Podemos reutilizar estos ficheros modificando los campos que no se necesitan.
Nota
En las relaciones uno a uno la relación puede ir en cualquiera de los dos modelos. En este caso la relación irá en el modelo Phone.
Ahora vamos a modificar la migración de usuarios eliminando algunos de sus campos.
En la migración de users:
Migración users
Ahora vamos a crear la tabla de teléfonos con su migración. El teléfono tendrá un prefijo y un número. El prefijo será el mismo para todos los teléfonos, por lo que no es necesario guardarlo en la base de datos. En este caso lo guardaremos como un campo de la tabla. Como hemos anticipado la relación irá en el modelo Phone, por lo que el campo user_id será la clave foránea que relaciona ambos modelos.
El campo user_id será el que relaciona ambos modelos. En la migración de phones:
En la migración de phones:
Migración phones
foreignId('user_id')define la clave foránea que enlaza conusers.id.
Ejecutamos las migraciones, en caso de que ya existan las tablas, primero eliminamos las tablas:
5.12.3 Definir la Relación en los Modelos
hasOne: Se usa en el modelo que posee la relación.belongsTo: Se usa en el modelo que pertenece a la relación.
En nuestro caso user_id está definido en phones, por lo que:
Usertiene un teléfono ➞hasOnePhonepertenece a un usuario ➞belongsTo
Vamos a crear en los modelos las funciones que definen las relaciones. Cuidado!! hay que crear las funciones en los modelos correctos. HasOne en el modelo User y BelongsTo en el modelo Phone.
En User.php:
Relación en el modelo User
En Phone.php:
Relación en el modelo Phone
5.12.4 Crear Datos con Seeder
Ahora vamos a crear datos de ejemplo, como solo vamos a crear unos cuantos usuarios y sus teléfonos no implementaremos factories para crear los datos. Simplemente crearemos un seeder que cree los datos.
Seeder:
UserSeeder
Hemos creado 4 usuarios, de los cuales 3 tienen teléfono y uno no. Ahora vamos a crear los teléfonos. Para ello creamos un nuevo seeder:
PhoneSeeder
En este caso hemos creado 3 teléfonos, uno para cada usuario. El usuario NoPhone no tiene teléfono, como ya avanzamos antes.
Ahora vamos a registrar los seeders en el DatabaseSeeder para que se ejecuten al ejecutar el comando de migración.
Y ya por último vamos a ejecutar las migraciones y los seeders. Por si ya teníamos datos en la base de datos, primero eliminamos las tablas fresh y luego ejecutamos las migraciones y los seeders.
Ejecutar:
5.12.5 Mostrar Datos Relacionados en Vista (index)
Creamos controlador:
Vamos a comenzar por la ruta users.index y el controlador UserController@index. En este caso vamos a listar los usuarios y sus teléfonos. Para ello vamos a cargar los usuarios y sus teléfonos en el controlador y luego los pasamos a la vista.
En el archivo de rutas web.php, añadimos la ruta:
Y ahora en el controlador definimos el método index que cargará los usuarios y sus teléfonos. En este caso vamos a cargar todos los usuarios y sus teléfonos.
Eager Loading
En Laravel, para cargar relaciones de forma eficiente, utilizamos el método with(). Esto nos permite cargar la relación phone al mismo tiempo que cargamos los usuarios. Si no lo hacemos, Laravel hará una consulta a la base de datos por cada usuario para cargar su teléfono, lo que puede ser muy ineficiente si tenemos muchos usuarios. Este método se llama Eager Loading.
Controlador:
UserController\@index
Ahora vamos a crear la vista users.index que mostrará los usuarios y sus teléfonos. En este caso vamos a mostrar el nombre y el teléfono de cada usuario.
Vista: resources/views/users/index.blade.php
Vista con teléfono
En este caso deberíamos haber creado primero un layout para la vista, pero como es un ejemplo sencillo no lo hemos hecho. En este caso hemos utilizado el operador ?? para mostrar un mensaje si el usuario no tiene teléfono.
Ahora vamos a comprobar que la vista funciona correctamente. Para ello vamos la siguiente URL:
Uso de belongsTo
Ahora vamos a utiulizar la relación en sentido contrario. Ahora teníamos un usuario y con su relación phone de tipo hasOne accedíamos al teléfono. Ahora vamos a hacer lo contrario, desde el teléfono acceder al usuario.
Para ello vamos a crear una nueva ruta:
En este caso vamos a crear un nuevo método en el controlador UserController que mostrará el usuario al que pertenece el teléfono. Comenzamos por el controlador:
UserController\@showPhone
El siguiente paso es crear la vista users.show que mostrará el usuario al que pertenece el teléfono. En este caso vamos a mostrar el nombre y el teléfono de cada usuario.
Vista: resources/views/users/show.blade.php
Vista con teléfono
En este caso deberíamos haber creado primero un layout para la vista, pero como es un ejemplo sencillo no lo hemos hecho. En este caso hemos utilizado el operador ?? para mostrar un mensaje si el usuario no tiene teléfono.
Ahora vamos a comprobar que la vista funciona correctamente. Para ello vamos la siguiente URL:
5.12.6 API para Usuarios con Teléfono
Comenzamos por dar soporte a la API. Para ello recordemos que en las nuevas versiones de LAravel hay que instalar el soporte para API ya que por defecto no viene instalado. Para ello ejecutamos el siguiente comando:
Una vez instalado el soporte para API vamos a modificar el prefijo para las rutas de la API. Para ello y modifiamos el fichero; bootstrap/app.php y añadimos el siguiente código:
Ahora vamos a añadir la ruta para la API. En este caso vamos a crear una nueva ruta para la API que devolverá los usuarios y sus teléfonos en formato JSON.
Ahora creamos un nuevo controlador UserController para la api. Luego vamos a crear un nuevo método en el controlador UserController que devolverá los usuarios y sus teléfonos en formato JSON.
Y el recurso (resource) que nos ayudará a formatear la respuesta de la API:
UserController que devolverá los usuarios y sus teléfonos en formato JSON.
UserController\@index
Ahora vamos a implementar el recurso UserResource que nos ayudará a formatear la respuesta de la API. En este caso vamos a crear un nuevo método en el recurso UserResource que devolverá los usuarios y sus teléfonos en formato JSON.
UserResource
Ahora utilizando el recurso UserResource podemos formatear la respuesta de la API de forma sencilla. En este caso hemos utilizado el operador ?? para mostrar un mensaje si el usuario no tiene teléfono.
Vamos a comprobar que la API funciona correctamente. Para ello vamos la siguiente URL:
Nos tiene que devolver un JSON con los usuarios y sus teléfonos:
5.12.7 Nueva ruta para obtener datos a partir de un teléfono
Ahora vamos a crear una nueva ruta para la API que devolverá el usuario al que pertenece el teléfono en formato JSON.
Ahora vamos a crear un nuevo método en el controlador UserController que devolverá el usuario al que pertenece el teléfono en formato JSON.
UserController\@show
Ahora devolvemos el cóigo 404 si no se encuentra el teléfono y el código 200 si se encuentra.
Para comprobar que la API funciona correctamente, vamos a probar la siguiente URL:
Un ejemplo de la respuesta con error sería:
y en caso de encontrar el teléfono y al propietario:| JSON devuelto por la API | |
|---|---|
5.12.8 Comprobaciones Finales
- Vista carga los usuarios y sus teléfonos correctamente.
- API devuelve los datos anidados como JSON.
5.12.9 Ejercicio
- Crea un nuevo modelo
Addressque tenga una relación uno a uno con el modeloUser. - Crea la migración y el seeder para el modelo
Address. - Crea la relación en los modelos
UseryAddress. - Crea la vista para mostrar los usuarios y sus direcciones.
- Crea la API para mostrar los usuarios y sus direcciones.
- Comprueba que todo funciona correctamente.