Fetching entries by a Matrix block field value is actually quite complicated, and not possible to do in a single query – at least not using built in services like craft.entries
.
Your idea of pulling all entries and looping through would work – but this kind of "brute force" solution might not scale that well, performance wise.
A better idea might be to first pull all the space
Matrix blocks that have a true
value for the isAvailable
field, and then pull any entry belonging to the buildings
section that has a relation at least one of those blocks.
Unfortunately, unlike the EntriesService (i.e. craft.entries
), Craft doesn't expose the MatrixService to Twig, making the above hard to accomplish in a template.
For a clean & effective solution to this problem, I'd look to the excellent Low Blocks plugin.
With Low Blocks, you'll be able to pull all space
blocks with a true
value for isAvailable
like this:
{% set spacesMatrixField = craft.fields.getFieldByHandle('spaces') %}
{% set spaceMatrixBlocks = craft.lowblocks.blocks.fieldId(spacesMatrixField.id).type('space').isAvailable('1') %}
After that, we can loop on the matrixBlocks
variable in order to create an array containing the IDs of the entries "owning" these Matrix blocks:
{% set buildingEntryIds = [] %}
{% for block in spaceMatrixBlocks %}
{% set buildingEntryIds = buildingEntryIds|merge([block.owner.id]) %}
{% endfor %}
And finally, we pull the entries using the buildingEntryIds
array variable as the parameter to the id
argument:
{% set entries = craft.entries.section('buildings').id(buildingEntryIds).limit(null) %}
Here's everything together, for cohesion:
{# Get Matrix blocks #}
{% set spacesMatrixField = craft.fields.getFieldByHandle('spaces') %}
{% set spaceMatrixBlocks = craft.lowblocks.blocks.fieldId(spacesMatrixField.id).type('space').isAvailable('1') %}
{# Get entry IDs #}
{% set buildingEntryIds = [] %}
{% for block in spaceMatrixBlocks %}
{% set buildingEntryIds = buildingEntryIds|merge([block.owner.id]) %}
{% endfor %}
{# Get entries #}
{% set entries = craft.entries.section('buildings').id(buildingEntryIds).limit(null) %}
That should hopefully do the trick. If you're interested though, here's some more info on why your first attempt wouldn't work:
First, I'd like to point out that its important to realize that each chained "segment" (such as section
or isAvailable
) in a call to craft.entries
in a template actually ends up as an attribute of an ElementCriteriaModel (an object Craft constructs to query the database). This means that
craft.entries.section('buildings').spaces.space.isAvailable('1').find
is actually the same as
craft.entries({
section : 'buildings',
spaces : null,
space : null,
isAvailable : '1'
})
It should be fairly obvious why this query won't do what you want; Craft has no idea that spaces.space.isAvailable
is actually the "path" to a certain Matrix block type's field, and will consider each of those segments (spaces
, space
and isAvailable
) as a separate attribute/argument.
Aside from the syntax issue, the real reason why you'll never be able to use something like spaces.space.isAvailable
in a craft.entries
query is that Matrix blocks aren't just regular, nested fields, but elements unto themselves (just like entries, categories or assets). Also, for any single entry there can be an indefinite number of instances of each block type. If you have multiple space
blocks for a single entry, some with a false
isAvailable
value and some with true
, how would Craft know if the entry should be returned or not?