How can I get only those entries with non-empty assets field

20

4

Since Craft 2.0 it is possible to pass :empty: and :notempty: to ElementCriteriaModel parameters when you’re looking for empty/non-empty values. If you are for example looking for all entries with a non-empty text field myTextField, you can do this:

{% set entries = craft.entries.section('mySection').myTextField(':notempty:') %}

However this syntax doesn't work for asset fields and I can't figure out how that syntax has to be.

An alternative would be to use the search function:

{% set entries = craft.entries.section('mySection').search('myAssetField:*') %}

But I generally try to avoid using the search parameter in queries whenever possible. It is probably not that much of a performance killer but by avoiding it I don't have to worry about my search index.

carlcs

Posted 2014-06-13T15:02:40.393

Reputation: 31 320

Answers

12

Craft 2.5 introduced a new feature for this.

It’s now possible to find elements where a relational field does/doesn’t have any related elements, by passing ':notempty:' or ':empty:' onto the field’s ElementCriteriaModel parameter (e.g. craft.entries.myEntriesField(':notempty:')).

So in this case you can use:

{% set entries = craft.entries.myAssetField(':notempty:') %}

carlcs

Posted 2014-06-13T15:02:40.393

Reputation: 31 320

1

Amazing tip! Such a line-saver! Here is the link to the update https://craftcms.com/changelog#2-5-2750 - the feature was added in Dec 2015

– Adam Menczykowski – 2017-05-08T14:08:28.443

8

You can do this with the relatedTo param.

First get all assets that could possibly be related. (If you only wanted to find entries that related assets from a particular source, this would be the place to add that filter.)

{% set assets = craft.assets.limit(null) %}

Next up, you would get all the entries that are related to any of those assets:

{% set entries = craft.entries.section('mySection').relatedTo({
    targetElement: assets,
    field: 'myAssetsField'
}) %}

And now you can do with those as you wish.

Brandon Kelly

Posted 2014-06-13T15:02:40.393

Reputation: 27 245

Brandon, great - I had an idea relatedTo: could be used for the problem. Having it all external this way though does seem to raise some questions that might be practical about efficiency and response speed, since it bypasses the abilities databases have to dole out parts of the returned answer as needed. For this reason as well as semantical clarity, how about a 'field(fieldname)' element to stack in the restriction chain? This would shift the query building to operate on the named field, and allow the database to give efficiency, as well as Christian's next/previous behaviour, no? – narration_sd – 2014-06-18T08:20:53.123

5

I think what you want is to check if a property of the asset is empty, rather than the asset itself, as a concept.

The following appears to work, and might be of interest if you think you have empty assets, or in this example, count filled-in ones:

{% set entries = craft.assets.filename(':notempty:') %}

Filled Count is {{ entries.count() }}

You could of course use any extra fields you've assigned to the Assets; as for checking attribution, photo date, rights, etc..

Does that match the problem you are trying to solve?

All right, adding on a deeper and also tested example, to solve the clarified problem, thanks.

If you think about the stacking of queries, the desired field to check for being filled in is nested within a target element field, and therefore can't be searched directly by an ElementCriteriaModel in present form. So, we use a for loop to access this set of images, limited by field values defined of their Asset, in this case a source where the image was obtained. The inner for loop just verifies which image assets those are, by title.

{% set params =  { section: 'lodging' } %}
{% set entries = craft.entries(params)  %}

{% for entry in entries %}

<p> {{ entry.title }} 
   count: {{ entry.images.imageSource(':notempty:') .count() }} </p>

     {% for sourcedImage in entry.images %}
          <p> - {{ sourcedImage.title }} </p>
     {% endfor %}

{% endfor %}

I've used a param block to define the initial query. This makes it a little clearer how we are using first-level fields of the original entity, and don't at present have a way to operate on any multi-valued contents.

This picture should probably be documented more clearly, and I've put on my list for cookbook pages to be made.

narration_sd

Posted 2014-06-13T15:02:40.393

Reputation: 1 512

I've put the example intended in; had pasted another at first. – narration_sd – 2014-06-14T15:20:00.177

It is a very good idea to check for the filename property, thank you for that! I just testet your simplified example and it works beautifully. Now I can get only those assets where a field "assetCopyright" is set, for example. But I can't figure out how to combine this technique with an additional section parameter. {% set entries = craft.entries.section('mySection').first.myAssetField.filename(':notempty:') %} as written in your previous answer revision, gives me plenty of php error. I believe you can't combine those parameters like that. – carlcs – 2014-06-14T21:11:42.957

quick answer after too much unsuccessful play: I've had trouble with things like this before, and not less today. I think the problem is that ElementCriteriaModal while powerful only operates at a single level; in this case, the entry. My thought after is that probably a for loop on entries testing the myAssetField.filename for notempty would solve - have to run before trying this as used up all my time ;) – narration_sd – 2014-06-14T23:28:43.180

Yes, this is the answer. The problem is that the assets you want to test are one level beyond the fields of the entries themselves. I'm having some thoughts about a stacking extension filter for ElementCriteriaModels, but at present I have an example that works fine. I'll put it in a second solution, as I don't think it will survive this comment system. Ok, I won't - this StackExchange software is very intrusive, and not a help. I'll add this example to the original answer as they want. This time. – narration_sd – 2014-06-15T03:02:58.623

Yes. Passing all queried entries that match the desired section to a loop and check if the asset field is non-empty in that loop, works. But this results in some drawbacks, e.g. getPrev/getNext doesn't work if an additional if conditional is necessary to filter the entries. – carlcs – 2014-06-15T03:10:02.750

Thanks for your help narration! And yes, don't like the comment system in SE either. Even worse than g+ was for code blocks. Will have a look at your new code tomorrow, it's past 5am here, and CIV vs. JPN is over :) – carlcs – 2014-06-15T03:13:06.240

Great, Christian. You've got a good argument for an extension of function. Maybe you will want to propose it to the Craft team? – narration_sd – 2014-06-15T03:18:30.883