How can I retrieve the current collection name within the filter options of the relationship field?

default discord avatar
mng9391
3 months ago
31

Is there any way I can fetch the current collection name within the filter options?

  • default discord avatar
    jarrod69420
    3 months ago

    you should have access to

    relationTo

    I believe



    which would be the slug of the collection

  • default discord avatar
    mng9391
    3 months ago

    @jarrod69420 According to the documentation, the "relationTo" parameter is used to filter based on the defined field relation, indicating that it is unrelated to the slug of the collection.

  • default discord avatar
    jarrod69420
    3 months ago

    What are ya looking to do exactly?

  • default discord avatar
    mng9391
    3 months ago

    something like that


    filter: ({ data, id }) => { if (!id) { return { id: { exists: false } }; } const currentCollection = ... return { [

    ${currentCollection}.reference

    ]: { equals: id } }; },
  • default discord avatar
    jarrod69420
    3 months ago

    You want to know the collection that the relationship field was defined in, like the parent config collection?

  • default discord avatar
    mng9391
    3 months ago

    No, for example, if we have a URL like "

    http://localhost:3000/cms/admin/collections/blogs/id

    ", the currentCollection in this case should be "blogs"

  • default discord avatar
    ssyberg
    3 months ago

    This is related to this feature request:

    https://github.com/payloadcms/payload/discussions/2829
  • discord user avatar
    jmikrut
    Payload Team
    3 months ago

    ohhh I see the issue here



    is it that you want to

    re-use

    a single

    filterOptions

    function across many different fields, but you need to know which collection the parent field is contained on?



    because if i understand correctly, you let's say you have a blogs collection, and then there's some relationship field within the blogs collection. And you want to build a

    filterOptions

    function for that field, but that same function is also used on other collections



    so you need to know if the

    filterOptions

    field is used on

    blogs

    or a different collection



    if this is the case, the pattern that we typically suggest is to create a higher-order function, where you can pass in the collection name, along with any other data that you need, and then return a

    filterOptions

    function



    so it would be like this:



    type GetFilterOptionsArgs = {
      collection: string
    }
    
    const getFilterOptions = (args: GetFilterOptionsArgs): FilterOptions => {
      const { collection } = args;
    
      // Return a typical filterOptions func here, but you now have access
      // to the parent collection that it's used on
      return ({ relationTo, siblingData }) => {
        if (relationTo === "products") {
          return {
            stock: { greater_than: siblingData.quantity },
          };
        }
      
        if (relationTo === "services") {
          return {
            isAvailable: { equals: true },
          };
        }
      },
    }
    
    
    const myRelationshipField: Field = {
      type: 'relationship',
      name: 'myRelationshipField',
      relationTo: ['products', 'services'],
      // Call your higher-order function, passing in the collection slug
      filterOptions: getFilterOptions({ collection: 'my-parent-collection' }),
    }
  • default discord avatar
    ssyberg
    3 months ago
    if this is the case, the pattern that we typically suggest is to create a higher-order function, where you can pass in the collection name,

    Yea we do this all over the place and it smells a bit funny to me - we have on order of 50 reusable "components" now and we're passing this info along in each of them. It's honestly a bit error prone and in some scenarios we need to pass these down multiple levels of nested fields/blocks and it's hard to track. IMO "data" knowing what data it actually represents it a pretty common pattern, Graphql does this natively with

    __typename


    It also feel a bit not DRY / redundant to have this setup all over the place:



    const Posts = {
        slug: 'posts',
        fields: [
            Hero({collection: 'posts'})
  • default discord avatar
    mng9391
    3 months ago

    Additionally, in cases where a block contains a relationship field and is utilized across different collections, I believe we might encounter the same issue

  • discord user avatar
    jmikrut
    Payload Team
    3 months ago

    @ssyberg i like the concept of exposing a collection in a variety of places, and i think we can build this at some point. there will be a lot to think through there though



    there are a LOT of places that we will need to utilize this new feature

  • default discord avatar
    ssyberg
    3 months ago

    Word, I did a little proof of concept and a single line change made this available in all relationships but I already can tell it'll be much more in depth

  • discord user avatar
    jmikrut
    Payload Team
    3 months ago

    filterOptions is one spot, but also - - validations, hooks, custom components, access control, defaultValue, probably more



    and i'd like to identify a common pattern



    like....

  • default discord avatar
    ssyberg
    3 months ago

    I added it in the data loader and that seemed promising, but I could see that it wasn't flowing through everywhere I wanted it

  • discord user avatar
    jmikrut
    Payload Team
    3 months ago

    because sometimes it might be a collection, but sometimes it might be a global - and then sometimes you might be on a version



    so it might need to be something like

    context: { collectionSlug, globalSlug, isVersion, payloadAPI }

    and likely more



    the pattern i suggested should still work here

  • default discord avatar
    ssyberg
    3 months ago

    this was my first attempt at touching core code so I'm sure this is a silly approach:


            result.docs.forEach((doc) => {
              const docKey = JSON.stringify([
                collection,
                doc.id,
                depth,
                currentDepth,
                locale,
                fallbackLocale,
                overrideAccess,
                showHiddenFields,
              ]);
              const docsIndex = keys.findIndex((key) => key === docKey);
    
              doc._collection = collection;
    
              if (docsIndex > -1) {
                docs[docsIndex] = doc;
              }
            });


    but yea I hear ya!



    for the moment we're adding a beforeChange hook to all collections - we've already implemented an inheritance system so this is just a few lines in our "base collection"

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.