fix: deep querying, handle getLocalizedPaths for blocks (#10187)
Fixes https://github.com/payloadcms/payload/issues/10126 Properly handles the `getLocalizedPaths` function for blocks. Previously, using collection with this config: ``` { slug: 'deep-nested', fields: [ { type: 'tabs', tabs: [ { name: 'content', fields: [ { type: 'blocks', name: 'blocks', blocks: [ { slug: 'testBlock', fields: [ { type: 'tabs', tabs: [ { name: 'meta', fields: [ { type: 'relationship', relationTo: 'movies', name: 'movie', }, ], }, ], }, ], }, ], }, ], }, ], }, ], }, ``` The following query didn't work in MongoDB: ```ts const result = await payload.find({ collection: 'deep-nested', where: { 'content.blocks.meta.movie': { equals: movie.id, }, }, }) ```
This commit is contained in:
@@ -45,13 +45,32 @@ export function getLocalizedPaths({
|
|||||||
|
|
||||||
let fieldsToSearch: FlattenedField[]
|
let fieldsToSearch: FlattenedField[]
|
||||||
|
|
||||||
|
let matchedField: FlattenedField
|
||||||
|
|
||||||
|
if (lastIncompletePath?.field?.type === 'blocks') {
|
||||||
|
if (segment === 'blockType') {
|
||||||
|
matchedField = {
|
||||||
|
name: 'blockType',
|
||||||
|
type: 'text',
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const block of lastIncompletePath.field.blocks) {
|
||||||
|
matchedField = block.flattenedFields.find((field) => field.name === segment)
|
||||||
|
if (matchedField) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (lastIncompletePath?.field && 'flattenedFields' in lastIncompletePath.field) {
|
if (lastIncompletePath?.field && 'flattenedFields' in lastIncompletePath.field) {
|
||||||
fieldsToSearch = lastIncompletePath.field.flattenedFields
|
fieldsToSearch = lastIncompletePath.field.flattenedFields
|
||||||
} else {
|
} else {
|
||||||
fieldsToSearch = lastIncompletePath.fields
|
fieldsToSearch = lastIncompletePath.fields
|
||||||
}
|
}
|
||||||
|
|
||||||
const matchedField = fieldsToSearch.find((field) => field.name === segment)
|
matchedField = fieldsToSearch.find((field) => field.name === segment)
|
||||||
|
}
|
||||||
|
|
||||||
lastIncompletePath.field = matchedField
|
lastIncompletePath.field = matchedField
|
||||||
|
|
||||||
if (currentPath === 'globalType' && globalSlug) {
|
if (currentPath === 'globalType' && globalSlug) {
|
||||||
@@ -94,7 +113,6 @@ export function getLocalizedPaths({
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (matchedField.type) {
|
switch (matchedField.type) {
|
||||||
case 'blocks':
|
|
||||||
case 'json':
|
case 'json':
|
||||||
case 'richText': {
|
case 'richText': {
|
||||||
const upcomingSegments = pathSegments.slice(i + 1).join('.')
|
const upcomingSegments = pathSegments.slice(i + 1).join('.')
|
||||||
|
|||||||
@@ -387,6 +387,47 @@ export default buildConfigWithDefaults({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
slug: 'deep-nested',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'tabs',
|
||||||
|
tabs: [
|
||||||
|
{
|
||||||
|
name: 'content',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'blocks',
|
||||||
|
name: 'blocks',
|
||||||
|
blocks: [
|
||||||
|
{
|
||||||
|
slug: 'testBlock',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'tabs',
|
||||||
|
tabs: [
|
||||||
|
{
|
||||||
|
name: 'meta',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'relationship',
|
||||||
|
relationTo: 'movies',
|
||||||
|
name: 'movie',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
onInit: async (payload) => {
|
onInit: async (payload) => {
|
||||||
await payload.create({
|
await payload.create({
|
||||||
|
|||||||
@@ -363,6 +363,38 @@ describe('Relationships', () => {
|
|||||||
expect(docs[0].id).toBe(doc.id)
|
expect(docs[0].id).toBe(doc.id)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should allow querying within tabs-blocks-tabs', async () => {
|
||||||
|
const movie = await payload.create({ collection: 'movies', data: { name: 'Pulp Fiction' } })
|
||||||
|
|
||||||
|
const { id } = await payload.create({
|
||||||
|
collection: 'deep-nested',
|
||||||
|
data: {
|
||||||
|
content: {
|
||||||
|
blocks: [
|
||||||
|
{
|
||||||
|
blockType: 'testBlock',
|
||||||
|
meta: {
|
||||||
|
movie: movie.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await payload.find({
|
||||||
|
collection: 'deep-nested',
|
||||||
|
where: {
|
||||||
|
'content.blocks.meta.movie': {
|
||||||
|
equals: movie.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.totalDocs).toBe(1)
|
||||||
|
expect(result.docs[0].id).toBe(id)
|
||||||
|
})
|
||||||
|
|
||||||
describe('Custom ID', () => {
|
describe('Custom ID', () => {
|
||||||
it('should query a custom id relation', async () => {
|
it('should query a custom id relation', async () => {
|
||||||
const { customIdRelation } = await restClient
|
const { customIdRelation } = await restClient
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export interface Config {
|
|||||||
'rels-to-pages': RelsToPage;
|
'rels-to-pages': RelsToPage;
|
||||||
'rels-to-pages-and-custom-text-ids': RelsToPagesAndCustomTextId;
|
'rels-to-pages-and-custom-text-ids': RelsToPagesAndCustomTextId;
|
||||||
'object-writes': ObjectWrite;
|
'object-writes': ObjectWrite;
|
||||||
|
'deep-nested': DeepNested;
|
||||||
users: User;
|
users: User;
|
||||||
'payload-locked-documents': PayloadLockedDocument;
|
'payload-locked-documents': PayloadLockedDocument;
|
||||||
'payload-preferences': PayloadPreference;
|
'payload-preferences': PayloadPreference;
|
||||||
@@ -52,6 +53,7 @@ export interface Config {
|
|||||||
'rels-to-pages': RelsToPagesSelect<false> | RelsToPagesSelect<true>;
|
'rels-to-pages': RelsToPagesSelect<false> | RelsToPagesSelect<true>;
|
||||||
'rels-to-pages-and-custom-text-ids': RelsToPagesAndCustomTextIdsSelect<false> | RelsToPagesAndCustomTextIdsSelect<true>;
|
'rels-to-pages-and-custom-text-ids': RelsToPagesAndCustomTextIdsSelect<false> | RelsToPagesAndCustomTextIdsSelect<true>;
|
||||||
'object-writes': ObjectWritesSelect<false> | ObjectWritesSelect<true>;
|
'object-writes': ObjectWritesSelect<false> | ObjectWritesSelect<true>;
|
||||||
|
'deep-nested': DeepNestedSelect<false> | DeepNestedSelect<true>;
|
||||||
users: UsersSelect<false> | UsersSelect<true>;
|
users: UsersSelect<false> | UsersSelect<true>;
|
||||||
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
||||||
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
|
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
|
||||||
@@ -350,6 +352,27 @@ export interface ObjectWrite {
|
|||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "deep-nested".
|
||||||
|
*/
|
||||||
|
export interface DeepNested {
|
||||||
|
id: string;
|
||||||
|
content?: {
|
||||||
|
blocks?:
|
||||||
|
| {
|
||||||
|
meta?: {
|
||||||
|
movie?: (string | null) | Movie;
|
||||||
|
};
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: 'testBlock';
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
|
};
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "payload-locked-documents".
|
* via the `definition` "payload-locked-documents".
|
||||||
@@ -425,6 +448,10 @@ export interface PayloadLockedDocument {
|
|||||||
relationTo: 'object-writes';
|
relationTo: 'object-writes';
|
||||||
value: string | ObjectWrite;
|
value: string | ObjectWrite;
|
||||||
} | null)
|
} | null)
|
||||||
|
| ({
|
||||||
|
relationTo: 'deep-nested';
|
||||||
|
value: string | DeepNested;
|
||||||
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'users';
|
relationTo: 'users';
|
||||||
value: string | User;
|
value: string | User;
|
||||||
@@ -667,6 +694,33 @@ export interface ObjectWritesSelect<T extends boolean = true> {
|
|||||||
updatedAt?: T;
|
updatedAt?: T;
|
||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "deep-nested_select".
|
||||||
|
*/
|
||||||
|
export interface DeepNestedSelect<T extends boolean = true> {
|
||||||
|
content?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
blocks?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
testBlock?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
meta?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
movie?: T;
|
||||||
|
};
|
||||||
|
id?: T;
|
||||||
|
blockName?: T;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
updatedAt?: T;
|
||||||
|
createdAt?: T;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "users_select".
|
* via the `definition` "users_select".
|
||||||
|
|||||||
Reference in New Issue
Block a user