Filtering Reference Based on Selected Product

2 replies
Last updated: Jan 10, 2023
Hi!I got an array products and within those products there are an array of variants.

I need to filter reference based on the selected product. Since I need to be able to choose several variants per product I cant seem to find a way to get the selected product.

Anyone know a solution to this?
AI Update

I can see you're trying to filter variant references based on a selected product within an array structure. This is a common challenge when working with nested arrays in Sanity Studio.

The key issue is that when you're in the variants array, you need to access the parent product field to filter which variants should be available. The tricky part is that parent gives you the variants array itself, so you need to traverse up through the document to find the product reference.

Based on the conditional fields documentation and solutions from the Sanity community, here's how to solve this:

defineField({
  name: 'products',
  title: 'Products',
  type: 'array',
  of: [
    defineArrayMember({
      name: 'productWithSelectedVariants',
      title: 'Product with selected variants',
      type: 'object',
      fields: [
        defineField({
          name: 'product',
          title: 'Product',
          type: 'reference',
          to: [{ type: 'product' }],
          weak: true,
        }),
        defineField({
          name: 'variants',
          title: 'Variants',
          type: 'array',
          of: [
            defineArrayMember({
              name: 'variant',
              title: 'Variant',
              type: 'reference',
              weak: true,
              to: [{ type: 'productVariant' }],
              options: {
                filter: ({ parent, document }) => {
                  // Find the product object that contains this variants array
                  const productObj = document.products?.find(
                    (p) => p.variants === parent
                  )
                  
                  // Get the product reference ID
                  const productID = productObj?.product?._ref
                  
                  if (!productID) {
                    return {
                      filter: '_type == "productVariant"',
                      params: {}
                    }
                  }
                  
                  return {
                    filter: '_type == "productVariant" && references($productID)',
                    params: {
                      productID,
                    },
                  }
                },
              },
            }),
          ],
        }),
      ],
    }),
  ],
})

How this works:

  1. The filter callback receives parent (the variants array) and document (the entire document)
  2. We search through document.products to find which product object contains this specific variants array by comparing p.variants === parent
  3. Once found, we extract product._ref to get the product ID
  4. We use GROQ's references() function to filter variants that reference this specific product

Important notes:

  • The parent parameter in the filter callback refers to the variants array itself, not the parent object containing it
  • You need to search through the document to find which product "owns" the current variants array
  • Make sure your productVariant documents have a reference field pointing back to their parent product for the references() filter to work properly
  • If no product is selected yet, the filter falls back to showing all variants (you could also return an empty result set if preferred)

If your variant documents don't have a reference back to the product, you'll need to adjust the GROQ filter to match based on other fields like a productId string field or similar identifier that connects variants to their products.

Here is the product part of the schema.
defineField({

name: 'products',

title: 'Products',

type: 'array',

group: 'content',

of: [

defineArrayMember({

name: 'productWithSelectedVariants',

title: 'Product with selected variants',

type: 'object',

fields: [

defineField({

name: 'product',

title: 'Product',

type: 'reference',

to: [{ type: 'product' }],

weak: true,

}),

defineField({

name: 'variants',

title: 'Variants',

type: 'array',

of: [

defineArrayMember({

name: 'variant',

title: 'Variant',

type: 'reference',

weak: true,

to: [{ type: 'productVariant', }],

options: {

filter: ({ parent, document }) => {

console.log('parent', parent);

console.log('document', document);


return {
`filter:
_type == "productVariant"
,`
params: {

shopifyProductId: null,

},

}

},

}

}),

]

}),

],

}),

],

})
This is tricky because you need to find the product within the document using the parent (which is an array of variants). Something like this should work if you’re using references to indicate the parent product:
defineField({
      name: 'products',
      title: 'Products',
      type: 'array',
      of: [
        defineArrayMember({
          name: 'productWithSelectedVariants',
          title: 'Product with selected variants',
          type: 'object',
          fields: [
            defineField({
              name: 'product',
              title: 'Product',
              type: 'reference',
              to: [{type: 'product'}],
              weak: true,
            }),
            defineField({
              name: 'variants',
              title: 'Variants',
              type: 'array',
              of: [
                defineArrayMember({
                  name: 'variant',
                  title: 'Variant',
                  type: 'reference',
                  weak: true,
                  to: [{type: 'productVariant'}],
                  options: {
                    filter: ({parent, document}) => {
                      const productID = document.products.find(
                        (product) => product.variants == parent
                      ).product._ref

                      return {
                        filter: `_type == "productVariant" && references($productID)`,
                        params: {
                          shopifyProductId: null,
                          productID,
                        },
                      }
                    },
                  },
                }),
              ],
            }),
          ],
        }),
      ],
    }),

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?