1

I have a category group (CG). I have a channel of entries with a matrix field (MF). Every block in that matrix field has a category field linked to the CG.

I want to list all the MF blocks in an ordered list grouped by their category. So for example:

  • Category A
    • MF block 1
    • MF block 2
    • Category AB
      • MF block 3

I'm looping over all categories within CG (with a recursive macro) and for each category I show a MF block that has that category related. The problem is that when a block is related to Category AB, it is also related to its parent Category A, so the block is shown in Category A as well as in Category AB.

I want it to be only shown in the lowest level category, being AB in this case.

It gets even trickier because I cannot limit the amount of categories selected to just 1, because than Craft automatically selects only the top parent. So now (even if I don't need it) I have to take care of the possibility that the matrix block has multiple categories selected (and their children).

I was thinking about grouping the matrix blocks by their category with the groupBy filter plugin but I haven't found the time yet to try this out. And I'm afraid I might loose the hierarchy of the CG.

I could use a section for the categories (where you can limit it to a single item and it'll select the correct child) but than the client has a hard time to see what the parent category is in the CP. So that's a downer.

Why I need this:

The client needs to be able to upload a ton of files related to an entry or add links related to an entry. That's why I created a matrix with 3 block types: external link, internal link and file. The file block has a table field where they can upload an asset and select a locale. Because all files can have multiple translations. But the locales these files can be translated in do not match the locales that the site is available in. So that's why I can't just use a translatable asset field. That also why I can't use a front-end asset browser (because of the links and the translated files and because I have no control over the created folder structure).

I could live without them being able to add links if that would make it simpler. Alternative solutions are more than welcome!

1 Answer 1

0

This is a truly tricky one. Let's restate the problem:

For each category in a tree list of categories: We want to display content from an element if the element is related to the category but not related to any of the category's children.

Off the top of my head, I can't think of a really performant way to do this... So, hopefully there aren't too many categories in your setup, and caching is key.

First we need to generate a recursive structure of categories:

{% set categories = craft.categories.group(...) %}

{% nav category in categories %}

    {% include '_elementsForThisCategory' with {category: category} only %}

    {% ifchildren %}
        {% children %}
    {% endifchildren %}

{% endnav %}

(You could easily use a macro in place of that include. These days I prefer includes over macros for anything other than the very simplest use cases.)

Next (in the included template/macro), we need to create a list of elements that are related to the present category but not related to any of its children:

{# Query entries that are related to this category's children. #}

{% set relatedToChildCats = craft.entries.relatedTo({
    targetElement: category.getChildren()
    field: 'yourMatrixField.yourCategoriesField'
}) %}

{# Query entries that are related to this category,
   and exclude the entries that are related to this category's children. #}

{% set theBuckStopsHere = craft.entries.relatedTo({
    targetElement: category
    field: 'yourMatrixField.yourCategoriesField'
}).id('not', relatedToChildCats.ids) %}

{# Display the content however you want. #}

{% for entry in theBuckStopsHere %}
    ...
{% endfor %}

→ You probably noticed that my example uses Entry elements, and your stated use case deals with MatrixBlock elements. I wanted to give the example using purely out-of-the-box-friendly code, and unfortunately Craft 2 doesn't provide a template method for querying Matrix blocks... But luckily, our good friend Low wrote a nice plugin (Low Blocks), which adds a handy craft.lowBlocks template variable.

1
  • It doesn't look like a performant way indeed but it does look like the only way to go. So I'll accept this as an answer. In the mean time, I've talked to the client and we agreed to limit the amount of categories to a level 1 list which makes it all a lot easier. But I'll probably need to use this in other parts of the project too. So thanks! May 29, 2017 at 6:42

Your Answer

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

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