12

I have done a "Is featured" switch on articles which works great. I can list the latest 4 featured articles but after that I would like to list the others (Of course they may be featured too). What is the best way to exclude an array of elements when getting elements? Or is there a better solution?

Update 1

{% set allArticles = craft.entries.section('articles').find() %}
{% set featuredArticles = craft.entries.section('articles').featured(1).limit(4).find() %}
{% set notFeaturedArticles = allArticles|without(featuredArticles) %}

This is the code and the notFeaturedArticles returns with the same entries as the allArticles.

Update 2

{% set featuredArticleIds = craft.entries.section('articles').featured(1).limit(4).ids() %}
{% set featuredArticlIdsString = featuredArticleIds | join(', ') %}
{% set notFeaturedArticles = craft.entries.section('articles').id('not '~featuredArticlIdsString) %}

In this case only the first entry is excluded from the not featured articles.

3
  • Why do you add answers to your questions text, Hun? This is confusing.
    – carlcs
    Aug 11, 2014 at 11:08
  • I am sorry, I thought if I would like to add code formatting to an answer, this is the right way.
    – user516
    Aug 11, 2014 at 11:32
  • Hmm.. after reading through the answers, I see why you did this. I think it would help if you write some introduction to the code blocks, if you really want to repost the answers here.
    – carlcs
    Aug 11, 2014 at 11:39

4 Answers 4

30

Craft documentation describes how to exclude entries with a given value. In this case, we want a string that looks like: 'and, not id1, not id2, not id3, not id4'

So we need a string that is all of the featured article ids separated by , not. First, get them as an array using ids():

{% set featuredArticleIds = craft.entries.section('articles').featured(1).limit(4).ids() %}

Then convert that array to a string, using twig's join filter:

{% set featuredArticleIdsString = featuredArticleIds | join(', not ') %}

Then, fetch all the articles, but not the featured ones:

{% set notFeaturedArticles = craft.entries.section('articles').id('and, not '~featuredArticleIdsString) %}

[thanks to carlcs for crucial fix]

3
  • Thanks for the tip. I have already tried this solution too, but the "notFeaturedArticles" filters only the first featured entry. That time I thought it can get only one entry when "not" is there. I have tried again, but with the same results: only the first entry is excluded.
    – user516
    Aug 11, 2014 at 7:53
  • 3
    @Hun there's a little mistake in Marion's answer. Use this code to compose your IDs string: 'and, not ' ~ featuredArticleIds|join(', not ').
    – carlcs
    Aug 11, 2014 at 11:34
  • Thanks for the help. Marion's solution with carlcs's correction works fine.
    – user516
    Aug 11, 2014 at 12:12
3

I can think of two ways to accomplish this:

The without filter:

Fetch all entries and all featured entries. Then remove the latter from the first:

{% set all          = craft.entries.section('news').find() %}
{% set featured     = craft.entries.section('news').featured(1).find() %}
{% set not_featured = all|without(featured) %}

Pro: You can set the limit for each type individually.
Cons: You need two queries (performance).

UPDATE:
I talked with Brad from P&T and he explained that my example is not working, because after fetching, all and featured become objects not arrays. However it might become possible at some point in the future.

For now you would have to do it like this:

{% set allIds = craft.entries.section('news').ids() %}
{% set featuredIds = craft.entries.section('news').light('1').ids() %}
{% set notFeaturedIds = allIds|without(featuredIds) %}

{% set entries = craft.entries.id(notFeaturedIds) %}

Sorting:

By default all entries are sorted by date. But you could just as well order by "featured" first and then by date.

{% set all = craft.entries.section('news').order('featured desc, postDate desc').find() %}

Pros: One query.
Cons: You can only set one limit.

6
  • I have already tried a similar solution (1st one) which didn't worked for me. I don't know why, but the query with the without() filter simply returns with all the entries.
    – user516
    Aug 10, 2014 at 20:31
  • You probably have a small mistake in your query, can you show it?
    – Victor
    Aug 10, 2014 at 20:38
  • Victor your without solution doesn't work (see my answer), but that second solution is really nice! @HunBubu have you considered using this?
    – carlcs
    Aug 11, 2014 at 13:50
  • @carlcs Yes noticed this too, send a support ticket to make sure it's not a bug and will update the answer when I get an answer.
    – Victor
    Aug 11, 2014 at 14:42
  • @carlcs Updated it with a working example and explanation.
    – Victor
    Aug 11, 2014 at 22:06
2

The without filter seems to be a bit picky with parameters. This is what did work in the end:

{# Get the Entry Models of the most recent feature acticles #}
{% set featuredArticles = craft.entries.section('articles').featured(1).limit(4) %}

{# Get the Entry Models of all articles without the most recent feature acticles #}
{% set notFeaturedArticles = craft.entries.section('articles').limit(null)
    |without(featuredArticles[0].title)
    |without(featuredArticles[1].title)
    |without(featuredArticles[2].title)
    |without(featuredArticles[3].title)
%}

Or you get the Entry Models over their IDs:

{# Get IDs of all the articles #}
{% set allArticlesIds = craft.entries.section('articles').ids() %}

{# Get IDs of the most recent feature acticles #}
{% set featuredArticlesIds = craft.entries.section('articles').featured(1).limit(4).ids() %}

{# Get IDs of all articles without the most recent feature acticles #}
{% set notFeaturedArticlesIds = allArticlesIds|without(featuredArticlesIds) %}

{# Get the Entry Models #}
{% set featuredArticles = craft.entries.section('articles').id(featuredArticlesIds) %}
{% set notFeaturedArticles = craft.entries.section('articles').id(notFeaturedArticlesIds) %}
2

For Craft 3, after you get the ids you don't want (that would be an array), create your query to disregard those ids in the following way:

{# omitIds = array of ids you want ommited #}
{% set dontNeedIds = 'elements.id not in ('~omitIds|join(",")~')' %}
{% set news = craft.entries.section("news").where(dontNeedIds).all() %}

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.