3

Say I have a channel with a matrix field which has two block types - text and video. Is there any way to select all entries in that channel that have a video block?

Idea 1

Use the Low Blocks plugin to query all video block types, then get their owner IDs, then use those to grab the entries.

Idea 2

Add a light switch field to the channel called "hasVideo". Update this field programmatically using the onSave event and a plugin. Then I can select all entries if hasVideo is true.

Is there a better way I'm missing?

2 Answers 2

2

You can write a custom plugin and make use of Craft's buildElementsQuery, to be able to add custom query statements to your ElementCriteriaModel.

Give your plugin a Template Variable addVideoBlockCriteria and with some lines of plugin code you will be able to do something like this from your template:

{% set criteria = craft.entries.section('blog').order('title asc') %}
{% set entries = craft.businesslogic.addVideoBlockCriteria(criteria) %}

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

Make your Template Variable pass the criteria to a plugin service, run them through buildElementsQuery, and you can now modify the query with some DbCommands.

public function addVideoBlockCriteria(ElementCriteriaModel $criteria)
{
    $query = craft()->elements->buildElementsQuery($criteria);

    $query->andWhere('matrixcontent_article.field_video_videoUrl is not null');

    $query->join('matrixblocks matrixblocks', 'matrixblocks.ownerId = entries.id');
    $query->join('matrixcontent_article matrixcontent_article', 'matrixcontent_article.elementId = matrixblocks.id');

    $results = $query->queryAll();

    return EntryModel::populateModels($results);
}

Use join to bring in the "matrixblocks" table and also your Matrix field's content table "matrixcontent_mymatrixfield". And add the conditions with andWhere; the Matrix field's fields column names follow a scheme "field_myMatrixBlockType_myFieldInMatrixBlock".

1

Idea 2 [...] Add a light switch field to the channel called "hasVideo". Update this field programmatically using the onSave event and a plugin. Then I can select all entries if hasVideo is true.

If you're going to write your own plugin I would encourage you to implement carlcs' solution, which is a solid one, rather than going for what is basically a hack.

However, if you do decide to use Low Blocks instead and do the heavy lifting in your template, here's how that could work:

{# Get blocks, using the field ID, filtering by block type #}
{% set blocks = craft.lowblocks.blocks.fieldId(22).type('video') %}

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

{# Get entries #}
{% set entries = craft.entries({ id : entryIds }) %}

Not super elegant, but it should work. Personally I'm a big fan of keeping templates as logic-free and simple as possible, so I'd probably go for carlcs' solution, myself.

Note: Be sure to switch out the number 22 in my example Twig for the actual ID of your Matrix field.

2
  • I will look into carlcs's answer but I don't see option #2 as a hack. Isn't it just denormalisation? Jun 15, 2015 at 11:37
  • Sure, you can consider it that, and it's probably a totally viable solution. It's a matter of perspective, of course – the reason I'd personally consider it a hack is that you'll end up with this odd relationship between a Matrix field and another, separate Lightswitch field. The latter won't be something your editor(s) should (or can) interact with, so it should probably be hidden using CSS so it won't confuse them. It adds up to a hack for me, though being a hack doesn't necessarily make it a "bad" way to accomplish your goal. I just think carlcs' solution would be more robust and in line. Jun 15, 2015 at 11:54

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.