1

Suppose we have a category group that allows for any number of levels to its structure. We also have a category field that allows for choosing any number of categories from that group.

In our example, we want to select three categories: one is a level 1 category in that group, another is a direct child of a different level 1 category and the third is a level 4 category from another branch in the category group (all three are in different branches for this example).

The question is: How do we query the category field to only return the categories we selected—the categories we actually clicked on when selecting categories? (Craft 3)

4
  • Do you mean you don't want to return anything related to the selected category's children?
    – dmatthams
    Commented Jul 17, 2019 at 14:15
  • I think it's pretty clear. Here's an example: If there are 5000 categories, nested 50 deep, and content author picks 3 categories from anywhere in that category tree JUST QUERY FOR THE THREE SELECTED CATEGORIES. NO PARENTS. NO CHILDREN. NOTHING ELSE. Just what was clicked. Commented Jul 17, 2019 at 15:47
  • I see, this is a tricky one.
    – dmatthams
    Commented Jul 17, 2019 at 16:14
  • There HAS to be a way to do this. The selected categories ARE the field's value, are they not? Commented Jul 18, 2019 at 15:06

4 Answers 4

4

Here's how to isolate only your chosen categories. This works off the premise that if a related category is a parent of one of the other related categories, it is not a category you chose.

{% set relatedCategories = entry.myCategoryField.with(['parent']).all() %}
{% set parentCategories = relatedCategories | map(c => c.parent) %}
{% set chosenCategories = relatedCategories | diff(parentCategories) %}
1
  • This is a really important answer, as it allows behaviour (in perhaps a slightly hacky way) that perhaps should be core.
    – ianp
    Commented Nov 8, 2022 at 18:03
1

Craft does not allow for this (as of Craft 3.1.34). When saving selected categories to a Category relationship field, it automatically assigns the parent categories and does nothing to designate which categories were actually selected/clicked/chosen.

0

Unless I'm missing something - this is how you run through the categories you selected in your Category field when you're looking at that entry's template:

{% for cat in entry.yourCategoryFieldhandle.all() %}
    {{ cat.title }}
{% endfor %}

EDIT: Ignore this, I can see what you mean. If you select a child category it automatically selects all the parents too.

This may not be suitable for your site, but if this is required functionality I'd be looking at using a Structure instead of categories. Using a Structure combined with an Entries field will let you do this no problem. P&T are going to be removing Categories and Tags in Craft 4 anyway.

3
  • Yes, the issue is that we aren't trying to query the parents in this situation and we aren't necessarily working with "leaves" either. Commented Jul 17, 2019 at 15:29
  • Yes, a Structure of Entries would be far more appropriate if you don't want the hierarchical inheritance.
    – foamcow
    Commented Jul 18, 2019 at 15:51
  • 1
    The issue is that we do want the hierarchical inheritance when assigning the taxonomy to an object but we do not want it in other places where those taxonomies are referenced and not actually being assigned to an object. This is a design flaw in Craft—plain and simple. Commented Jul 24, 2019 at 15:36
0

This is untested, but you could do a loop within each category to compare it and figure out if it's the end of a branch. I've probably missed something in my code but it might help as a start.

{% set catsYouWant = [] %}
{% for cat in entry.yourCategoryFieldhandle.all() %}

    {% set possible = false %}

    // If category is level one it is possibly the end of branch
    {% if cat.level == 1 %}
        {% set possible == true %}
    {% endif %}

    // Compare category against others to see if is a parent, if so we can discount it
    {% for catCompare in entry.yourCategoryFieldhandle.all() %}

        // if catCompare is child of cat then cat is not end of branch
        {% if catCompare.ancestorOf(cat) %}
            {% set possible = false %}
        {% endif %}

    {% endfor %}

    {% if possible == true %}
        {% set catsYouWant = catsYouWant|merge(cat) %}
    {% endif %}
{% endfor %}
2
  • What I'm looking for in particular is a means to query the field directly to give me the categories (or IDs) that were specifically selected when populating the field. In a perfect world, this might look something like: entry.catFieldHandle.chosen().ids() In theory, something like that would return the three category IDs provided in my example. It would not return their parents or anything else. Commented Jul 18, 2019 at 15:04
  • Hopefully someone else knows a cleaner way, but in theory, the code above should give you the same result albeit not as tidy.
    – dmatthams
    Commented Jul 18, 2019 at 15:20

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.