Hello, I have 2 collections, one named Courses and one named Lessons. I've added a relationship inside Courses that points to Lessons. I'm trying to make the relationship contain all the data from the Lesson, not just the id. In another question on this discord I've seen that adding
admin.useAsTitleanother field that is different from the id, then the relationships would also use that field specified.
I tried to specify
admin.useAsTitlein my Lessons collection, but Courses continues to add only the Lesson id.
Is there any built-in method to populate the relationship of Lessons inside Courses? Or should I code it myself? Thanks
Hi there,
Setting
admin.useAsTitlewithin your Lessons collection will allow you to specify a field to be used as the documents title. This title will be used in the admin panel instead of the ID (see screenshots).
However, the data returned by your relationship field is not affected by
admin.useAsTitle, so regardless of whether your relationship field is displaying the document id or another field (like a title), it will still return the full document data.
Here is an example of what this would look like for your Lessons collection:
const Lessons: CollectionConfig = {
slug: 'lessons',
admin: {
useAsTitle: 'title',
},
fields: [
{
name: 'title',
type: 'text',
},
],
}
export default Lessons;And here is a basic Course collection with a relationship field to your Lessons:
const Courses: CollectionConfig = {
slug: 'courses',
fields: [
{
name: 'title',
type: 'text',
},
{
name: 'lessons',
type: 'relationship',
relationTo: 'lessons',
hasMany: true,
},
],
}
export default Courses;I hope this answered your question, if not let me know!
Many apologies, I didn't explain the problem clear enough. The problem is not in the admin UI, is in the database. I have a Block Field named LessonBlock inside the Courses Collection.
I'm writing FieldHooks so the BlockName inherits the value of another field when saved into the databse. So far I've been successful with Text Fields, but with the Relationship Field, in my Hooks in the
argsI only have access to the Lesson id, which is the only one that is being sent to the database.
My question was if there is any built-in way to send more info about the Lesson to the database. For example, in the screenshot the lesson has
id, blockName, blockTypebut I would like to save it like this:
lessons: Array
0: Object
lessonInfo: Object
id: 'ofiwjefoiwefjeowifjw'
title: 'thisisthelessontitle'
blockName: 'thisisthelessontitle'
blockType: 'lessonBlock'I'm trying to save the title to the database because this way I would have access to it inside the Hooks
argswithout having to make additional api calls.
So in short my main goal is to have access to the Lesson title inside the hooks, and I'm trying to do it by saving the Lesson title inside Courses in the relationship field that point to the Lessons Collection.
I'm not sure if this is a twisted way to accomplish this or if there is another more straight-forward way, but being that this is the Payload community, my main question remains if I can accomplish this:
lessons: Array
0: Object
lessonInfo: Object
id: 'ofiwjefoiwefjeowifjw'
title: 'thisisthelessontitle'
blockName: 'thisisthelessontitle'
blockType: 'lessonBlock'Can I save more than the ID in the database when I have a Relationship pointing to another collection?
Thanks for clarifying, this is definitely possible.
The relationship field by default will return the
full document data, so we need to work out why this isn't happening for you.
I mocked up a course collection similar to your screenshot - I personally would not put your relationship field in an array, instead I set
hasMany: trueon the field.
This is how the collection code looks:
const Courses: CollectionConfig = {
slug: 'courses',
fields: [
{
name: 'title',
type: 'text',
},
{
type: 'array',
name: 'subjects',
fields: [
{
name: 'subjectName',
type: 'text',
},
{
name: 'topics',
type: 'array',
fields: [
{
name: 'topicName',
type: 'text',
},
{
name: 'lessons',
type: 'relationship',
relationTo: 'lessons',
hasMany: true
},
]
},
]
},
],
}
export default Courses;Now when I create and save a new course, this is what the data looks like (screenshot)
You can see each lesson object contains the full lesson data.
Try to compare the above example with
yourcourse collection - if you are still having issues, send me a reply and we'll dig further!
Hi, thank you very much for your help, it is very appreciated!
I tried to edit my code, thinking that maybe the fact that I was using Blocks Fields was an issue, so I made a relation to Lessons inside the LessonBlock, inside the TopicBlock (name: 'otherLessons') and finally directly inside the Courses CollectionConfig (name: 'topLevelLessons'). In all cases, only the Id of the lesson was saved inside the database (screenshot). I'm starting to think that maybe there is something I have to configure inside Payload to change this behavior?
Here is my code:
(I'm sending it in multiple message because Discord says that the message is too long if I add it here)
// Lessons.ts
import { CollectionConfig } from 'payload/types';
const Lessons: CollectionConfig = {
slug: 'lessons',
admin: {
useAsTitle: 'title',
},
access: {
read: ({ req: { user } }) => true,
},
fields: [
{
name: 'title',
type: 'text',
label: 'Title',
required: true,
},
{
name: 'description',
label: 'Description',
type: 'textarea',
required: true,
},
{
name: 'vimeo',
type: 'text',
label: 'Vimeo link',
required: true,
},
{
name: 'author',
type: 'text',
admin: {
readOnly: true,
},
hooks: {
beforeValidate: [(args) => {
// this hook adds the data of the person that creates the lesson
args.data.author = `${args.req.user.firstName} ${args.req.user.lastName}`
}],
},
},
{
name: 'authorId',
type: 'text',
admin: {
readOnly: true,
hidden: true,
},
hooks: {
beforeValidate: [(args) => {
// add the id of the user that creates the lesson
args.data.authorId = `${args.req.user.id}`
}],
},
},
],
}
export default Lessons;// Courses.ts
import { Block, CollectionConfig } from 'payload/types';
const ExerciseBlock: Block = {
slug: 'exerciseBlock',
fields: [
{
type: 'text',
name:'exerciseName',
label: 'Esercizio',
}
]
}
const LessonBlock: Block = {
slug: 'lessonBlock',
fields: [
{
type: 'relationship',
relationTo: 'lessons',
name: 'lesson',
label: 'Lezione',
},
]
}
const TopicBlock: Block = {
slug: 'topicBlock',
fields: [
{
type: 'text',
name: 'topicName',
label: 'Argomento',
},
{
type: 'relationship',
name: 'otherLessons',
hasMany: true,
relationTo: 'lessons',
},
{
type: 'blocks',
name: 'lessons',
label: 'Lezioni',
blocks: [
LessonBlock,
ExerciseBlock,
],
}
],
}
const SubjectBlock: Block = {
slug: 'subjectBlock',
fields: [
{
type: 'text',
name: 'subjectName',
label: 'Materia',
},
{
type: 'blocks',
name: 'topics',
label: 'Argomenti',
blocks: [
TopicBlock
],
},
],
}
const Courses: CollectionConfig = {
slug: 'courses',
admin: {
useAsTitle: 'title',
},
fields: [
{
type: 'text',
name: 'title',
unique: true,
required: true,
},
{
type: 'blocks',
name: 'subjects',
label: 'Materie',
blocks: [
SubjectBlock,
],
},
{
type: 'relationship',
name: 'topLevelLessons',
relationTo: 'lessons',
hasMany: true,
},
],
}
export default Courses;Just for the record, here is how my lessons look inside the database, they have multiple fields so it's not like there is nothing to add in the relationship besides the ID
Hey there, I have a couple questions!
1. Is there a reason you are using blocks instead of arrays? I am trying to make sense of that first
2. Can you re-state the question, what are you looking to achieve here?
1. I am using blocks because when everything is collapsed they have the blockName property, which allows to find things easier (with arrays everything is labeled like Subject1, Subject2, Subject3 ecc.. and you have to open them to know what's inside)
2. The problem that I'm facing is that the relationship to Lessons adds only the lessons Id, leaving out the rest of the info of the Lesson.
The answer 2 describes the problem that I'm trying to solve, but my ultimate goal is to write a custom hook that will assign to blockName the same value as the title of the Block. I was able to do this for Blocks where the title is a Text Field, but for the LessonBlock the title is a Relationship Field, which only supplies the ID. If this Relationship field would supply all the info of the Lesson that it is referencing to, I would have the title on the Lesson available inside the Hook
args, so I would be able to assign it to the blockName of the LessonBlock.
I'm doing it this way to avoid making more API calls and potentially slow down the website (I'm not a super expert dev so I hope it makes sense).
1. We actually just released a feature a couple days ago (v1.2) that allows you to programmatically set the label of an array (not sure if that is of use to you) you would add it under
admin.components.RowLabelread more about it here
https://payloadcms.com/docs/fields/array#admin-config2. Can you show me this hook that you are talking about?
1. Neat! I will check it out later to see if it is better for the website I'm making
2. Ofc
The hook is implemented inside Courses CollectionConfig, I left it out in the code I sent before, just so the focus would stay on the main problem
// Courses.ts
// up here there is the code for the Blocks, it is the same as in the code I sent before so I will leave it out
const Courses: CollectionConfig = {
slug: 'courses',
admin: {
useAsTitle: 'title',
},
fields: [
{
type: 'text',
name: 'title',
unique: true,
required: true,
},
{
type: 'blocks',
name: 'subjects',
label: 'Materie',
blocks: [
SubjectBlock,
],
},
{
type: 'relationship',
name: 'topLevelLessons',
relationTo: 'lessons',
hasMany: true,
},
],
hooks: {
beforeValidate: [(args) => {
// this will give the blockName the same value as the topicName so the user doesnt have to input it manually
matchBlockName(args)
}],
}
}
export default Courses;// hookMatchBlockName.ts
const matchBlockName = (args) => {
// iterate through every subject in Courses
args.data.subjects.forEach(sbj => {
sbj.blockName = sbj.subjectName;
// iterate through every topic in Subject
sbj.topics.forEach((tpc) => {
tpc.blockName = tpc.topicName;
if (tpc.lessons && tpc.lessons.length > 0) {
tpc.lessons.forEach(lsn => {
console.log(lsn);
// the console log returns this
// {
// id: '637e1c160900db59687d5c77',
// lesson: '637e1a82b11c34e9350e54f7',
// blockType: 'lessonBlock'
// }
// {
// id: '637e1c190900db59687d5c78',
// lesson: '637e1aacb06ea7eea66087c2',
// blockType: 'lessonBlock'
// }
// there is no title for the lesson
});
};
if (tpc.exercises) {
console.log('tpc.exercises fired')
tpc.exercises.forEach(exr => {
exr.blockName = exr.exerciseName;
});
};
});
});
}
export { matchBlockName };This hook I wrote, successfully assings the blockName for Subjects and Topics, but it can't find the title of the lesson inside the
args, because payload is not including them when saving the Relationship Field of the Lesson (I'm trying to make payload save all the fields of the Lesson it is referncing to, not only the ID)
(For now leave out the code about Exercises, if I can solve the problem for Lessons, I will be able to solve it for Exercises too since they are very similar in how the website handles them)
May I ask if the screenshot is from the website of MongoDB? My interface there looks a bit different... I made a new project with payload and copied this exact code but I still wasn't able to populate relationships with the rest of the data... Maybe there is an issue with the way I'm connecting/setting up the database, yours looks different than mine
Star
Discord
online
Get dedicated engineering support directly from the Payload team.