Categorias
iiiiii
Relaciones ORM
Descripcion
Realizar una relacion entre prod y imagenes produc
Nota1
0. teners inatalado el paquete typeorm . documentacion "https://orkhan.gitbook.io/typeorm/docs/relations"
1. crea las dos entidades
2. creamos un product DTO(para crear un nuevo producto) para ver que estrucutra va a teners los productos
3. se crea en entity producto el "@OneToMany()"
4. ahora realizamos la relacion de imagenes hacia producto usando a "@ManyToOne"
5. se crea un servicio para crear las imagenes y guardar el producto relacion a imagenes
6. se realiza un findAll para listar productos con sus imagenes
NOTA: tambien se peude poner en el findAll los datos de relacion agregando la siguiente propiedad
@OneToMany(
() => ProductImage,
(productImage) => productImage.product,
{ cascade: true, eager: true } //eager en true
)
pero este tipo de configuracio solo se puede hacer si estas usando findAll, findXXX etc no al usar "createQueryBuilder"
para estos casos de usa "leftJoinAndSelect"
Modulo
//CREAR PRODUCTO
import { IsArray, IsIn, IsInt, IsNumber, IsOptional,
IsPositive, IsString, MinLength
} from 'class-validator';
export class CreateProductDto {
@IsString()
@MinLength(1)
title: string;
@IsNumber()
@IsPositive()
@IsOptional()
price?: number;
@IsString()
@IsOptional()
description?: string;
@IsString()
@IsOptional()
slug?: string;
@IsInt()
@IsPositive()
@IsOptional()
stock?: number;
@IsString({ each: true })
@IsArray()
sizes: string[]
@IsIn(['men','women','kid','unisex'])
gender: string;
@IsString({ each: true })
@IsArray()
@IsOptional()
tags: string[];
@IsString({ each: true })
@IsArray()
@IsOptional()
images?: string[];
}
Servicio
import { BadRequestException, Injectable, InternalServerErrorException, Logger, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DataSource, Repository } from 'typeorm'; //Aqui se usa el Repositori de typeorm para enlazar las entidades.
import { CreateProductDto } from './dto/create-product.dto';
import { UpdateProductDto } from './dto/update-product.dto';
import { PaginationDto } from 'src/common/dtos/pagination.dto';
import { validate as isUUID } from 'uuid';
import { ProductImage, Product } from './entities';
@Injectable()
export class ProductsService {
private readonly logger = new Logger('ProductsService');
constructor(
@InjectRepository(Product)
private readonly productRepository: Repository<Product>, //enlazamos entidad de productos
@InjectRepository(ProductImage)
private readonly productImageRepository: Repository<ProductImage>, //Aqui se enlaza la entidad de imagenes
) {}
async create(createProductDto: CreateProductDto) {
try {
/*NOTA: lo que se hace
con est ainstruccion
images: images.map( image => this.productImageRepository.create({ url: image }) )
es decir que la propiedad imagen: se va retornar una instancia de tipo objeto productImageRepository para que cree el producto con la ralacion.
*/
const { images = [], ...productDetails } = createProductDto; //El product dto
const product = this.productRepository.create({
...productDetails,
images: images.map( image => this.productImageRepository.create({ url: image }) ) //Aqui no se espesifca el producto que relaciona las imagenes ya que typeORM lo hace
});
await this.productRepository.save( product );
return { ...product, images };
} catch (error) {
this.handleDBExceptions(error);
}
}
/*Mostar los productos con sus imagenes relacionadas con la siguiente instruccion.
relations: {
images: true,
}
*/
async findAll( paginationDto: PaginationDto ) {
const { limit = 10, offset = 0 } = paginationDto;
const products = await this.productRepository.find({
take: limit,
skip: offset,
relations: {
images: true,
}
})
/*Aqui lo unico que se hace con el codigo " return products.map" como cada producto regresa lo siguiente
{
prodi1:0
,prodi2:0
prodi3:0
images:[
{id:1,'url1'},
{id:2,'url1'}
]
}
se cambia a
{
prodi1:0
,prodi2:0
prodi3:0
images:[
'url1',
'url1'
]
}
*/
return products.map( ( product ) => ({
...product,
images: product.images.map( img => img.url )
}))
}
}
Entity
/*****PRODUCTOS****/
import { BeforeInsert, BeforeUpdate, Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
import { ProductImage } from './';
@Entity({ name: 'products' })
export class Product {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column('text', {
unique: true,
})
title: string;
@Column('float',{
default: 0
})
price: number;
@Column({
type: 'text',
nullable: true
})
description: string;
@Column('text', {
unique: true
})
slug: string;
@Column('int', {
default: 0
})
stock: number;
@Column('text',{
array: true
})
sizes: string[];
@Column('text')
gender: string;
@Column('text', {
array: true,
default: []
})
tags: string[];
// images un producto puede tener muchas IMAGENES
@OneToMany(
() => ProductImage,
(productImage) => productImage.product,
{ cascade: true, eager: true }
)
images?: ProductImage[];
@BeforeInsert()
checkSlugInsert() {
if ( !this.slug ) {
this.slug = this.title;
}
this.slug = this.slug
.toLowerCase()
.replaceAll(' ','_')
.replaceAll("'",'')
}
@BeforeUpdate()
checkSlugUpdate() {
this.slug = this.slug
.toLowerCase()
.replaceAll(' ','_')
.replaceAll("'",'')
}
}
/*****IMAGENES******/
import { Product } from './';
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
@Entity({ name: 'product_images' })
export class ProductImage {
@PrimaryGeneratedColumn()
id: number;
@Column('text')
url: string;
@ManyToOne(
() => Product,
( product ) => product.images,
{ onDelete: 'CASCADE' }
)
product: Product
}