fix(graphql): population of joins that target relationship fields that have relationTo as an array (#12289)

Fixes population of joins that target relationship fields that have
`relationTo` as an array, for example:
```ts
// Posts collection
{
  name: 'polymorphic',
  type: 'relationship',
  relationTo: ['categories', 'users'],
},
// Categories collection
{
  name: 'polymorphic',
  type: 'join',
  collection: 'posts',
  on: 'polymorphic',
}
```

Thanks @jaycetde for the integration test
https://github.com/payloadcms/payload/pull/12278!

---------

Co-authored-by: Jayce Pulsipher <jpulsipher@nav.com>
This commit is contained in:
Sasha
2025-05-01 21:04:42 +03:00
committed by GitHub
parent b9868c4a3b
commit c08c7071ee
5 changed files with 114 additions and 4 deletions

View File

@@ -11,6 +11,7 @@ export type ObjectTypeConfig = {
type Args = { type Args = {
baseFields?: ObjectTypeConfig baseFields?: ObjectTypeConfig
collectionSlug?: string
config: SanitizedConfig config: SanitizedConfig
fields: Field[] fields: Field[]
forceNullable?: boolean forceNullable?: boolean
@@ -23,6 +24,7 @@ type Args = {
export function buildObjectType({ export function buildObjectType({
name, name,
baseFields = {}, baseFields = {},
collectionSlug,
config, config,
fields, fields,
forceNullable, forceNullable,
@@ -43,6 +45,7 @@ export function buildObjectType({
return { return {
...objectTypeConfig, ...objectTypeConfig,
...fieldSchema({ ...fieldSchema({
collectionSlug,
config, config,
field, field,
forceNullable, forceNullable,

View File

@@ -8,6 +8,7 @@ import type {
DateField, DateField,
EmailField, EmailField,
Field, Field,
FlattenedJoinField,
GraphQLInfo, GraphQLInfo,
GroupField, GroupField,
JoinField, JoinField,
@@ -68,6 +69,7 @@ function formattedNameResolver({
} }
type SharedArgs = { type SharedArgs = {
collectionSlug?: string
config: SanitizedConfig config: SanitizedConfig
forceNullable?: boolean forceNullable?: boolean
graphqlResult: GraphQLInfo graphqlResult: GraphQLInfo
@@ -340,7 +342,7 @@ export const fieldToSchemaMap: FieldToSchemaMap = {
}, },
} }
}, },
join: ({ field, graphqlResult, objectTypeConfig, parentName }) => { join: ({ collectionSlug, field, graphqlResult, objectTypeConfig, parentName }) => {
const joinName = combineParentName(parentName, toWords(field.name, true)) const joinName = combineParentName(parentName, toWords(field.name, true))
const joinType = { const joinType = {
@@ -385,9 +387,23 @@ export const fieldToSchemaMap: FieldToSchemaMap = {
const draft = Boolean(args.draft ?? context.req.query?.draft) const draft = Boolean(args.draft ?? context.req.query?.draft)
const fullWhere = combineQueries(where, { const targetField = (field as FlattenedJoinField).targetField
[field.on]: { equals: parent._id ?? parent.id },
}) const fullWhere = combineQueries(
where,
Array.isArray(targetField.relationTo)
? {
[field.on]: {
equals: {
relationTo: collectionSlug,
value: parent._id ?? parent.id,
},
},
}
: {
[field.on]: { equals: parent._id ?? parent.id },
},
)
if (Array.isArray(collection)) { if (Array.isArray(collection)) {
throw new Error('GraphQL with array of join.field.collection is not implemented') throw new Error('GraphQL with array of join.field.collection is not implemented')

View File

@@ -29,6 +29,7 @@ import { recursivelyBuildNestedPaths } from './recursivelyBuildNestedPaths.js'
import { withOperators } from './withOperators.js' import { withOperators } from './withOperators.js'
type Args = { type Args = {
collectionSlug?: string
nestedFieldName?: string nestedFieldName?: string
parentName: string parentName: string
} }

View File

@@ -111,6 +111,7 @@ export function initCollections({ config, graphqlResult }: InitCollectionsGraphQ
collection.graphQL.type = buildObjectType({ collection.graphQL.type = buildObjectType({
name: singularName, name: singularName,
baseFields, baseFields,
collectionSlug: collectionConfig.slug,
config, config,
fields, fields,
forceNullable: forceNullableObjectType, forceNullable: forceNullableObjectType,
@@ -339,6 +340,7 @@ export function initCollections({ config, graphqlResult }: InitCollectionsGraphQ
collection.graphQL.versionType = buildObjectType({ collection.graphQL.versionType = buildObjectType({
name: `${singularName}Version`, name: `${singularName}Version`,
collectionSlug: collectionConfig.slug,
config, config,
fields: versionCollectionFields, fields: versionCollectionFields,
forceNullable: forceNullableObjectType, forceNullable: forceNullableObjectType,

View File

@@ -940,6 +940,94 @@ describe('Joins Field', () => {
) )
}) })
it('should have simple paginate with page for joins polymorphic', async () => {
let queryWithLimit = `query {
Categories(where: {
name: { equals: "paginate example" }
}) {
docs {
polymorphic(
sort: "createdAt",
limit: 2
) {
docs {
title
}
hasNextPage
}
}
}
}`
let pageWithLimit = await restClient
.GRAPHQL_POST({ body: JSON.stringify({ query: queryWithLimit }) })
.then((res) => res.json())
const queryUnlimited = `query {
Categories(
where: {
name: { equals: "paginate example" }
}
) {
docs {
polymorphic(
sort: "createdAt",
limit: 0
) {
docs {
title
createdAt
}
hasNextPage
}
}
}
}`
const unlimited = await restClient
.GRAPHQL_POST({ body: JSON.stringify({ query: queryUnlimited }) })
.then((res) => res.json())
expect(pageWithLimit.data.Categories.docs[0].polymorphic.docs).toHaveLength(2)
expect(pageWithLimit.data.Categories.docs[0].polymorphic.docs[0].id).toStrictEqual(
unlimited.data.Categories.docs[0].polymorphic.docs[0].id,
)
expect(pageWithLimit.data.Categories.docs[0].polymorphic.docs[1].id).toStrictEqual(
unlimited.data.Categories.docs[0].polymorphic.docs[1].id,
)
expect(pageWithLimit.data.Categories.docs[0].polymorphic.hasNextPage).toStrictEqual(true)
queryWithLimit = `query {
Categories(where: {
name: { equals: "paginate example" }
}) {
docs {
polymorphic(
sort: "createdAt",
limit: 2,
page: 2,
) {
docs {
title
}
hasNextPage
}
}
}
}`
pageWithLimit = await restClient
.GRAPHQL_POST({ body: JSON.stringify({ query: queryWithLimit }) })
.then((res) => res.json())
expect(pageWithLimit.data.Categories.docs[0].polymorphic.docs[0].id).toStrictEqual(
unlimited.data.Categories.docs[0].polymorphic.docs[2].id,
)
expect(pageWithLimit.data.Categories.docs[0].polymorphic.docs[1].id).toStrictEqual(
unlimited.data.Categories.docs[0].polymorphic.docs[3].id,
)
})
it('should populate joins with hasMany when on both sides documents are in draft', async () => { it('should populate joins with hasMany when on both sides documents are in draft', async () => {
const category = await payload.create({ const category = await payload.create({
collection: 'categories-versions', collection: 'categories-versions',