fix: virtual relationship fields with select (#12266)
Continuation of https://github.com/payloadcms/payload/pull/12265. Currently, using `select` on new relationship virtual fields: ``` const doc = await payload.findByID({ collection: 'virtual-relations', depth: 0, id, select: { postTitle: true }, }) ``` doesn't work, because in order to calculate `post.title`, the `post` field must be selected as well. This PR adds logic that sanitizes the incoming `select` to include those relationships into `select` (that are related to selected virtual fields) --------- Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
This commit is contained in:
@@ -1997,6 +1997,23 @@ describe('database', () => {
|
||||
expect(draft.docs[0]?.postTitle).toBe('my-title')
|
||||
})
|
||||
|
||||
it('should not break when using select', async () => {
|
||||
const post = await payload.create({ collection: 'posts', data: { title: 'my-title-10' } })
|
||||
const { id } = await payload.create({
|
||||
collection: 'virtual-relations',
|
||||
depth: 0,
|
||||
data: { post: post.id },
|
||||
})
|
||||
|
||||
const doc = await payload.findByID({
|
||||
collection: 'virtual-relations',
|
||||
depth: 0,
|
||||
id,
|
||||
select: { postTitle: true },
|
||||
})
|
||||
expect(doc.postTitle).toBe('my-title-10')
|
||||
})
|
||||
|
||||
it('should allow virtual field as reference to ID', async () => {
|
||||
const post = await payload.create({ collection: 'posts', data: { title: 'my-title' } })
|
||||
const { id } = await payload.create({
|
||||
@@ -2129,6 +2146,26 @@ describe('database', () => {
|
||||
expect(doc.postCategoryTitle).toBe('1-category')
|
||||
})
|
||||
|
||||
it('should not break when using select 2x deep', async () => {
|
||||
const category = await payload.create({
|
||||
collection: 'categories',
|
||||
data: { title: '3-category' },
|
||||
})
|
||||
const post = await payload.create({
|
||||
collection: 'posts',
|
||||
data: { title: '3-post', category: category.id },
|
||||
})
|
||||
const doc = await payload.create({ collection: 'virtual-relations', data: { post: post.id } })
|
||||
|
||||
const docWithSelect = await payload.findByID({
|
||||
collection: 'virtual-relations',
|
||||
depth: 0,
|
||||
id: doc.id,
|
||||
select: { postCategoryTitle: true },
|
||||
})
|
||||
expect(docWithSelect.postCategoryTitle).toBe('3-category')
|
||||
})
|
||||
|
||||
it('should allow to query by virtual field 2x deep', async () => {
|
||||
const category = await payload.create({
|
||||
collection: 'categories',
|
||||
|
||||
@@ -98,6 +98,19 @@ export const Pages: CollectionConfig = {
|
||||
type: 'relationship',
|
||||
relationTo: 'users',
|
||||
},
|
||||
{
|
||||
name: 'virtualRelationship',
|
||||
type: 'text',
|
||||
virtual: 'author.name',
|
||||
},
|
||||
{
|
||||
name: 'virtual',
|
||||
type: 'text',
|
||||
virtual: true,
|
||||
hooks: {
|
||||
afterRead: [() => 'virtual value'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'hasManyNumber',
|
||||
type: 'number',
|
||||
|
||||
@@ -10,6 +10,10 @@ export const Users: CollectionConfig = {
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
},
|
||||
// Email added by default
|
||||
// Add more fields as needed
|
||||
],
|
||||
|
||||
@@ -255,6 +255,35 @@ describe('@payloadcms/plugin-import-export', () => {
|
||||
expect(str.indexOf('createdAt')).toBeLessThan(str.indexOf('updatedAt'))
|
||||
})
|
||||
|
||||
it('should create a CSV file with virtual fields', async () => {
|
||||
const fields = ['id', 'virtual', 'virtualRelationship']
|
||||
const doc = await payload.create({
|
||||
collection: 'exports',
|
||||
user,
|
||||
data: {
|
||||
collectionSlug: 'pages',
|
||||
fields,
|
||||
format: 'csv',
|
||||
where: {
|
||||
title: { contains: 'Virtual ' },
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const exportDoc = await payload.findByID({
|
||||
collection: 'exports',
|
||||
id: doc.id,
|
||||
})
|
||||
|
||||
expect(exportDoc.filename).toBeDefined()
|
||||
const expectedPath = path.join(dirname, './uploads', exportDoc.filename as string)
|
||||
const data = await readCSV(expectedPath)
|
||||
|
||||
// Assert that the csv file contains the expected virtual fields
|
||||
expect(data[0].virtual).toStrictEqual('virtual value')
|
||||
expect(data[0].virtualRelationship).toStrictEqual('name value')
|
||||
})
|
||||
|
||||
it('should create a file for collection csv from array.subfield', async () => {
|
||||
let doc = await payload.create({
|
||||
collection: 'exports',
|
||||
|
||||
@@ -131,6 +131,7 @@ export interface UserAuthOperations {
|
||||
*/
|
||||
export interface User {
|
||||
id: string;
|
||||
name?: string | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
email: string;
|
||||
@@ -199,6 +200,8 @@ export interface Page {
|
||||
)[]
|
||||
| null;
|
||||
author?: (string | null) | User;
|
||||
virtualRelationship?: string | null;
|
||||
virtual?: string | null;
|
||||
hasManyNumber?: number[] | null;
|
||||
relationship?: (string | null) | User;
|
||||
excerpt?: string | null;
|
||||
@@ -444,6 +447,7 @@ export interface PayloadMigration {
|
||||
* via the `definition` "users_select".
|
||||
*/
|
||||
export interface UsersSelect<T extends boolean = true> {
|
||||
name?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
email?: T;
|
||||
@@ -500,6 +504,8 @@ export interface PagesSelect<T extends boolean = true> {
|
||||
};
|
||||
};
|
||||
author?: T;
|
||||
virtualRelationship?: T;
|
||||
virtual?: T;
|
||||
hasManyNumber?: T;
|
||||
relationship?: T;
|
||||
excerpt?: T;
|
||||
|
||||
@@ -6,11 +6,12 @@ import { richTextData } from './richTextData.js'
|
||||
export const seed = async (payload: Payload): Promise<boolean> => {
|
||||
payload.logger.info('Seeding data...')
|
||||
try {
|
||||
await payload.create({
|
||||
const user = await payload.create({
|
||||
collection: 'users',
|
||||
data: {
|
||||
email: devUser.email,
|
||||
password: devUser.password,
|
||||
name: 'name value',
|
||||
},
|
||||
})
|
||||
// create pages
|
||||
@@ -80,6 +81,16 @@ export const seed = async (payload: Payload): Promise<boolean> => {
|
||||
})
|
||||
}
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
await payload.create({
|
||||
collection: 'pages',
|
||||
data: {
|
||||
author: user.id,
|
||||
title: `Virtual ${i}`,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
await payload.create({
|
||||
collection: 'pages',
|
||||
|
||||
Reference in New Issue
Block a user