¿Cómo manipular una relación uno a uno en el controlador?
En la programación, manipular relaciones de base de datos de una manera efectiva es esencial para crear aplicaciones robustas y funcionales. Vamos a explorar cómo implementar y manejar una relación uno a uno en un controlador, específicamente en la parte de creación de usuarios con un Customer ID opcional en una base de datos.
¿Qué cambios hacer en el Data Transfer Object (DTO)?
Cuando se crea un usuario, a menudo necesitamos enviar datos adicionales más allá de simplemente un correo electrónico y contraseña. Por eso, es importante modificar el DTO para recibir y manejar un nuevo atributo, el Customer ID. Este campo es opcional, por lo que al no ser enviado, no generará errores.
El siguiente paso consiste en la creación del usuario mediante el servicio adecuado – en este caso, el CustomerService. Si se proporciona un Customer ID, se debe buscar en la base de datos y establecer la relación.
Implementación en el controlador:
Inyección del CustomerService: Para reutilizar las funciones del servicio.
Validación: Verificar si se envía el Customer ID.
Relationship Setting: Buscar el Customer usando el ID y establecer la relación.
¿Qué sucede si hay errores en la base de datos?
Al implementar relaciones, es común enfrentar errores si las llaves primarias no están configuradas como autoincrementales. Esto evita que la base de datos genere automáticamente identificadores únicos.
Solución del error de autoincremento:
Modificar las entidades para incluir el decorador correcto para que las llaves primarias sean autogeneradas.
@Entity()exportclassUserEntity{@PrimaryGeneratedColumn() id:number;// otros campos...}
¿Por qué es importante generar una migración?
Los cambios en la estructura de las entidades requieren una migración de base de datos para sincronizar los cambios de manera controlada y segura.
Generar migración: Ejecutar npm run migrations:generate -- fixid.
Ejecutar migración: Use npm run migrations para aplicar los cambios.
¿Cómo probar la relación en el entorno real?
Luego de realizar los cambios, es esencial probar el endpoint. Usando una herramienta como Insomnia, se puede verificar el flujo al crear un usuario y asociarlo con un cliente.
Probar el endpoint de creación: Verificar que el identifier sea autogenerado y que el Customer ID sea opcional.
Relacionar usuario y cliente: Introducir un Customer ID existente para establecer la relación.
¿Cómo visualizar la relación en las respuestas?
Finalmente, para listar usuarios y ver sus relaciones en un solo paso, es fundamental modificar el servicio para incluir las relaciones cuando se obtengan los datos.
Al aplicar estos pasos, se puede listar todos los usuarios y sus correspondientes relaciones de manera efectiva. Dominar estos conceptos es crucial para la manipulación avanzada de bases de datos. ¡Sigue explorando y aprendiendo!
Manipulación de Relaciones Uno a Uno en Controladores y Servicios
Si queremos que cuando se consulte al usuario tambien se obtengan los datos del customer debemos agregar la siguiente opcion
asyncfindAll(){returnawaitthis.userRepo.find({relations:['customer']// esta});}asyncfindOne(id: number){const user =awaitthis.userRepo.findOne(id,{relations:['customer']// esta});if(!user){thrownewNotFoundException(`User #${id} not found`);}return user;}
Resolviendo la relación uno a uno en el controlador
Vamos a resolver que al momento de crear un usuario, podamos mandar un id de un customer y así mismo crear la relación, pero para esto primero debemos modificar el DTO de creación de usuario:
src/users/dtos/user.dto:
// ...exportclassCreateUserDto{// ..// simplemente especificamos que existe esta propiedad// anadimos validaciones @IsPositive() @IsOptional() @ApiProperty()readonly customerId:number;}exportclassUpdateUserDtoextendsPartialType(CreateUserDto){}
Ahora si, vamos a modificar nuestro servicio de usuarios y vamos a preguntar si existe un customerId, y si sí, resolvamos la relación:
**src/users/services/user.service**:
// ...import{User}from'../entities/user.entity';import{CreateUserDto,UpdateUserDto}from'../dtos/user.dto';import{ProductsService}from'./../../products/services/products.service';// importamos el servicio de Customersimport{CustomersService}from'./customers.service';@Injectable()exportclassUsersService{constructor(// inyectamos el servicio de customers en nuestro servicioprivate customersService:CustomersService, @InjectRepository(User)private userRepo:Repository<User>,){}findAll(){// al momento de traer los usuarios, traemos su customer en caso de tenerloreturnthis.userRepo.find({ where:{ id }, relations:['customer'],});}asyncfindOne(id:number){// al momento de traer un usuario, traemos su customer en caso de tenerloconst user =awaitthis.userRepo.findOne({ where:{ id }, relations:['customer'],});if(!user){thrownewNotFoundException(`Users #${id} not found`);}return user;}asynccreate(data:CreateUserDto){const newUser =this.userRepo.create(data);// si existe un customer el la data que nos envíanif(data.customerId){// encuentra a ese customer en base a su idconst customer =awaitthis.customersService.findOne(data.customerId);// relaciona al usuario con el customer newUser.customer= customer;}returnthis.userRepo.save(newUser);}// ...}
Y listo, ya hemos resuelto la relación entre user y customer.
Hay un problema semantico con este código. Idealmente, no deberías obtener un producto del repositorio de usuarios, para eso está el repositorio de productos.
Gracias Nicolas! En realidad nombré mal el nombre de la constante, ya que en el ejemplo, mi idea sí era obtener el usuario con el userRepository y resolver la relación de customer.
Entonces en vez de const product -> const user
Saludos!
Para obtener un usuario con su customer, en el método findOne, pueden hacer el siguiente cambio:
asyncfindOne(id: number){const user =awaitthis.userRepo.findOne(id,{relations:['customer']});if(!user){thrownewNotFoundException(`User #${id} not found`);}return user;}