I have a product with a variant matrix field(productVariants). Inside each variant is the option to supply a sale price (salePrice).

On the front end, I'm trying to show a product grid of only products that are on sale. Sounds simple, but I can't for the life of me figure out the syntax.

Here's where I'm at so far using matrixBlock queries. A few things I am not clear on:

  1. owner - what is the owner? Docs only say myEntry and I'm not on a page or in a loop to get the entry that the matrix field is a part of
  2. fieldId - is this the matrix field ID or the ID of the text field with the value I'm searching for?
  3. How do I target salePrice in the search syntax since it's one of the fields inside the Matrix field?

{% set myMatrixBlockQuery = craft.matrixBlocks.owner(myEntry).fieldId(33).type('text').search('salePrice:*') %}

{% set saleEntries = myMatrixBlockQuery.all() %}

{% for product in saleEntries %}
{{ product.title }}, etc...
{% endfor %}

I've spent a couple of hours in StackExchange and in the Craft docs and still can't wrap my head around the syntax for this situation so I appreciate your help!

  • It's a bit confusing if you just added the craft commerce products to a matrix in an entry and want to query over products which are linked in the matrix field e.g. "sale" and display them. Or if you just want to loop over all craft commerce product types that are on sale (what wouldn't require an extra matrix)
    – HendrikEng
    Commented Jun 24, 2019 at 21:27
  • I'm not using Craft Commerce - it wasn't in the client's budget so we're doing a combo of Craft, Matrix fields, and a 3rd party processor (SendOwl). Commented Jun 25, 2019 at 14:56

3 Answers 3


I was finally able to piece this together using Brad's explanation of MatrixBlocks parameters and this thread Can I get an entry by Matrix Block ID?

Seems merge([block.owner.id]) was the piece I was missing to fold the MatrixBlock query back into an entries loop.

{# Find Matrix Blocks with field value #}
{% set blocks = craft.matrixBlocks.type('variant').fieldId(33).salePrice(':notempty:').all() %}

{# Get entry IDs #}
{% set saleEntryIds = [] %}
{% for block in blocks %}
    {% set saleEntryIds = saleEntryIds|merge([block.owner.id]) %}
{% endfor %}

{# Get entries with limit #}
{% set saleProducts = craft.entries.section('shop').id(saleEntryIds).limit(4) %}

{% for product in saleProducts.all() %}
    {{ product.title }}
    {% for block in product.productVariants.all() %}
        {{ block.salePrice }}
    {% endfor %}
{% endfor %}

Seems a little overly complicated - maybe there's an easier way to do it?


If you have a product entry for each product in your section shop, you could loop and output only the ones that actually have a value in their matrix field salePrice like that:

  1. shop = the section that holds your product entries
  2. productVariants = the matrix field handle
  3. variants = the matrix block type that holds the field price value
  4. salePrice = the text field that holds the salePrice value

     {% set products = craft.entries.section('shop').limit(20) %}
     {% for product in products %}
         {% for block in product.productVariants %}
             {% if block.type == 'variants' and block.salePrice|length %}
                 {{ product.url }} - {{ product.title }} - {{ block.salePrice }} <br>
             {% endif %}
         {% endfor %}
     {% endfor %}
  • 1
    I did that initially, but needed a way to limit the total items on sale, and didn't love that I was literally loading all the products and then showing a few of them. Seemed like there should be a way to filter via a query beforehand. Commented Jun 25, 2019 at 14:59
  • You could filter the que by the fields value. Will update the answer according to that.
    – HendrikEng
    Commented Jun 25, 2019 at 15:00
  • Yeah that's what I was trying to do, but couldn't find a way to get to the text field inside the matrix field to filter by. Commented Jun 25, 2019 at 15:33
  • updated the answer
    – HendrikEng
    Commented Jun 25, 2019 at 16:44
  • This is basically the same thing I tried initially. It sort of works, but has a few limitations, thus the reason I was looking for a different way to do it. These are the issues: 1. I can't set a limit on how many of my sale products to show 2. Because I'm looping through which matrix blocks have content in salePrice I can get duplicates of a product if more than one variant are on sale. Commented Jun 25, 2019 at 17:01

Not entirely sure how you have things setup, but this is one way.

type('prices') will filter down to only block types named prices.

fieldId(6) is the ID number of the whole Matrix field.

salePrice(':notempty:') will match all fields named salePrices in Matrix blocks that have a value set.

{% set blocks = craft.matrixBlocks.type('prices').fieldId(6).salePrice(':notempty:').all() %}

{% for block in blocks %}
    <li>{{ block.salePrice }}</li>
{% endfor %}
  • Thanks for the explanation - the types and fieldId make more sense now. For reference: shop is the main section where the product entries live. productVariants is a Matrix field where they put in size, price, style, etc... variants is the block type salePrice is where they enter a price if applicable.Unfortunately I'm still not quite there. This query only seems to give me back what's inside the matrix block, I am trying to show the main entry only if the salePrice matrix field has been filled out. How do I use this query in a entires loop to get only the entries I need? Commented Jun 25, 2019 at 23:31
  • you would need to loop over the entries in the shop section as well, to output the entries(product) and their urls or title and the price from the matrix...I think you might be overthinking the query, you could simply add a limit when you are fetching the variants from the section and it won't list duplicates since each variant has only one salesPrice.I am not sure if full matrix block query will be more performant than the usual way i posted above....but i might be wrong and @brad-bell will know :) edit: i added the correct handles in the example
    – HendrikEng
    Commented Jun 26, 2019 at 6:50
  • Right, I just can't figure out how to use the matrixBlocks query in the loop. I keep thinking I'm over thinking, but if I set the limit on the original loop then it's only looking through 20 of 100 entries and it can't find any on sale. Changing this line and adding a limit does remove duplicates, though {% for block in product.productVariants.limit(1) %} Commented Jun 26, 2019 at 14:29
  • Hmm, I am wondering why you get duplicates, if each product variant has one matrix field with the salesPrice the query shouldn't result in any duplicates. The query basically just loops over all products and their variants with the salesPrice. Could you post screenshots of your field setup?
    – HendrikEng
    Commented Jun 27, 2019 at 6:11
  • Each product has a variant matrix field - that may be the confusion. I ended up finding a way to make the Matrix blocks work - new answer above. Thanks for all your help! Commented Jun 27, 2019 at 14:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.