feat: join field with polymorphic relationships (#9990)
### What? The join field had a limitation imposed that prevents it from targeting polymorphic relationship fields. With this change we can support any relationship fields. ### Why? Improves the functionality of join field. ### How? Extended the database adapters and removed the config sanitization that would throw an error when polymorphic relationships were used. Fixes #
This commit is contained in:
@@ -115,6 +115,30 @@ export const Categories: CollectionConfig = {
|
||||
collection: 'posts',
|
||||
on: 'blocks.category',
|
||||
},
|
||||
{
|
||||
name: 'polymorphic',
|
||||
type: 'join',
|
||||
collection: postsSlug,
|
||||
on: 'polymorphic',
|
||||
},
|
||||
{
|
||||
name: 'polymorphics',
|
||||
type: 'join',
|
||||
collection: postsSlug,
|
||||
on: 'polymorphics',
|
||||
},
|
||||
{
|
||||
name: 'localizedPolymorphic',
|
||||
type: 'join',
|
||||
collection: postsSlug,
|
||||
on: 'localizedPolymorphic',
|
||||
},
|
||||
{
|
||||
name: 'localizedPolymorphics',
|
||||
type: 'join',
|
||||
collection: postsSlug,
|
||||
on: 'localizedPolymorphics',
|
||||
},
|
||||
{
|
||||
name: 'singulars',
|
||||
type: 'join',
|
||||
|
||||
@@ -53,6 +53,30 @@ export const Posts: CollectionConfig = {
|
||||
hasMany: true,
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'polymorphic',
|
||||
type: 'relationship',
|
||||
relationTo: ['categories', 'users'],
|
||||
},
|
||||
{
|
||||
name: 'polymorphics',
|
||||
type: 'relationship',
|
||||
relationTo: ['categories', 'users'],
|
||||
hasMany: true,
|
||||
},
|
||||
{
|
||||
name: 'localizedPolymorphic',
|
||||
type: 'relationship',
|
||||
relationTo: ['categories', 'users'],
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'localizedPolymorphics',
|
||||
type: 'relationship',
|
||||
relationTo: ['categories', 'users'],
|
||||
hasMany: true,
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'group',
|
||||
type: 'group',
|
||||
|
||||
@@ -291,6 +291,67 @@ test.describe('Join Field', () => {
|
||||
await expect(joinField.locator('tbody .row-1')).toContainText('Test Post 1 Updated')
|
||||
})
|
||||
|
||||
test('should create join collection from polymorphic relationships', async () => {
|
||||
await page.goto(categoriesURL.edit(categoryID))
|
||||
const joinField = page.locator('#field-polymorphic.field-type.join')
|
||||
await expect(joinField).toBeVisible()
|
||||
await joinField.locator('.relationship-table__add-new').click()
|
||||
const drawer = page.locator('[id^=doc-drawer_posts_1_]')
|
||||
await expect(drawer).toBeVisible()
|
||||
const titleField = drawer.locator('#field-title')
|
||||
await expect(titleField).toBeVisible()
|
||||
await titleField.fill('Test polymorphic Post')
|
||||
await expect(drawer.locator('#field-polymorphic')).toContainText('example')
|
||||
await drawer.locator('button[id="action-save"]').click()
|
||||
await expect(drawer).toBeHidden()
|
||||
await expect(joinField.locator('tbody .row-1')).toContainText('Test polymorphic Post')
|
||||
})
|
||||
test('should create join collection from polymorphic, hasMany relationships', async () => {
|
||||
await page.goto(categoriesURL.edit(categoryID))
|
||||
const joinField = page.locator('#field-polymorphics.field-type.join')
|
||||
await expect(joinField).toBeVisible()
|
||||
await joinField.locator('.relationship-table__add-new').click()
|
||||
const drawer = page.locator('[id^=doc-drawer_posts_1_]')
|
||||
await expect(drawer).toBeVisible()
|
||||
const titleField = drawer.locator('#field-title')
|
||||
await expect(titleField).toBeVisible()
|
||||
await titleField.fill('Test polymorphic Post')
|
||||
await expect(drawer.locator('#field-polymorphics')).toContainText('example')
|
||||
await drawer.locator('button[id="action-save"]').click()
|
||||
await expect(drawer).toBeHidden()
|
||||
await expect(joinField.locator('tbody .row-1')).toContainText('Test polymorphic Post')
|
||||
})
|
||||
test('should create join collection from polymorphic localized relationships', async () => {
|
||||
await page.goto(categoriesURL.edit(categoryID))
|
||||
const joinField = page.locator('#field-localizedPolymorphic.field-type.join')
|
||||
await expect(joinField).toBeVisible()
|
||||
await joinField.locator('.relationship-table__add-new').click()
|
||||
const drawer = page.locator('[id^=doc-drawer_posts_1_]')
|
||||
await expect(drawer).toBeVisible()
|
||||
const titleField = drawer.locator('#field-title')
|
||||
await expect(titleField).toBeVisible()
|
||||
await titleField.fill('Test polymorphic Post')
|
||||
await expect(drawer.locator('#field-localizedPolymorphic')).toContainText('example')
|
||||
await drawer.locator('button[id="action-save"]').click()
|
||||
await expect(drawer).toBeHidden()
|
||||
await expect(joinField.locator('tbody .row-1')).toContainText('Test polymorphic Post')
|
||||
})
|
||||
test('should create join collection from polymorphic, hasMany, localized relationships', async () => {
|
||||
await page.goto(categoriesURL.edit(categoryID))
|
||||
const joinField = page.locator('#field-localizedPolymorphics.field-type.join')
|
||||
await expect(joinField).toBeVisible()
|
||||
await joinField.locator('.relationship-table__add-new').click()
|
||||
const drawer = page.locator('[id^=doc-drawer_posts_1_]')
|
||||
await expect(drawer).toBeVisible()
|
||||
const titleField = drawer.locator('#field-title')
|
||||
await expect(titleField).toBeVisible()
|
||||
await titleField.fill('Test polymorphic Post')
|
||||
await expect(drawer.locator('#field-localizedPolymorphics')).toContainText('example')
|
||||
await drawer.locator('button[id="action-save"]').click()
|
||||
await expect(drawer).toBeHidden()
|
||||
await expect(joinField.locator('tbody .row-1')).toContainText('Test polymorphic Post')
|
||||
})
|
||||
|
||||
test('should render empty relationship table when creating new document', async () => {
|
||||
await page.goto(categoriesURL.create)
|
||||
const joinField = page.locator('#field-relatedPosts.field-type.join')
|
||||
|
||||
@@ -90,6 +90,26 @@ describe('Joins Field', () => {
|
||||
upload: uploadedImage,
|
||||
categories,
|
||||
categoriesLocalized: categories,
|
||||
polymorphic: {
|
||||
relationTo: 'categories',
|
||||
value: category.id,
|
||||
},
|
||||
polymorphics: [
|
||||
{
|
||||
relationTo: 'categories',
|
||||
value: category.id,
|
||||
},
|
||||
],
|
||||
localizedPolymorphic: {
|
||||
relationTo: 'categories',
|
||||
value: category.id,
|
||||
},
|
||||
localizedPolymorphics: [
|
||||
{
|
||||
relationTo: 'categories',
|
||||
value: category.id,
|
||||
},
|
||||
],
|
||||
group: {
|
||||
category: category.id,
|
||||
camelCaseCategory: category.id,
|
||||
@@ -216,6 +236,17 @@ describe('Joins Field', () => {
|
||||
expect(docs[0].upload.relatedPosts.docs).toHaveLength(10)
|
||||
})
|
||||
|
||||
it('should join on polymorphic relationships', async () => {
|
||||
const categoryWithPosts = await payload.findByID({
|
||||
collection: categoriesSlug,
|
||||
id: category.id,
|
||||
})
|
||||
expect(categoryWithPosts.polymorphic.docs[0]).toHaveProperty('id')
|
||||
expect(categoryWithPosts.polymorphics.docs[0]).toHaveProperty('id')
|
||||
expect(categoryWithPosts.localizedPolymorphic.docs[0]).toHaveProperty('id')
|
||||
expect(categoryWithPosts.localizedPolymorphics.docs[0]).toHaveProperty('id')
|
||||
})
|
||||
|
||||
it('should filter joins using where query', async () => {
|
||||
const categoryWithPosts = await payload.findByID({
|
||||
id: category.id,
|
||||
|
||||
@@ -38,6 +38,10 @@ export interface Config {
|
||||
'group.camelCasePosts': 'posts';
|
||||
arrayPosts: 'posts';
|
||||
blocksPosts: 'posts';
|
||||
polymorphic: 'posts';
|
||||
polymorphics: 'posts';
|
||||
localizedPolymorphic: 'posts';
|
||||
localizedPolymorphics: 'posts';
|
||||
filtered: 'posts';
|
||||
hiddenPosts: 'hidden-posts';
|
||||
singulars: 'singular';
|
||||
@@ -123,6 +127,48 @@ export interface Post {
|
||||
category?: (string | null) | Category;
|
||||
categories?: (string | Category)[] | null;
|
||||
categoriesLocalized?: (string | Category)[] | null;
|
||||
polymorphic?:
|
||||
| ({
|
||||
relationTo: 'categories';
|
||||
value: string | Category;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'users';
|
||||
value: string | User;
|
||||
} | null);
|
||||
polymorphics?:
|
||||
| (
|
||||
| {
|
||||
relationTo: 'categories';
|
||||
value: string | Category;
|
||||
}
|
||||
| {
|
||||
relationTo: 'users';
|
||||
value: string | User;
|
||||
}
|
||||
)[]
|
||||
| null;
|
||||
localizedPolymorphic?:
|
||||
| ({
|
||||
relationTo: 'categories';
|
||||
value: string | Category;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'users';
|
||||
value: string | User;
|
||||
} | null);
|
||||
localizedPolymorphics?:
|
||||
| (
|
||||
| {
|
||||
relationTo: 'categories';
|
||||
value: string | Category;
|
||||
}
|
||||
| {
|
||||
relationTo: 'users';
|
||||
value: string | User;
|
||||
}
|
||||
)[]
|
||||
| null;
|
||||
group?: {
|
||||
category?: (string | null) | Category;
|
||||
camelCaseCategory?: (string | null) | Category;
|
||||
@@ -207,6 +253,22 @@ export interface Category {
|
||||
docs?: (string | Post)[] | null;
|
||||
hasNextPage?: boolean | null;
|
||||
} | null;
|
||||
polymorphic?: {
|
||||
docs?: (string | Post)[] | null;
|
||||
hasNextPage?: boolean | null;
|
||||
} | null;
|
||||
polymorphics?: {
|
||||
docs?: (string | Post)[] | null;
|
||||
hasNextPage?: boolean | null;
|
||||
} | null;
|
||||
localizedPolymorphic?: {
|
||||
docs?: (string | Post)[] | null;
|
||||
hasNextPage?: boolean | null;
|
||||
} | null;
|
||||
localizedPolymorphics?: {
|
||||
docs?: (string | Post)[] | null;
|
||||
hasNextPage?: boolean | null;
|
||||
} | null;
|
||||
singulars?: {
|
||||
docs?: (string | Singular)[] | null;
|
||||
hasNextPage?: boolean | null;
|
||||
@@ -239,6 +301,23 @@ export interface Singular {
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "users".
|
||||
*/
|
||||
export interface User {
|
||||
id: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
email: string;
|
||||
resetPasswordToken?: string | null;
|
||||
resetPasswordExpiration?: string | null;
|
||||
salt?: string | null;
|
||||
hash?: string | null;
|
||||
loginAttempts?: number | null;
|
||||
lockUntil?: string | null;
|
||||
password?: string | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "versions".
|
||||
@@ -347,23 +426,6 @@ export interface RestrictedPost {
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "users".
|
||||
*/
|
||||
export interface User {
|
||||
id: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
email: string;
|
||||
resetPasswordToken?: string | null;
|
||||
resetPasswordExpiration?: string | null;
|
||||
salt?: string | null;
|
||||
hash?: string | null;
|
||||
loginAttempts?: number | null;
|
||||
lockUntil?: string | null;
|
||||
password?: string | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-locked-documents".
|
||||
@@ -481,6 +543,10 @@ export interface PostsSelect<T extends boolean = true> {
|
||||
category?: T;
|
||||
categories?: T;
|
||||
categoriesLocalized?: T;
|
||||
polymorphic?: T;
|
||||
polymorphics?: T;
|
||||
localizedPolymorphic?: T;
|
||||
localizedPolymorphics?: T;
|
||||
group?:
|
||||
| T
|
||||
| {
|
||||
@@ -525,6 +591,10 @@ export interface CategoriesSelect<T extends boolean = true> {
|
||||
};
|
||||
arrayPosts?: T;
|
||||
blocksPosts?: T;
|
||||
polymorphic?: T;
|
||||
polymorphics?: T;
|
||||
localizedPolymorphic?: T;
|
||||
localizedPolymorphics?: T;
|
||||
singulars?: T;
|
||||
filtered?: T;
|
||||
updatedAt?: T;
|
||||
|
||||
Reference in New Issue
Block a user