From d8dd73b669a5ef44630f88350adcf3f9c3df82df Mon Sep 17 00:00:00 2001 From: YANG QIA <2013xile@gmail.com> Date: Mon, 28 Oct 2024 10:19:45 +0800 Subject: [PATCH] fix(tree): issue where paths are not updated when disassociate children (#5522) --- .../src/server/__tests__/path.test.ts | 42 ++++++++ .../src/server/plugin.ts | 97 ++++++++++++------- 2 files changed, 102 insertions(+), 37 deletions(-) diff --git a/packages/plugins/@nocobase/plugin-collection-tree/src/server/__tests__/path.test.ts b/packages/plugins/@nocobase/plugin-collection-tree/src/server/__tests__/path.test.ts index c53e3e27fb..894b68943b 100644 --- a/packages/plugins/@nocobase/plugin-collection-tree/src/server/__tests__/path.test.ts +++ b/packages/plugins/@nocobase/plugin-collection-tree/src/server/__tests__/path.test.ts @@ -570,4 +570,46 @@ describe('tree path test', () => { // await treeCollection.removeFromDb(); // expect(await db.getCollection(name).existsInDb()).toBeFalsy(); // }) + + it('should update paths when remove children', async () => { + const data = await treeCollection.repository.create({ + values: { + name: 'a1', + children: [ + { + name: 'b1', + }, + { + name: 'b2', + }, + ], + }, + }); + const b1 = data.get('children')[0]; + const b2 = data.get('children')[1]; + const tks = [b1.get(treeCollection.filterTargetKey), b2.get(treeCollection.filterTargetKey)]; + const paths = await db.getRepository(name).find({ + filter: { + [nodePkColumnName]: { + $in: tks, + }, + }, + }); + expect(paths.length).toBe(2); + expect(paths[0].get('path')).toBe('/1/2'); + expect(paths[1].get('path')).toBe('/1/3'); + // @ts-ignore + await db.getRepository(`${treeCollection.name}.children`, data.get(treeCollection.filterTargetKey)).remove(tks); + const paths2 = await db.getRepository(name).find({ + filter: { + [nodePkColumnName]: { + $in: tks, + }, + }, + }); + console.log(paths2); + expect(paths2.length).toBe(2); + expect(paths2[0].get('path')).toBe('/2'); + expect(paths2[1].get('path')).toBe('/3'); + }); }); diff --git a/packages/plugins/@nocobase/plugin-collection-tree/src/server/plugin.ts b/packages/plugins/@nocobase/plugin-collection-tree/src/server/plugin.ts index 7fce8610e4..10ab1412f2 100644 --- a/packages/plugins/@nocobase/plugin-collection-tree/src/server/plugin.ts +++ b/packages/plugins/@nocobase/plugin-collection-tree/src/server/plugin.ts @@ -14,11 +14,6 @@ import lodash from 'lodash'; import { Transaction } from 'sequelize'; import { TreeCollection } from './tree-collection'; -const getFilterTargetKey = (model: Model) => { - // @ts-ignore - return model.constructor.collection.filterTargetKey; -}; - class PluginCollectionTreeServer extends Plugin { async beforeLoad() { const condition = (options) => { @@ -55,7 +50,7 @@ class PluginCollectionTreeServer extends Plugin { //afterCreate this.db.on(`${collection.name}.afterCreate`, async (model: Model, options) => { const { transaction } = options; - const tk = getFilterTargetKey(model); + const tk = collection.filterTargetKey; let path = `/${model.get(tk)}`; path = await this.getTreePath(model, path, collection, name, transaction); const rootPk = path.split('/')[1]; @@ -71,49 +66,35 @@ class PluginCollectionTreeServer extends Plugin { //afterUpdate this.db.on(`${collection.name}.afterUpdate`, async (model: Model, options) => { - const tk = getFilterTargetKey(model); + const tk = collection.filterTargetKey; // only update parentId and filterTargetKey if (!(model._changed.has(tk) || model._changed.has(parentForeignKey))) { return; } const { transaction } = options; - let path = `/${model.get(tk)}`; - path = await this.getTreePath(model, path, collection, name, transaction); - const collectionTreePath = this.db.getCollection(name); - const nodePkColumnName = collectionTreePath.getField('nodePk').columnName(); - const pathData = await this.app.db.getRepository(name).findOne({ - filter: { - [nodePkColumnName]: model.get(tk), - }, - transaction, - }); + await this.updateTreePath(model, collection, name, transaction); + }); - const relatedNodes = await this.app.db.getRepository(name).find({ - filter: { - path: { - $startsWith: `${pathData.get('path')}`, - }, + // after remove + this.db.on(`${collection.name}.afterBulkUpdate`, async (options) => { + const tk = collection.filterTargetKey; + if (!(options.where && options.where[tk])) { + return; + } + const instances = await this.db.getRepository(collection.name).find({ + where: { + [tk]: options.where[tk], }, - transaction, + transaction: options.transaction, }); - const rootPk = path.split('/')[1]; - for (const node of relatedNodes) { - await this.app.db.getRepository(name).update({ - values: { - path: node.get('path').replace(`${pathData.get('path')}`, path), - rootPk: rootPk ? Number(rootPk) : null, - }, - filter: { - [nodePkColumnName]: node.get('nodePk'), - }, - transaction, - }); + for (const model of instances) { + await this.updateTreePath(model, collection, name, options.transaction); } }); //afterDestroy this.db.on(`${collection.name}.afterDestroy`, async (model: Model, options: DestroyOptions) => { - const tk = getFilterTargetKey(model); + const tk = collection.filterTargetKey; await this.app.db.getRepository(name).destroy({ filter: { nodePk: model.get(tk), @@ -164,7 +145,7 @@ class PluginCollectionTreeServer extends Plugin { pathCollectionName: string, transaction?: Transaction, ) { - const tk = getFilterTargetKey(model); + const tk = collection.filterTargetKey; const parentForeignKey = collection.treeParentField?.foreignKey || 'parentId'; if (model.get(parentForeignKey) && model.get(parentForeignKey) !== null) { const parent = await this.app.db.getRepository(collection.name).findOne({ @@ -195,6 +176,48 @@ class PluginCollectionTreeServer extends Plugin { } return path; } + + private async updateTreePath( + model: Model, + collection: Collection, + pathCollectionName: string, + transaction: Transaction, + ) { + const tk = collection.filterTargetKey; + let path = `/${model.get(tk)}`; + path = await this.getTreePath(model, path, collection, pathCollectionName, transaction); + const collectionTreePath = this.db.getCollection(pathCollectionName); + const nodePkColumnName = collectionTreePath.getField('nodePk').columnName(); + const pathData = await this.app.db.getRepository(pathCollectionName).findOne({ + filter: { + [nodePkColumnName]: model.get(tk), + }, + transaction, + }); + + const relatedNodes = await this.app.db.getRepository(pathCollectionName).find({ + filter: { + path: { + $startsWith: `${pathData.get('path')}`, + }, + }, + transaction, + }); + const rootPk = path.split('/')[1]; + for (const node of relatedNodes) { + const newPath = node.get('path').replace(pathData.get('path'), path); + await this.app.db.getRepository(pathCollectionName).update({ + values: { + path: newPath, + rootPk: rootPk ? Number(rootPk) : null, + }, + filter: { + [nodePkColumnName]: node.get('nodePk'), + }, + transaction, + }); + } + } } export default PluginCollectionTreeServer;