mirror of
				https://github.com/strapi/strapi.git
				synced 2025-11-03 19:36:20 +00:00 
			
		
		
		
	look for duplicated inverseBy relationships
This commit is contained in:
		
							parent
							
								
									3e01e639d3
								
							
						
					
					
						commit
						e273f7f76d
					
				@ -11,6 +11,8 @@ const errors = require('./errors');
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// TODO: move back into strapi
 | 
					// TODO: move back into strapi
 | 
				
			||||||
const { transformContentTypes } = require('./utils/content-types');
 | 
					const { transformContentTypes } = require('./utils/content-types');
 | 
				
			||||||
 | 
					const { getJoinTableName } = require('./metadata/relations');
 | 
				
			||||||
 | 
					const types = require('./types');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Database {
 | 
					class Database {
 | 
				
			||||||
  constructor(config) {
 | 
					  constructor(config) {
 | 
				
			||||||
@ -72,9 +74,89 @@ class Database {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: Do the same for repeated mappedBy relations
 | 
				
			||||||
 | 
					const getLinks = ({ db }) => {
 | 
				
			||||||
 | 
					  const relationsToUpdate = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  db.metadata.forEach((contentType) => {
 | 
				
			||||||
 | 
					    const attributes = contentType.attributes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // For each relation type, add the joinTable name to tablesToUpdate
 | 
				
			||||||
 | 
					    Object.values(attributes).forEach((attribute) => {
 | 
				
			||||||
 | 
					      if (!types.isRelation(attribute.type)) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (attribute.inversedBy) {
 | 
				
			||||||
 | 
					        const invRelation = db.metadata.get(attribute.target).attributes[attribute.inversedBy];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Both relations use inversedBy
 | 
				
			||||||
 | 
					        if (invRelation?.inversedBy) {
 | 
				
			||||||
 | 
					          relationsToUpdate[attribute.joinTable.name] = {
 | 
				
			||||||
 | 
					            relation: attribute,
 | 
				
			||||||
 | 
					            invRelation,
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return Object.values(relationsToUpdate);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// const isLinkTableEmpty = async (db, linkTableName) => {
 | 
				
			||||||
 | 
					//   const result = await db.getConnection().count('* as count').from(linkTableName);
 | 
				
			||||||
 | 
					//   return result.count === 0;
 | 
				
			||||||
 | 
					// };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: move into strapi
 | 
					// TODO: move into strapi
 | 
				
			||||||
Database.transformContentTypes = transformContentTypes;
 | 
					Database.transformContentTypes = transformContentTypes;
 | 
				
			||||||
Database.init = async (config) => new Database(config);
 | 
					Database.init = async (config) => {
 | 
				
			||||||
 | 
					  const db = new Database(config);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // TODO: Create validations folder for this.
 | 
				
			||||||
 | 
					  const links = getLinks({ db });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const errorList = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (const { relation, invRelation } of links) {
 | 
				
			||||||
 | 
					    // Generate the join table name based on the relation target
 | 
				
			||||||
 | 
					    // table and attribute name.
 | 
				
			||||||
 | 
					    const joinTableName = getJoinTableName(
 | 
				
			||||||
 | 
					      db.metadata.get(relation.target).tableName,
 | 
				
			||||||
 | 
					      relation.inversedBy
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const contentType = db.metadata.get(invRelation.target);
 | 
				
			||||||
 | 
					    const invContentType = db.metadata.get(relation.target);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // If both sides use inversedBy
 | 
				
			||||||
 | 
					    // TODO: Same for mappedBy
 | 
				
			||||||
 | 
					    if (relation.inversedBy && invRelation.inversedBy) {
 | 
				
			||||||
 | 
					      // If the generated join table name is the same as the one assigned in relation.joinTable,
 | 
				
			||||||
 | 
					      // relation is on the inversed side of the bidirectional relation.
 | 
				
			||||||
 | 
					      // and the other is on the owner side.
 | 
				
			||||||
 | 
					      if (joinTableName === relation.joinTable.name) {
 | 
				
			||||||
 | 
					        errorList.push(
 | 
				
			||||||
 | 
					          `Error on attribute "${invRelation.inversedBy}" in model "${invContentType.tableName}"(${invContentType.uid}):` +
 | 
				
			||||||
 | 
					            ` One of the sides of the relationship must be the owning side. You should use mappedBy` +
 | 
				
			||||||
 | 
					            ` instead of inversedBy in the relation "${invRelation.inversedBy}".`
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        errorList.push(
 | 
				
			||||||
 | 
					          `Error on attribute "${relation.inversedBy}" in model "${contentType.tableName}"(${contentType.uid}):` +
 | 
				
			||||||
 | 
					            ` One of the sides of the relationship must be the owning side. You should use mappedBy` +
 | 
				
			||||||
 | 
					            ` instead of inversedBy in the relation "${relation.inversedBy}".`
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (errorList.length > 0) {
 | 
				
			||||||
 | 
					    errorList.forEach((error) => strapi.log.error(error));
 | 
				
			||||||
 | 
					    throw new Error('There are errors in some of your models. Please check the logs above.');
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return db;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  Database,
 | 
					  Database,
 | 
				
			||||||
 | 
				
			|||||||
@ -16,6 +16,8 @@ const isAnyToMany = (attribute) => ['oneToMany', 'manyToMany'].includes(attribut
 | 
				
			|||||||
const isBidirectional = (attribute) => hasInversedBy(attribute) || hasMappedBy(attribute);
 | 
					const isBidirectional = (attribute) => hasInversedBy(attribute) || hasMappedBy(attribute);
 | 
				
			||||||
const isOwner = (attribute) => !isBidirectional(attribute) || hasInversedBy(attribute);
 | 
					const isOwner = (attribute) => !isBidirectional(attribute) || hasInversedBy(attribute);
 | 
				
			||||||
const shouldUseJoinTable = (attribute) => attribute.useJoinTable !== false;
 | 
					const shouldUseJoinTable = (attribute) => attribute.useJoinTable !== false;
 | 
				
			||||||
 | 
					const getJoinTableName = (tableName, attributeName) =>
 | 
				
			||||||
 | 
					  _.snakeCase(`${tableName}_${attributeName}_links`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Creates a oneToOne relation metadata
 | 
					 * Creates a oneToOne relation metadata
 | 
				
			||||||
@ -397,7 +399,7 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
 | 
				
			|||||||
    throw new Error(`Unknown target ${attribute.target}`);
 | 
					    throw new Error(`Unknown target ${attribute.target}`);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const joinTableName = _.snakeCase(`${meta.tableName}_${attributeName}_links`);
 | 
					  const joinTableName = getJoinTableName(meta.tableName, attributeName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const joinColumnName = _.snakeCase(`${meta.singularName}_id`);
 | 
					  const joinColumnName = _.snakeCase(`${meta.singularName}_id`);
 | 
				
			||||||
  let inverseJoinColumnName = _.snakeCase(`${targetMeta.singularName}_id`);
 | 
					  let inverseJoinColumnName = _.snakeCase(`${targetMeta.singularName}_id`);
 | 
				
			||||||
@ -560,4 +562,5 @@ module.exports = {
 | 
				
			|||||||
  isAnyToMany,
 | 
					  isAnyToMany,
 | 
				
			||||||
  hasOrderColumn,
 | 
					  hasOrderColumn,
 | 
				
			||||||
  hasInverseOrderColumn,
 | 
					  hasInverseOrderColumn,
 | 
				
			||||||
 | 
					  getJoinTableName,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user