fix: join field with the target relationship inside localized array (#10621)
Fixes https://github.com/payloadcms/payload/issues/10356
This commit is contained in:
@@ -151,11 +151,19 @@ export const buildJoinAggregation = async ({
|
||||
join.field.localized && adapter.payload.config.localization && locale ? `.${locale}` : ''
|
||||
const as = `${versions ? `version.${join.joinPath}` : join.joinPath}${localeSuffix}`
|
||||
|
||||
let foreignField: string
|
||||
|
||||
if (join.getForeignPath) {
|
||||
foreignField = `${join.getForeignPath({ locale })}${polymorphicSuffix}`
|
||||
} else {
|
||||
foreignField = `${join.field.on}${polymorphicSuffix}`
|
||||
}
|
||||
|
||||
aggregate.push(
|
||||
{
|
||||
$lookup: {
|
||||
as: `${as}.docs`,
|
||||
foreignField: `${join.field.on}${localeSuffix}${polymorphicSuffix}`,
|
||||
foreignField,
|
||||
from: adapter.collections[slug].collection.name,
|
||||
localField: versions ? 'parent' : '_id',
|
||||
pipeline,
|
||||
|
||||
@@ -517,6 +517,7 @@ export type SanitizedJoin = {
|
||||
* The field configuration defining the join
|
||||
*/
|
||||
field: JoinField
|
||||
getForeignPath?(args: { locale?: TypedLocale }): string
|
||||
/**
|
||||
* The path of the join field in dot notation
|
||||
*/
|
||||
|
||||
@@ -39,6 +39,7 @@ export const sanitizeJoinField = ({
|
||||
const pathSegments = field.on.split('.') // Split the schema path into segments
|
||||
let currentSegmentIndex = 0
|
||||
|
||||
let localized = false
|
||||
// Traverse fields and match based on the schema path
|
||||
traverseFields({
|
||||
callback: ({ field, next }) => {
|
||||
@@ -48,6 +49,27 @@ export const sanitizeJoinField = ({
|
||||
const currentSegment = pathSegments[currentSegmentIndex]
|
||||
// match field on path segments
|
||||
if ('name' in field && field.name === currentSegment) {
|
||||
if ('localized' in field && field.localized) {
|
||||
localized = true
|
||||
const fieldIndex = currentSegmentIndex
|
||||
|
||||
join.getForeignPath = ({ locale }) => {
|
||||
return pathSegments.reduce((acc, segment, index) => {
|
||||
let result = `${acc}${segment}`
|
||||
|
||||
if (index === fieldIndex) {
|
||||
result = `${result}.${locale}`
|
||||
}
|
||||
|
||||
if (index !== pathSegments.length - 1) {
|
||||
result = `${result}.`
|
||||
}
|
||||
|
||||
return result
|
||||
}, '')
|
||||
}
|
||||
}
|
||||
|
||||
// Check if this is the last segment in the path
|
||||
if (
|
||||
(currentSegmentIndex === pathSegments.length - 1 &&
|
||||
@@ -78,7 +100,8 @@ export const sanitizeJoinField = ({
|
||||
join.targetField = joinRelationship
|
||||
|
||||
// override the join field localized property to use whatever the relationship field has
|
||||
field.localized = joinRelationship.localized
|
||||
// or if it's nested to a localized array / blocks / tabs / group
|
||||
field.localized = localized
|
||||
// override the join field hasMany property to use whatever the relationship field has
|
||||
field.hasMany = joinRelationship.hasMany
|
||||
|
||||
|
||||
@@ -109,15 +109,36 @@ export const traverseFields = ({
|
||||
if (field.type === 'tabs' && 'tabs' in field) {
|
||||
for (const tab of field.tabs) {
|
||||
let tabRef = ref
|
||||
|
||||
if (skip) {
|
||||
return false
|
||||
}
|
||||
|
||||
if ('name' in tab && tab.name) {
|
||||
if (!ref[tab.name] || typeof ref[tab.name] !== 'object') {
|
||||
if (fillEmpty) {
|
||||
ref[tab.name] = {}
|
||||
if (tab.localized) {
|
||||
ref[tab.name] = { en: {} }
|
||||
} else {
|
||||
ref[tab.name] = {}
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
callback &&
|
||||
callback({
|
||||
field: { ...tab, type: 'tab' },
|
||||
next,
|
||||
parentRef: currentParentRef,
|
||||
ref: tabRef,
|
||||
})
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
tabRef = tabRef[tab.name]
|
||||
|
||||
if (tab.localized) {
|
||||
@@ -132,29 +153,34 @@ export const traverseFields = ({
|
||||
})
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
callback &&
|
||||
callback({
|
||||
field: { ...tab, type: 'tab' },
|
||||
next,
|
||||
parentRef: currentParentRef,
|
||||
ref: tabRef,
|
||||
})
|
||||
) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
callback &&
|
||||
callback({
|
||||
field: { ...tab, type: 'tab' },
|
||||
next,
|
||||
if (!tab.localized) {
|
||||
traverseFields({
|
||||
callback,
|
||||
fields: tab.fields,
|
||||
fillEmpty,
|
||||
parentRef: currentParentRef,
|
||||
ref: tabRef,
|
||||
})
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
traverseFields({
|
||||
callback,
|
||||
fields: tab.fields,
|
||||
fillEmpty,
|
||||
parentRef: currentParentRef,
|
||||
ref: tabRef,
|
||||
})
|
||||
if (skip) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
@@ -166,10 +192,18 @@ export const traverseFields = ({
|
||||
if (!ref[field.name]) {
|
||||
if (fillEmpty) {
|
||||
if (field.type === 'group') {
|
||||
ref[field.name] = {}
|
||||
if (field.localized) {
|
||||
ref[field.name] = {
|
||||
en: {},
|
||||
}
|
||||
} else {
|
||||
ref[field.name] = {}
|
||||
}
|
||||
} else if (field.type === 'array' || field.type === 'blocks') {
|
||||
if (field.localized) {
|
||||
ref[field.name] = {}
|
||||
ref[field.name] = {
|
||||
en: [],
|
||||
}
|
||||
} else {
|
||||
ref[field.name] = []
|
||||
}
|
||||
|
||||
@@ -109,6 +109,12 @@ export const Categories: CollectionConfig = {
|
||||
collection: 'posts',
|
||||
on: 'array.category',
|
||||
},
|
||||
{
|
||||
name: 'localizedArrayPosts',
|
||||
type: 'join',
|
||||
collection: 'posts',
|
||||
on: 'localizedArray.category',
|
||||
},
|
||||
{
|
||||
name: 'blocksPosts',
|
||||
type: 'join',
|
||||
|
||||
@@ -104,6 +104,18 @@ export const Posts: CollectionConfig = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'localizedArray',
|
||||
type: 'array',
|
||||
localized: true,
|
||||
fields: [
|
||||
{
|
||||
name: 'category',
|
||||
type: 'relationship',
|
||||
relationTo: categoriesSlug,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'blocks',
|
||||
type: 'blocks',
|
||||
|
||||
@@ -115,6 +115,7 @@ describe('Joins Field', () => {
|
||||
camelCaseCategory: category.id,
|
||||
},
|
||||
array: [{ category: category.id }],
|
||||
localizedArray: [{ category: category.id }],
|
||||
blocks: [{ blockType: 'block', category: category.id }],
|
||||
})
|
||||
}
|
||||
@@ -214,6 +215,16 @@ describe('Joins Field', () => {
|
||||
expect(categoryWithPosts.arrayPosts.docs).toBeDefined()
|
||||
})
|
||||
|
||||
it('should populate joins with localized array relationships', async () => {
|
||||
const categoryWithPosts = await payload.findByID({
|
||||
id: category.id,
|
||||
collection: categoriesSlug,
|
||||
})
|
||||
|
||||
expect(categoryWithPosts.localizedArrayPosts.docs).toBeDefined()
|
||||
expect(categoryWithPosts.localizedArrayPosts.docs).toHaveLength(10)
|
||||
})
|
||||
|
||||
it('should populate joins with blocks relationships', async () => {
|
||||
const categoryWithPosts = await payload.findByID({
|
||||
id: category.id,
|
||||
|
||||
@@ -41,6 +41,7 @@ export interface Config {
|
||||
'group.relatedPosts': 'posts';
|
||||
'group.camelCasePosts': 'posts';
|
||||
arrayPosts: 'posts';
|
||||
localizedArrayPosts: 'posts';
|
||||
blocksPosts: 'posts';
|
||||
polymorphic: 'posts';
|
||||
polymorphics: 'posts';
|
||||
@@ -199,6 +200,12 @@ export interface Post {
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
localizedArray?:
|
||||
| {
|
||||
category?: (string | null) | Category;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
blocks?:
|
||||
| {
|
||||
category?: (string | null) | Category;
|
||||
@@ -272,6 +279,10 @@ export interface Category {
|
||||
docs?: (string | Post)[] | null;
|
||||
hasNextPage?: boolean | null;
|
||||
} | null;
|
||||
localizedArrayPosts?: {
|
||||
docs?: (string | Post)[] | null;
|
||||
hasNextPage?: boolean | null;
|
||||
} | null;
|
||||
blocksPosts?: {
|
||||
docs?: (string | Post)[] | null;
|
||||
hasNextPage?: boolean | null;
|
||||
@@ -649,6 +660,12 @@ export interface PostsSelect<T extends boolean = true> {
|
||||
category?: T;
|
||||
id?: T;
|
||||
};
|
||||
localizedArray?:
|
||||
| T
|
||||
| {
|
||||
category?: T;
|
||||
id?: T;
|
||||
};
|
||||
blocks?:
|
||||
| T
|
||||
| {
|
||||
@@ -680,6 +697,7 @@ export interface CategoriesSelect<T extends boolean = true> {
|
||||
camelCasePosts?: T;
|
||||
};
|
||||
arrayPosts?: T;
|
||||
localizedArrayPosts?: T;
|
||||
blocksPosts?: T;
|
||||
polymorphic?: T;
|
||||
polymorphics?: T;
|
||||
|
||||
Reference in New Issue
Block a user