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
const multiTenantCollectionsFound: string[] = []
@@ -157,28 +160,45 @@ export const multiTenantPlugin =
if (collectionSlugs.includes(foldersSlug)) {
multiTenantCollectionsFound.push(foldersSlug)
const overrides = pluginConfig.collections[foldersSlug]?.tenantFieldOverrides
? pluginConfig.collections[foldersSlug]?.tenantFieldOverrides
: pluginConfig.tenantField || {}
incomingConfig.folders = incomingConfig.folders || {}
incomingConfig.folders.collectionOverrides = incomingConfig.folders.collectionOverrides || []
incomingConfig.folders.collectionOverrides.push(({ collection }) => {
/**
* Add tenant field to enabled collections
* Add filter options to all relationship fields
*/
const folderTenantField = tenantField({
...(pluginConfig?.tenantField || {}),
name: tenantFieldName,
debug: pluginConfig.debug,
overrides,
addFilterOptionsToFields({
blockReferencesWithFilters,
config: incomingConfig,
fields: collection.fields,
tenantEnabledCollectionSlugs: collectionSlugs,
tenantEnabledGlobalSlugs: globalCollectionSlugs,
tenantFieldName,
tenantsArrayFieldName,
tenantsArrayTenantFieldName,
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
* - filters results by selected tenant
@@ -314,9 +334,10 @@ export const multiTenantPlugin =
}
/**
* Modify enabled collections
* Add filter options to all relationship fields
*/
addFilterOptionsToFields({
blockReferencesWithFilters,
config: incomingConfig,
fields: collection.fields,
tenantEnabledCollectionSlugs: collectionSlugs,
@@ -334,9 +355,7 @@ export const multiTenantPlugin =
/**
* Add tenant field to enabled collections
*/
collection.fields.splice(
0,
0,
collection.fields.unshift(
tenantField({
name: tenantFieldName,
debug: pluginConfig.debug,
@@ -349,19 +368,14 @@ export const multiTenantPlugin =
)
const { useBaseFilter, useBaseListFilter } = pluginConfig.collections[collection.slug] || {}
if (useBaseFilter ?? useBaseListFilter ?? true) {
/**
* Add list filter to enabled collections
* - filters results by selected tenant
*/
if (!collection.admin) {
collection.admin = {}
}
const baseFilter = collection.admin?.baseFilter ?? collection.admin?.baseListFilter
collection.admin = collection.admin || {}
collection.admin.baseFilter = combineFilters({
baseFilter,
baseFilter: collection.admin?.baseFilter ?? collection.admin?.baseListFilter,
customFilter: (args) =>
filterDocumentsByTenants({
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 { filterDocumentsByTenants } from '../filters/filterDocumentsByTenants.js'
type AddFilterOptionsToFieldsArgs = {
blockReferencesWithFilters: string[]
config: Config | SanitizedConfig
fields: Field[]
tenantEnabledCollectionSlugs: string[]
@@ -15,6 +16,7 @@ type AddFilterOptionsToFieldsArgs = {
}
export function addFilterOptionsToFields({
blockReferencesWithFilters,
config,
fields,
tenantEnabledCollectionSlugs,
@@ -74,6 +76,7 @@ export function addFilterOptionsToFields({
field.type === 'group'
) {
addFilterOptionsToFields({
blockReferencesWithFilters,
config,
fields: field.fields,
tenantEnabledCollectionSlugs,
@@ -87,14 +90,21 @@ export function addFilterOptionsToFields({
if (field.type === 'blocks') {
;(field.blockReferences ?? field.blocks).forEach((_block) => {
const block =
typeof _block === 'string'
? // TODO: iterate over blocks mapped to block slug in v4, or pass through payload.blocks
config?.blocks?.find((b) => b.slug === _block)
: _block
let block: Block | undefined
if (typeof _block === 'string') {
if (blockReferencesWithFilters.includes(_block)) {
return
}
block = config?.blocks?.find((b) => b.slug === _block)
blockReferencesWithFilters.push(_block)
} else {
block = _block
}
if (block?.fields) {
addFilterOptionsToFields({
blockReferencesWithFilters,
config,
fields: block.fields,
tenantEnabledCollectionSlugs,
@@ -111,6 +121,7 @@ export function addFilterOptionsToFields({
if (field.type === 'tabs') {
field.tabs.forEach((tab) => {
addFilterOptionsToFields({
blockReferencesWithFilters,
config,
fields: tab.fields,
tenantEnabledCollectionSlugs,