PHP:如何将子资源JSON合并到主JSON指定关联字段
修正mergeSubResources函数实现关联资源合并
核心逻辑梳理
要实现关联资源的正确合并,核心是基于外键关系建立主资源与子资源的映射,通过索引化子资源提升匹配效率,同时覆盖一对一、一对多两种关联场景。
假设变量结构示例
先明确输入数据的典型结构,方便理解函数逻辑:
// $tablesStructures:定义表间外键关系 const $tablesStructures = { Seasons: { foreignKeys: { genre_id: { targetTable: 'Genres', targetKey: 'id' } } }, Genres: { // 若需要一对多(Genre对应多个Season),可在这里定义反向关联的外键映射 } }; // $subRes:待合并的关联资源集合 const $subRes = { Genres: [ { id: 1, name: 'Action' }, { id: 2, name: 'Drama' } ] }; // mainJson:主资源JSON const mainJson = { Seasons: [ { id: 101, title: 'Season 1', genre_id: 1 }, { id: 102, title: 'Season 2', genre_id: 2 } ] };
修正后的mergeSubResources函数
function mergeSubResources(mainJson, subRes, tablesStructures) { // 1. 为子资源创建索引Map,快速通过ID查找单个资源 const subResourceIndexes = {}; // 2. 为一对多场景创建按父ID分组的子资源映射 const subResourcesGroupedByParent = {}; // 预处理子资源,构建索引和分组 for (const [tableName, items] of Object.entries(subRes)) { // 构建单资源索引 subResourceIndexes[tableName] = new Map(items.map(item => [item.id, item])); // 构建一对多分组(基于子表的外键定义) const tableSchema = tablesStructures[tableName]; if (tableSchema?.foreignKeys) { for (const [foreignKey, relation] of Object.entries(tableSchema.foreignKeys)) { const groupKey = `${relation.targetTable}_${tableName}`; subResourcesGroupedByParent[groupKey] = subResourcesGroupedByParent[groupKey] || new Map(); items.forEach(item => { const parentId = item[foreignKey]; if (!parentId) return; if (!subResourcesGroupedByParent[groupKey].has(parentId)) { subResourcesGroupedByParent[groupKey].set(parentId, []); } subResourcesGroupedByParent[groupKey].get(parentId).push(item); }); } } } // 遍历主资源,合并关联子资源 for (const [mainTable, mainItems] of Object.entries(mainJson)) { const mainTableSchema = tablesStructures[mainTable]; // 处理一对一关联:主资源通过外键指向子资源 if (mainTableSchema?.foreignKeys) { for (const [foreignKey, relation] of Object.entries(mainTableSchema.foreignKeys)) { const targetTable = relation.targetTable; const targetIndex = subResourceIndexes[targetTable]; if (!targetIndex) continue; mainItems.forEach(item => { const targetId = item[foreignKey]; if (targetId == null) return; // 将子资源合并到主资源的对应字段(字段名可根据需求调整,这里用目标表名) item[targetTable] = targetIndex.get(targetId); }); } } // 处理一对多关联:多个子资源通过外键指向主资源 const relatedSubTable = Object.keys(subRes).find(table => { return tablesStructures[table]?.foreignKeys?.some(fk => fk.targetTable === mainTable); }); if (relatedSubTable) { const groupKey = `${mainTable}_${relatedSubTable}`; const groupedSubs = subResourcesGroupedByParent[groupKey]; if (groupedSubs) { mainItems.forEach(item => { item[relatedSubTable] = groupedSubs.get(item.id) || []; }); } } } return mainJson; }
关键修正点
- 索引化子资源:用
Map存储子资源,避免每次匹配都遍历数组,大幅提升性能 - 覆盖两种关联场景:同时支持一对一(主→子)和一对多(子→主)的合并逻辑
- 空值处理:跳过无外键值的主资源项,避免出现无效合并
- 可扩展性:字段名、关联规则可根据实际业务需求调整
调用示例
const mergedResult = mergeSubResources(mainJson, $subRes, $tablesStructures); console.log(mergedResult); // 输出结果: // { // Seasons: [ // { id: 101, title: 'Season 1', genre_id: 1, Genres: { id: 1, name: 'Action' } }, // { id: 102, title: 'Season 2', genre_id: 2, Genres: { id: 2, name: 'Drama' } } // ] // }
内容的提问来源于stack exchange,提问作者Conta
相关产品推荐
相关产品推荐

