Cuando una orden de compra necesita contener múltiples productos y, a su vez, un mismo producto puede aparecer en distintas órdenes, estamos frente a una relación muchos a muchos. Resolver este tipo de asociación en Sequelize requiere una tabla intermedia y el uso del método belongs to many. A continuación se explica paso a paso cómo implementar esta estructura de forma práctica.
¿Cómo se resuelve una relación muchos a muchos con una tabla ternaria?
Una tabla ternaria (también llamada tabla de unión o join table) es la pieza clave para conectar dos entidades que se relacionan de forma múltiple. En este caso, se crea una tabla llamada order_product que actúa como puente entre órdenes y productos [01:00].
La estructura de esta tabla contiene:
- Un ID propio.
- Un order ID que referencia a la tabla de órdenes.
- Un product ID que referencia a la tabla de productos.
- Un campo amount de tipo entero, que representa la cantidad de ese producto dentro de la orden [02:24].
Cada fila de order_product responde tres preguntas: ¿a qué orden pertenece?, ¿qué producto es? y ¿cuántas unidades se pidieron? Es como una línea dentro de una factura.
javascript
// Definición simplificada del esquema order_product
const OrderProductSchema = {
id: { /* ... */ },
orderId: {
field: 'order_id',
allowNull: false,
references: { model: OrderTable, key: 'id' },
},
productId: {
field: 'product_id',
allowNull: false,
references: { model: ProductTable, key: 'id' },
},
amount: {
allowNull: false,
type: DataTypes.INTEGER,
},
};
Es importante que productId no sea único, ya que un mismo producto puede aparecer en múltiples órdenes [02:48].
¿Cómo crear y ejecutar la migración de la tabla intermedia?
Una vez definido el modelo, se genera la migración desde la terminal [03:30]:
bash
npm run migrations:generate -- --name order-product
Dentro del archivo generado se importa el esquema de order_product y se aplica con createTable. Un detalle fundamental es respetar la convención de nombres en los archivos: todos los modelos deben terminar con .model en su nombre de archivo para que las importaciones funcionen correctamente [04:00].
Después de configurar la migración, se ejecuta:
bash
npm run migrations:run
Esto crea físicamente la tabla de unión entre órdenes y productos en la base de datos [05:08].
¿Cómo se configura belongs to many en Sequelize?
Con la tabla ya creada, el siguiente paso es registrar el modelo en el setup de Sequelize e indicar las asociaciones [05:20]. Aquí es donde entra belongs to many, el método que Sequelize provee para definir relaciones muchos a muchos.
La asociación se define dentro del modelo Order:
javascript
// Dentro de Order.associate
Order.belongsToMany(Product, {
through: OrderProduct,
foreignKey: 'order_id',
otherKey: 'product_id',
});
¿Qué significan los parámetros de esta asociación?
- through: indica a través de cuál tabla se resuelve la relación, en este caso
OrderProduct [06:38].
- foreignKey: define la llave foránea que corresponde al modelo donde se declara la asociación, aquí
order_id [07:08].
- otherKey: especifica la llave foránea de la otra entidad involucrada, es decir
product_id [07:20].
¿Por qué no basta con has many o belongs to?
En una relación uno a muchos se usa has many o belongs to porque existe un vínculo directo entre las tablas. En cambio, la relación muchos a muchos no tiene un vínculo directo entre las dos entidades principales; necesita un intermediario. Por eso Sequelize ofrece belongs to many, que internamente resuelve los joins a través de la tabla ternaria [06:10].
Si estos conceptos de normalización y diseño relacional resultan nuevos, vale la pena estudiar fundamentos de bases de datos relacionales para comprender las reglas de normalización y cómo se diseñan correctamente las relaciones entre tablas [02:58].
Con toda esta configuración lista, el modelo Order ya puede consultar los productos asociados y la cantidad de cada uno. ¿Has implementado relaciones muchos a muchos en tus proyectos? Comparte tu experiencia y las variantes que hayas encontrado.