Reading a field value of a collection that is related to my user collection

default discord avatar
herb3763
2 weeks ago

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
}
    Open the post
    Continue the discussion in Discord
    Like what we're doing?
    Star us on GitHub!

    Star

    Connect with the Payload Community on Discord

    Discord

    online

    Can't find what you're looking for?

    Get help straight from the Payload team with an Enterprise License.