fix(plugin-multi-tenant): prevent duplicate filters on referenced blocks (#13607)

Fixes https://github.com/payloadcms/payload/issues/13601

When using block references, tenant filter options were being applied
n+1 every time a block reference was used.
This commit is contained in:
Jarrod Flesch
2025-08-27 12:41:57 -04:00
committed by GitHub
parent 138938ec55
commit 03a00ca37d
2 changed files with 54 additions and 29 deletions

View File

@@ -143,6 +143,9 @@ export const multiTenantPlugin =
[[], []], [[], []],
) )
// used to track and not duplicate filterOptions on referenced blocks
const blockReferencesWithFilters: string[] = []
// used to validate enabled collection slugs // used to validate enabled collection slugs
const multiTenantCollectionsFound: string[] = [] const multiTenantCollectionsFound: string[] = []
@@ -157,28 +160,45 @@ export const multiTenantPlugin =
if (collectionSlugs.includes(foldersSlug)) { if (collectionSlugs.includes(foldersSlug)) {
multiTenantCollectionsFound.push(foldersSlug) multiTenantCollectionsFound.push(foldersSlug)
const overrides = pluginConfig.collections[foldersSlug]?.tenantFieldOverrides
? pluginConfig.collections[foldersSlug]?.tenantFieldOverrides
: pluginConfig.tenantField || {}
incomingConfig.folders = incomingConfig.folders || {} incomingConfig.folders = incomingConfig.folders || {}
incomingConfig.folders.collectionOverrides = incomingConfig.folders.collectionOverrides || [] incomingConfig.folders.collectionOverrides = incomingConfig.folders.collectionOverrides || []
incomingConfig.folders.collectionOverrides.push(({ collection }) => { incomingConfig.folders.collectionOverrides.push(({ collection }) => {
/** /**
* Add tenant field to enabled collections * Add filter options to all relationship fields
*/ */
const folderTenantField = tenantField({ addFilterOptionsToFields({
...(pluginConfig?.tenantField || {}), blockReferencesWithFilters,
name: tenantFieldName, config: incomingConfig,
debug: pluginConfig.debug, fields: collection.fields,
overrides, tenantEnabledCollectionSlugs: collectionSlugs,
tenantEnabledGlobalSlugs: globalCollectionSlugs,
tenantFieldName,
tenantsArrayFieldName, tenantsArrayFieldName,
tenantsArrayTenantFieldName, tenantsArrayTenantFieldName,
tenantsCollectionSlug, tenantsCollectionSlug,
unique: false,
}) })
collection.fields.unshift(folderTenantField)
if (pluginConfig.collections[foldersSlug]?.useBaseListFilter !== false) { const overrides = pluginConfig.collections[collection.slug]?.tenantFieldOverrides
? pluginConfig.collections[collection.slug]?.tenantFieldOverrides
: pluginConfig.tenantField || {}
/**
* Add tenant field to enabled collections
*/
collection.fields.unshift(
tenantField({
name: tenantFieldName,
debug: pluginConfig.debug,
overrides,
tenantsArrayFieldName,
tenantsArrayTenantFieldName,
tenantsCollectionSlug,
unique: false,
}),
)
const { useBaseFilter, useBaseListFilter } = pluginConfig.collections[collection.slug] || {}
if (useBaseFilter ?? useBaseListFilter ?? true) {
/** /**
* Add list filter to enabled collections * Add list filter to enabled collections
* - filters results by selected tenant * - filters results by selected tenant
@@ -314,9 +334,10 @@ export const multiTenantPlugin =
} }
/** /**
* Modify enabled collections * Add filter options to all relationship fields
*/ */
addFilterOptionsToFields({ addFilterOptionsToFields({
blockReferencesWithFilters,
config: incomingConfig, config: incomingConfig,
fields: collection.fields, fields: collection.fields,
tenantEnabledCollectionSlugs: collectionSlugs, tenantEnabledCollectionSlugs: collectionSlugs,
@@ -334,9 +355,7 @@ export const multiTenantPlugin =
/** /**
* Add tenant field to enabled collections * Add tenant field to enabled collections
*/ */
collection.fields.splice( collection.fields.unshift(
0,
0,
tenantField({ tenantField({
name: tenantFieldName, name: tenantFieldName,
debug: pluginConfig.debug, debug: pluginConfig.debug,
@@ -349,19 +368,14 @@ export const multiTenantPlugin =
) )
const { useBaseFilter, useBaseListFilter } = pluginConfig.collections[collection.slug] || {} const { useBaseFilter, useBaseListFilter } = pluginConfig.collections[collection.slug] || {}
if (useBaseFilter ?? useBaseListFilter ?? true) { if (useBaseFilter ?? useBaseListFilter ?? true) {
/** /**
* Add list filter to enabled collections * Add list filter to enabled collections
* - filters results by selected tenant * - filters results by selected tenant
*/ */
if (!collection.admin) { collection.admin = collection.admin || {}
collection.admin = {}
}
const baseFilter = collection.admin?.baseFilter ?? collection.admin?.baseListFilter
collection.admin.baseFilter = combineFilters({ collection.admin.baseFilter = combineFilters({
baseFilter, baseFilter: collection.admin?.baseFilter ?? collection.admin?.baseListFilter,
customFilter: (args) => customFilter: (args) =>
filterDocumentsByTenants({ filterDocumentsByTenants({
filterFieldName: tenantFieldName, filterFieldName: tenantFieldName,

View File

@@ -1,9 +1,10 @@
import type { Config, Field, RelationshipField, SanitizedConfig } from 'payload' import type { Block, Config, Field, RelationshipField, SanitizedConfig } from 'payload'
import { defaults } from '../defaults.js' import { defaults } from '../defaults.js'
import { filterDocumentsByTenants } from '../filters/filterDocumentsByTenants.js' import { filterDocumentsByTenants } from '../filters/filterDocumentsByTenants.js'
type AddFilterOptionsToFieldsArgs = { type AddFilterOptionsToFieldsArgs = {
blockReferencesWithFilters: string[]
config: Config | SanitizedConfig config: Config | SanitizedConfig
fields: Field[] fields: Field[]
tenantEnabledCollectionSlugs: string[] tenantEnabledCollectionSlugs: string[]
@@ -15,6 +16,7 @@ type AddFilterOptionsToFieldsArgs = {
} }
export function addFilterOptionsToFields({ export function addFilterOptionsToFields({
blockReferencesWithFilters,
config, config,
fields, fields,
tenantEnabledCollectionSlugs, tenantEnabledCollectionSlugs,
@@ -74,6 +76,7 @@ export function addFilterOptionsToFields({
field.type === 'group' field.type === 'group'
) { ) {
addFilterOptionsToFields({ addFilterOptionsToFields({
blockReferencesWithFilters,
config, config,
fields: field.fields, fields: field.fields,
tenantEnabledCollectionSlugs, tenantEnabledCollectionSlugs,
@@ -87,14 +90,21 @@ export function addFilterOptionsToFields({
if (field.type === 'blocks') { if (field.type === 'blocks') {
;(field.blockReferences ?? field.blocks).forEach((_block) => { ;(field.blockReferences ?? field.blocks).forEach((_block) => {
const block = let block: Block | undefined
typeof _block === 'string'
? // TODO: iterate over blocks mapped to block slug in v4, or pass through payload.blocks if (typeof _block === 'string') {
config?.blocks?.find((b) => b.slug === _block) if (blockReferencesWithFilters.includes(_block)) {
: _block return
}
block = config?.blocks?.find((b) => b.slug === _block)
blockReferencesWithFilters.push(_block)
} else {
block = _block
}
if (block?.fields) { if (block?.fields) {
addFilterOptionsToFields({ addFilterOptionsToFields({
blockReferencesWithFilters,
config, config,
fields: block.fields, fields: block.fields,
tenantEnabledCollectionSlugs, tenantEnabledCollectionSlugs,
@@ -111,6 +121,7 @@ export function addFilterOptionsToFields({
if (field.type === 'tabs') { if (field.type === 'tabs') {
field.tabs.forEach((tab) => { field.tabs.forEach((tab) => {
addFilterOptionsToFields({ addFilterOptionsToFields({
blockReferencesWithFilters,
config, config,
fields: tab.fields, fields: tab.fields,
tenantEnabledCollectionSlugs, tenantEnabledCollectionSlugs,