I am using payload with postgres and I have two collections that are related,
Usersand
RelatedUserInfo. They are related together by the
externalIdand
userExternIdfields (which is not the primary key on either collection).
export const Users: CollectionConfig = {
slug: 'users',
fields: [
{
name: 'externalId',
type: 'text'
},
{
name: 'firstName',
type: 'text',
required: true
}
]
}
export const RelatedUserInfo: CollectionConfig = {
slug: 'related-user-info',
fields: [
{
name: 'userExternId',
type: 'text'
},
{
name: 'email',
type: 'text',
required: true
}
]
}I want the admin console for
RelatedUserInfoto show the
emailfield from the corresponding
Userbut I'm not sure how to do this. All the field types that I would think would do this
relationshipor
joinor
virtualseem to require that the two tables are related to each other by the parent's primary key, but this is not the case with my tables. I need some way to tell it to create a relationship between
Users.externalIdand
RelatedUserInfo.userExternId.
Didnt read the docs? You use relationship field.
I think maybe I'm not explaining my goal very well. I don't want the payload user to have to select a
Uservalue within the
RelatedUserInfocollection via a dropdown list or something like that. The data in both of these collections is not managed by the user. It's entered via the api and it's constantly changing. I want payload to basically join the two tables together and show the email of the corresponding
Userrecord (assuming one exists) within the
RelatedUserInforecord . In order to do that I need a way to tell payload that those two collections are related via the
Users.ExternalIdand
RelatedUserInfo.UserExternIdfields. That's what I'm not sure how to do.
using hooks combined with relationship will solve that
Are you trying to display
emailon the User info?
You could build a custom field for
externalIDthat will query based on the value.
{
name: 'userExternalId',
type: 'text',
required: false,
admin: {
allowEdit: false,
components: {
Field: "@/components/RelatedInfo" // the custom component
},
},
}Here's the custom component might looks like :
export default function RelatedInfo() {
const {value, setValue} = useField<string | undefined>() // value of userExternalId field earlier
// states
const [relatedUser, setRelatedUser] = useState<User | null>(null)
const [loadingUser, setLoadingUser] = useState(false)
useEffect(() => {
setLoadingUser(true)
getUser(value)
.then(setRelatedUser)
.catch(console.error)
.finally(() => setLoadingUser(false))
}, [value])
const getUser = async (userExternalId: string): Promise<User | null> => {
// we query user collection
const res = await fetch(
`/api/users?where[userExternalId][equals]=${encodeURIComponent(email)}`,
{ headers: { 'Content-Type': 'application/json' } }
)
const json = await res.json()
return json.docs?.[0] ?? null
}
if (value) {
return (
<>
{loadingUser && <p>Loading user…</p>}
{relatedUser && (
<h1>{relatedUser.email}</h1>
)}
</>
)
}
}I'd say this one prob fit ur case better, and custom field allows you do much much more.
Or, have a duplicate of the
emailon your
RelatedUsercollection. Then you had a hook every
emailchange to update the data on
RelatedUsercollection based on
externalIdor something .
Although this meant that there will be duplicate data and you might not want that.
You can see more on querying [here](
https://payloadcms.com/docs/queries/overview#writing-queries) and building custom field [here](
https://payloadcms.com/docs/custom-components/overview#building-custom-component)
Okay, interesting solution, thank you for putting that together.
Okay, I ended up solving by using a virtual field with a hook to query for the data I wanted from the other table. I added this to the
RelatedUserInfotable:
{
name: 'userExternId',
type: 'text'
},
{
name: 'userEmail',
type: 'text',
virtual: true,
hooks: {
afterRead: [
async ({ siblingData }) => {
const payload = await getPayload({ config });
const res = await payload.find({
collection: 'users',
select: { email: true },
where: { externalId: { equals: siblingData.userExternId }}
});
if (res.docs.length === 1) {
return res.docs[0].email;
}
}
]
}
}Star
Discord
online
Get dedicated engineering support directly from the Payload team.