📚 Relation Validation Limit
🎯 Introducción
Section titled “🎯 Introducción”El util relation-validation-limit.ts es una herramienta para validar que las relaciones entre entidades en Strapi no excedan un número máximo permitido.
Quick Start
Section titled “Quick Start”Implementación básica en 3 pasos
Section titled “Implementación básica en 3 pasos”1. Importar el util
import { validateMaxRelations } from "../../../../utils/relation-validation-limit";2. Configurar la validación
const config = { collection: "api::mi-entidad.mi-entidad", populate: { miCampo: { populate: ["relaciones"], }, }, relations: ["miCampo.relaciones"], maxAllowed: 4, errorMessage: (contentType, maxAllowed) => `Máximo ${maxAllowed} relaciones permitidas en ${contentType}.`,};3. Aplicar en lifecycles
export default { async beforeCreate(event) { await validateMaxRelations(config, event.params, true); }, async beforeUpdate(event) { await validateMaxRelations(config, event.params, false); },};🔧 ¿Qué hace este util?
Section titled “🔧 ¿Qué hace este util?”Este util valida automáticamente que las relaciones (conexiones entre entidades) no excedan un límite máximo configurado. Funciona tanto para operaciones de creación como de actualización, y maneja diferentes tipos de operaciones de relación:
- Connect: Agregar nuevas relaciones
- Disconnect: Remover relaciones existentes
- Set: Reemplazar todas las relaciones con un nuevo conjunto
⚙️ Configuración
Section titled “⚙️ Configuración”Estructura de configuración
Section titled “Estructura de configuración”interface RelationValidationConfig { collection: string; // Nombre de la colección (ej: 'api::inspiria-serie.inspiria-serie') populate: object; // Configuración de población de datos relations: string[]; // Array de relaciones a validar maxAllowed: number; // Número máximo de relaciones permitidas errorMessage: (contentType: string, maxAllowed: number) => string;}Ejemplo de configuración básica
Section titled “Ejemplo de configuración básica”const config = { collection: "api::inspiria-serie.inspiria-serie", populate: { serieList: { populate: ["series"], }, }, relations: ["serieList.series"], maxAllowed: 4, errorMessage: (contentType: string, maxAllowed: number) => `Exceeded maximum allowed relations in ${contentType}. Maximum allowed is ${maxAllowed}.`,};🚀 Casos de uso
Section titled “🚀 Casos de uso”1. Validación en Inspiria Series
Section titled “1. Validación en Inspiria Series”const config = { collection: INSPIRIA_SERIES, populate: COMPONENTS_INSPIRIA_SERIES, relations: [RELATION_SERIES], // 'serieList.series' maxAllowed: MAX_ALLOWED_RELATION, // 4 errorMessage: ERROR_EXCEEDED_MAXIMUM_ALLOWED_RELATIONS,};2. Validación en Inspiria Live
Section titled “2. Validación en Inspiria Live”const config = { collection: INSPIRIA_LIVES, populate: COMPONENTS_INSPIRIA_LIVE, relations: [RELATION_LIVES], maxAllowed: MAX_ALLOWED_RELATION, errorMessage: ERROR_EXCEEDED_MAXIMUM_ALLOWED_RELATIONS,};3. Validación múltiple en Inspiria All Content
Section titled “3. Validación múltiple en Inspiria All Content”const config = { collection: INSPIRIA_ALL_CONTENT, populate: COMPONENTS_INSPIRIA_ALL_CONTENT, relations: [RELATION_PRODUCTS, RELATION_SERIES], // Múltiples relaciones maxAllowed: MAX_ALLOWED_RELATION, errorMessage: ERROR_EXCEEDED_MAXIMUM_ALLOWED_RELATIONS,};4. Validación en Osteocom Course Page
Section titled “4. Validación en Osteocom Course Page”const config = { collection: OSTEOCOM_COURSE_PAGE, populate: COMPONENTS_OSTEOCOM_COURSE_PAGE, relations: [RELATION_OSTEOCOM_PRODUCTS], maxAllowed: MAX_ALLOWED_RELATION, errorMessage: ERROR_EXCEEDED_MAXIMUM_ALLOWED_RELATIONS,};🔄 Implementación en Lifecycles
Section titled “🔄 Implementación en Lifecycles”Patrón estándar de implementación
Section titled “Patrón estándar de implementación”import { validateMaxRelations } from "../../../../utils/relation-validation-limit";
const config = { collection: "api::tu-entidad.tu-entidad", populate: { // Configuración de población }, relations: ["campo.relacion"], maxAllowed: 4, errorMessage: ERROR_EXCEEDED_MAXIMUM_ALLOWED_RELATIONS,};
export default { async beforeCreate(event) { await validateMaxRelations(config, event.params, true); },
async beforeUpdate(event) { await validateMaxRelations(config, event.params, false); },};Implementación con validaciones adicionales
Section titled “Implementación con validaciones adicionales”export default { async beforeCreate(event) { // Validación de límites de relación await validateMaxRelations(config, event.params, true);
// Otras validaciones (ej: validación de verticales) await validateProductVerticals( relationsCRMconfig, event.params, true, VERTICAL_INSPIRIA ); },
async beforeUpdate(event) { await validateMaxRelations(config, event.params, false); await validateProductVerticals( relationsCRMconfig, event.params, false, VERTICAL_INSPIRIA ); },};❌ Manejo de errores
Section titled “❌ Manejo de errores”Error estándar
Section titled “Error estándar”Cuando se excede el límite máximo, se lanza un error de validación:
// Error generado automáticamente"Exceeded maximum allowed relations in an item of serieList. Maximum allowed is 4.";Configuración de mensajes de error personalizados
Section titled “Configuración de mensajes de error personalizados”const config = { // ... otras propiedades errorMessage: (contentType: string, maxAllowed: number) => `¡Ups! Has intentado agregar demasiadas ${contentType}. El máximo permitido es ${maxAllowed}.`,};🔍 Casos especiales
Section titled “🔍 Casos especiales”1. Relaciones anidadas
Section titled “1. Relaciones anidadas”// Para relaciones como 'serieList.series'const relations = ["serieList.series"];// El util automáticamente:// - Extrae 'serieList' como containerKey// - Extrae 'series' como fieldKey2. Múltiples relaciones
Section titled “2. Múltiples relaciones”const config = { relations: [ "productList.products", // Primera relación "categoryList.categories", // Segunda relación "tagList.tags", // Tercera relación ], // Todas serán validadas con el mismo maxAllowed};3. Operaciones en arrays de componentes
Section titled “3. Operaciones en arrays de componentes”// Para arrays de componentes como:{ "componentArray": [ { "id": 1, "relations": { "connect": [{ "id": 1 }, { "id": 2 }] } }, { "relations": { "connect": [{ "id": 3 }, { "id": 4 }] } } ]}// Cada elemento del array es validado independientemente4. Validación en diferentes idiomas (localización)
Section titled “4. Validación en diferentes idiomas (localización)”// El util maneja automáticamente la localizaciónconst content = await getContentById( where.id, config.collection, config.populate, body.locale // Idioma del request);📝 Constantes utilizadas
Section titled “📝 Constantes utilizadas”Valores comunes
Section titled “Valores comunes”// Límite estándar utilizado en la mayoría de casosexport const MAX_ALLOWED_RELATION = 4;
// Mensaje de error estándarexport const ERROR_EXCEEDED_MAXIMUM_ALLOWED_RELATIONS = ( contentType: string, maxAllowed: number) => `Exceeded maximum allowed relations in an item of ${contentType}. Maximum allowed is ${maxAllowed}.`;Estructuras de relación comunes
Section titled “Estructuras de relación comunes”// Inspiriaexport const RELATION_SERIES = "serieList.series";export const RELATION_LIVES = "liveList.lives";export const RELATION_PRODUCTS = "productList.products";
// Osteocomexport const RELATION_OSTEOCOM_PRODUCTS = "osteocomProductsSliders.osteocomProducts";🎯 Mejores prácticas
Section titled “🎯 Mejores prácticas”- Configuración consistente: Usa las constantes definidas en lugar de valores hardcodeados
- Mensajes de error claros: Personaliza los mensajes para que sean entendibles por los usuarios finales
- Validación temprana: Siempre valida en
beforeCreateybeforeUpdate - Límites razonables: Establece límites que equilibren funcionalidad y rendimiento
- Documentación: Documenta claramente qué relaciones se están validando y por qué
🚨 Consideraciones importantes
Section titled “🚨 Consideraciones importantes”- El util funciona antes de que se guarden los datos en la base de datos
- Las validaciones son síncronas y bloquean la operación si fallan
- El util maneja automáticamente las operaciones de localización
- Las relaciones existentes se cargan automáticamente para operaciones de actualización
- El util es compatible con arrays de componentes y relaciones anidadas
🔧 Troubleshooting
Section titled “🔧 Troubleshooting”Problemas comunes y soluciones
Section titled “Problemas comunes y soluciones”❌ Error: “Cannot read property ‘length’ of undefined”
Section titled “❌ Error: “Cannot read property ‘length’ of undefined””Causa: La estructura de datos no coincide con la configuración del populate.
Solución:
// ❌ Incorrectopopulate: { wrongField: { populate: ["series"], }}
// ✅ Correctopopulate: { serieList: { // Debe coincidir con tu estructura de datos populate: ["series"], }}❌ Error: “Collection not found”
Section titled “❌ Error: “Collection not found””Causa: El nombre de la colección es incorrecto.
Solución:
// ❌ Incorrectocollection: "inspiria-serie";
// ✅ Correctocollection: "api::inspiria-serie.inspiria-serie";❌ Validación no se ejecuta
Section titled “❌ Validación no se ejecuta”Causa: El util no está importado o configurado correctamente en los lifecycles.
Solución:
// Verificar que esté en beforeCreate y beforeUpdateexport default { async beforeCreate(event) { await validateMaxRelations(config, event.params, true); // ✅ Último parámetro true }, async beforeUpdate(event) { await validateMaxRelations(config, event.params, false); // ✅ Último parámetro false },};❌ Error de límite incorrecto en arrays
Section titled “❌ Error de límite incorrecto en arrays”Causa: Confusión entre límite por elemento vs límite total.
Explicación:
// Si tienes un array de componentes:[ { relations: { connect: [1, 2] } }, // 2 relaciones ✅ { relations: { connect: [3, 4] } }, // 2 relaciones ✅ { relations: { connect: [5, 6, 7, 8, 9] } }, // 5 relaciones ❌];// El límite se aplica POR ELEMENTO del array, no al totalDebugging
Section titled “Debugging”Para debuggear problemas, puedes agregar logs temporales:
export default { async beforeCreate(event) { console.log("Datos recibidos:", JSON.stringify(event.params.data, null, 2)); await validateMaxRelations(config, event.params, true); },};