> Manuales > Manual de NestJS

Cómo definir relaciones de N a M (de muchos a muchos) entre entidades de TypeORM en el contexto de una aplicación desarrollada con NestJS. Explicaciones para entender y saber manejar los decoradores @ManyToMany y @JoinTable.

Relaciones de muchos a muchos con TypeORM y Nest

En el pasado artículo introdujimos los mecanismos de creación de relaciones entre entidades en Nest. Vimos con detalle un ejemplo de implementación de una relación de "1 a muchos", así que en este artículo vamos a avanzar un poco más, para implementar relaciones de "muchos a muchos". Lo haremos con TypeORM en una aplicación Nest, mediante la aplicación de decoradores en las entidades y la asignación de las relaciones programáticamente, mediante código.

Damos por hecho que sabes qué es una relación de "muchos a muchos" o de "N a M" como a veces se la llama también. Si no es así, puedes consultar más sobre bases de datos.

Nuestras entidades en el modelo de base de datos

Para este ejemplo vamos a trabajar con una entidad para almacenar las posibles tallas de un producto. Los tamaños de los productos pueden ser cosas como "M", "S", "L". Nuestras entidades serían:

Es una relación de muchos a muchos porque un producto puede tener varias tallas y una talla puede estar presente en múltiples productos.

Creando la entidad de Size

Nos basaremos en la aplicación desarrollada a lo largo del Manual de Nest. En artículos anteriores ya habíamos creado la entidad Product, por lo que solamente necesitamos crear una nueva entidad, Size.

El código completo de nuestra entidad es este:

import { Column, Entity, ManyToMany, PrimaryGeneratedColumn } from 'typeorm';
import { Product } from './product.entity';

@Entity('sizes')
export class Size {
  @PrimaryGeneratedColumn()
  id: number;

  @Column('varchar', { length: 5 })
  size: string;
}

Simplemente hemos creado una entidad con su id autonumérico y un nombre para la talla en la columna "size".

Decorador @ManyToMany

En el código de la entidad Size nos falta por definir la relación que tendrá con la entidad de productos. Para ello usamos el decorador @ManyToMany de TypeORM.

Este decorador es parecido a otros de relaciones que hemos visto anteriormente, así que no te sorprenderá mucho.

Creando relación muchos a muchos en Size

Dentro de la entidad Size, definimos la relación con la entidad Product de esta manera:

@ManyToMany(() => Product, (product) => product.sizes)
products: Product[];

El decorador @ManyToMany() lo alimentamos con dos parámetros que son de tipo función.

Finalmente indicamos que la entidad Size tendrá una propiedad llamada "products" (en plural) que es un array de elementos de clase Product.

Creando relación muchos a muchos en Product

Dentro de la entidad Product, definimos la relación con la entidad Size de esta manera:

@JoinTable()
@ManyToMany(() => Size, (size) => size.products)
sizes: Size[];

El decorador @ManyToMany() funciona de manera análoga a lo que acabamos de explicar.

Decorador @JoinTable

La novedad en este código es la definición del decorador @JoinTable al crear la relación. Este decorador sirve para indicar la tabla principal en una relación de "muchos a muchos", o quizás más claro, indica a quién pertenece aquello que se está relacionando.

Por ejemplo, ¿Los productos tienen tallas? ¿o quizás las tallas tengan productos? En este caso está claro que los productos son los que tienen tallas, por lo tanto, el decorador @JoinTable se coloca en los productos.

Si no está tan claro cuál es la entidad principal, entonces puedes colocar @JoinTable en la entidad que prefieras.

Agregar la entidad Size al products.module.ts

Para que el framework tome esta entidad en consideración necesitamos agregarla al módulo desde donde vamos a usarla. En este caso teníamos un módulo de productos donde ya veníamos usando otras entidades, así que debemos añadir simplemente Size al array de entidades definido mediante el método forFeature() de TypeOrmModule.

Este es el código que nos quedará en el decorador @Module de products.module.ts.

@Module({
  imports: [TypeOrmModule.forFeature([Product, Review, Size])],
  controllers: [ProductsController, ReviewsController],
  providers: [ProductsService, ReviewsService]
})

Ahora, si arrancamos la aplicación veremos que se generan un par de tablas nuevas. La primera para almacenar los distintos nombres de tallas, en la tabla "sizes".

Luego tenemos la tabla que relaciona productos con tallas, que contiene los identificadores de la tabla de producto y la tabla de size.

Como es una relación de "muchos a muchos" sabes que genera una nueva tabla, llamada habitualmente tabla pivote, con los identificadores de los registros que se tienen que relacionar.

La tabla en nuestro caso se llama "products_sizes_sizes" y tendrá la siguiente estructura.

TABLA products_sizes_sizes:
+------------+---------+------+-----+---------+-------+
| Field      | Type    | Null | Key | Default | Extra |
+------------+---------+------+-----+---------+-------+
| productsId | int(11) | NO   | PRI | NULL    |       |
| sizesId    | int(11) | NO   | PRI | NULL    |       |
+------------+---------+------+-----+---------+-------+

El nombre de la tabla pivote puede variar dependiendo del caso. Siempre pone el nombre de la tabla principal (definida por que es la que tiene el @JoinTable, seguido del nombre de la relación en plural, y luego el nombre de la tabla relacionada. En este caso recuerda que nuestra entidad "Size" dijimos que el nombre de la tabla era "sizes" por el decorador @Entity("sizes").

Recuerda que las tablas se van a crear automáticamente solamente si tienes la propiedad synchronize con el valor true en la conexión definida en el módulo principal en el TypeOrmModule.forRoot().

Conclusión

Acabamos de aprender a definir las relaciones de muchos a muchos en nuestras entidades. Como has podido comprobar es un simple trabajo de configuración en TypeScript. Entran en nuevo varios conceptos y hay que usar los decoradores adecuados, pero el trabajo es muy simple.

Llegado a este punto tenemos la relación definida y debemos haber visto la tabla ya creada en nuestra base de datos. A partir de ahora solamente se trata de agregar datos relacionados, para lo que necesitaremos entrar con programación. Esta parte la veremos en la siguiente entrega del manual.

Miguel Angel Alvarez

Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...

Manual