diff --git a/packages/core/client/src/variables/VariablesProvider.tsx b/packages/core/client/src/variables/VariablesProvider.tsx index 8274052e60..547305b2f2 100644 --- a/packages/core/client/src/variables/VariablesProvider.tsx +++ b/packages/core/client/src/variables/VariablesProvider.tsx @@ -124,7 +124,7 @@ const VariablesProvider = ({ children }) => { } return item?.[key]; }); - current = _.flatten(await Promise.all(result)); + current = removeThroughCollectionFields(_.flatten(await Promise.all(result)), associationField); } else if (shouldToRequest(current[key]) && current.id != null && associationField?.target) { const url = `/${collectionName}/${current.id}/${key}:${getAction(associationField.type)}`; let data = null; @@ -149,9 +149,9 @@ const VariablesProvider = ({ children }) => { raw(current)[key] = data.data.data; } - current = getValuesByPath(current, key); + current = removeThroughCollectionFields(getValuesByPath(current, key), associationField); } else { - current = getValuesByPath(current, key); + current = removeThroughCollectionFields(getValuesByPath(current, key), associationField); } if (associationField?.target) { @@ -344,3 +344,26 @@ function mergeVariableToCollectionNameWithLocalVariables( return variablesStore; } + +/** + * 去除关系字段中的中间表字段。 + * 如果在创建新记录的时候,存在关系字段的中间表字段,提交的时候会报错,所以需要去除。 + * @param value + * @param associationField + * @returns + */ +export function removeThroughCollectionFields( + value: Record | Record[], + associationField: CollectionFieldOptions_deprecated, +) { + if (!associationField?.through || !value) { + return value; + } + + if (Array.isArray(value)) { + return value.map((item) => { + return _.omit(item, associationField.through); + }); + } + return _.omit(value, associationField.through); +} diff --git a/packages/core/client/src/variables/__tests__/removeThroughCollectionFields.test.tsx b/packages/core/client/src/variables/__tests__/removeThroughCollectionFields.test.tsx new file mode 100644 index 0000000000..56a9cebbf1 --- /dev/null +++ b/packages/core/client/src/variables/__tests__/removeThroughCollectionFields.test.tsx @@ -0,0 +1,80 @@ +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. + * For more information, please refer to: https://www.nocobase.com/agreement. + */ + +import { removeThroughCollectionFields } from '../VariablesProvider'; + +describe('removeThroughCollectionFields', () => { + it('should remove through collection fields from a single record', () => { + const value = { + id: 1, + name: 'John Doe', + throughField: 'Some value', + }; + const associationField = { + through: 'throughField', + }; + const result = removeThroughCollectionFields(value, associationField); + expect(result).toEqual({ + id: 1, + name: 'John Doe', + }); + }); + + it('should remove through collection fields from an array of records', () => { + const value = [ + { + id: 1, + name: 'John Doe', + throughField: 'Some value', + }, + { + id: 2, + name: 'Jane Smith', + throughField: 'Another value', + }, + ]; + const associationField = { + through: 'throughField', + }; + const result = removeThroughCollectionFields(value, associationField); + expect(result).toEqual([ + { + id: 1, + name: 'John Doe', + }, + { + id: 2, + name: 'Jane Smith', + }, + ]); + }); + + it('should return the original value if associationField.through is not defined', () => { + const value = { + id: 1, + name: 'John Doe', + }; + const associationField = {}; + const result = removeThroughCollectionFields(value, associationField); + expect(result).toEqual(value); + }); + + it('should return the original value if value is null or undefined', () => { + const associationField = { + through: 'throughField', + }; + let value = null; + let result = removeThroughCollectionFields(value, associationField); + expect(result).toBeNull(); + + value = undefined; + result = removeThroughCollectionFields(value, associationField); + expect(result).toBeUndefined(); + }); +}); diff --git a/packages/core/client/src/variables/__tests__/useVariables.test.tsx b/packages/core/client/src/variables/__tests__/useVariables.test.tsx index 1eb77e3a1e..83cd499f61 100644 --- a/packages/core/client/src/variables/__tests__/useVariables.test.tsx +++ b/packages/core/client/src/variables/__tests__/useVariables.test.tsx @@ -52,6 +52,13 @@ vi.mock('../../collection-manager', async () => { target: 'test', }; } + if (path === 'users.belongsToManyField') { + return { + type: 'belongsToMany', + target: 'test', + through: 'throughCollectionName', + }; + } if (path === 'local.belongsToField') { return { type: 'belongsTo', @@ -115,6 +122,20 @@ mockRequest.onGet('/users/0/hasManyField:list?pageSize=9999').reply(() => { }, ]; }); +mockRequest.onGet('/users/0/belongsToManyField:list?pageSize=9999').reply(() => { + return [ + 200, + { + data: [ + { + id: 0, + name: '$user.belongsToManyField', + throughCollectionName: 'throughCollectionName', + }, + ], + }, + ]; +}); mockRequest.onGet('/test/0/hasManyField:list?pageSize=9999').reply(() => { return [ 200, @@ -370,6 +391,21 @@ describe('useVariables', () => { }); }); + it('should remove through collection field', async () => { + const { result } = renderHook(() => useVariables(), { + wrapper: Providers, + }); + + await waitFor(async () => { + expect(await result.current.parseVariable('{{ $user.belongsToManyField }}')).toEqual([ + { + id: 0, + name: '$user.belongsToManyField', + }, + ]); + }); + }); + it('register variable', async () => { const { result } = renderHook(() => useVariables(), { wrapper: Providers,