I have a
users
collection and an
editorialStaff
collection. The
editorialStaff
collection is a profile that I use for the writers/editors, where public details they wish to attach to their Posts are stored (such as the email they wish to advertise, bio, website/socials, etc.). The
editorialStaff
collection has a relationship field called
mapToUserAccount
which relates to the
users
collection. This allows an editorial profile to be ‘connected’ to a user account. (Not all users of the Payload admin panel are writers/editors).
The
mapToUserAccount
field gets optionally filled with one of the users from the
users
collection. When the
mapToUserAccount
field is filled and saved, it updates a field in the
users
collection called
editorialProfile
with the ID of the editorialStaff profile. I've attached simplified screenshots of my
editorialStaff
config to show it's fields (and to save on characters).
I am trying to allow certain people access to things who have the ‘editor’ role in the
roles
field of their corresponding editorial profile. I am trying to get the
editorialProfile
field from their
user
(haven’t had an issue with), and then query the
editorialStaff
collection for an
editorialStaff
with the ID of the
editorialProfile
field, and return the roles in this
editorialStaff
collection item.
I am wondering what would be the recommended way to do this? I don’t want have everything in the
users
collection and throw out the
editorialStaff
collection even if it would make querying a little easier, as information in the
editorialStaff
collection will grow and should be separate from the
users
collection. When I look at the API for a user, I can see that by setting depth to 1, the information from the related
editorialStaff
collection is visible. I haven’t been able to isolate the roles though of the corresponding
editorialStaff
collection using the
findByID
Local API, and am not sure how to isolate these roles.
Reading a field value of a collection that is related to my user collection
I've managed to get something working, although TypeScript is giving out to me for it and I understand why but am not sure how to fix it.
This piece of code is
checkEditorialRole.ts
:
import type { User, EditorialStaff } from '../../payload-types'
export const checkEditorialRole = (allRoles: EditorialStaff['roles'] = [], user?: User): boolean => {
if (user) {
if (
allRoles.some((role) => {
return user?.editorialProfile?.roles?.some((individualRole) => {
return individualRole === role
})
})
)
return true
}
return false
}
This piece of code is
isEditor.ts
:
import { AccessArgs } from 'payload/config'
import { checkEditorialRole } from '../collections/editorialStaff/checkEditorialRole'
import type { User } from '../payload-types'
type isRole = (args: AccessArgs<unknown, User>) => boolean
export const isEditor: isRole = ({ req: { user } }) => {
return checkEditorialRole(['editor'], user)
}
I can import isEditor into the collections I want to manage access for for editors. TypeScript has underlined
roles?
in the line of code
return user?.editorialProfile?.roles?.some((individualRole)
from the first block of code, stating that "Property 'roles' does not exist on type 'string | EditorialStaff'". I can understand why it says this, and having generated types to make sure that payload-types.ts is up to date, I don't quite know why it works while TypeScript is giving out.
I've managed to piece together a solution that works without any TypeScript issues. It seems I was overthinking things regarding the Local API when I could use it to fetch data on an item in a collection, then select the data I want from it separately.
This is the code for
isEditor.ts
:
import { AccessArgs } from 'payload/config'
import { checkEditorialRole } from '../collections/editorialStaff/checkEditorialRole'
import type { User } from '../payload-types'
type isEditorialRole = (args: AccessArgs<unknown, User>) => Promise<boolean>
export const isEditor: isEditorialRole = ({ req: { user } }) => {
return checkEditorialRole(['editor'], user)
}
This is the code for
checkEditorialRole.ts
:
import payload from 'payload'
import type { User, EditorialStaff } from '../../payload-types'
export const checkEditorialRole = async (allRoles: EditorialStaff['roles'] = [], user?: User): Promise<boolean> => {
if (user) {
// Get the editorialStaff doc that relates to the current user
const findEditorialProfile = await payload.find({
collection: 'editorialStaff',
where: {
'mapToUserAccount': {
equals: user.id
}
}
})
// Access the doc and isolate the roles
const editorialRoles = findEditorialProfile.docs[0].roles;
if (
// If, out of all the roles submitted into the function, one of them matches with
// one of the roles in the user's editorialStaff profile, return true
allRoles.some((role) => {
return editorialRoles.some((individualRole) => {
return individualRole === role
})
})
) {
return true;
} else {
// otherwise, return false
return false;
}
}
// If no user is logged in return false
return false
}
Regarding the TypeScript underlining of
roles?
in the line of code
return user?.editorialProfile?.roles?.some((individualRole)
that I was having issues with before, I've managed to fix it with the code below (although I prefer to use the Local API from above as it's easier to understand):
checkEditorialRole.ts
export const checkEditorialRole = async (allRoles: EditorialStaff['roles'] = [], user?: User): Promise<boolean> => {
if (user) {
// Get the editorialStaff doc ID that relates to the current user
const editorialProfileID =
// Make sure user.editorialProfile exists and is not a string (aka not just an ID),
// so the id of the EditorialStaff (type) object can be accessed
user.editorialProfile && typeof user.editorialProfile !== 'string'
? user.editorialProfile.id
: null;
if (editorialProfileID !== null) {
// Get the editorialStaff doc that relates to the current user via ID
const editorialProfileContent = await payload.findByID({
collection: 'editorialStaff',
id: editorialProfileID,
depth: 1,
});
// Access the doc and isolate the roles of the
// editorialStaff doc that relates to the current user
const editorialRoles = editorialProfileContent.roles;
if (
// if, out of all the roles submitted into the function,
// one of them matches with one of the roles in the
// user's editorialStaff profile, return true
allRoles.some((role) => {
return editorialRoles.some((individualRole) => {
return individualRole === role
})
})
) {
return true
} else {
// otherwise, return false
return false
}
} else {
// Handle the case where editorialProfile is null
return false
}
}
// If no user is logged in return false
return false
}
Star
Discord
online
Get help straight from the Payload team with an Enterprise License.