1

At the bottom of an entry I want to display links for eight other posts. To make this useful, what I'd like to do is:

  • start with entries that are related to the post currently being viewed by category (generally there are 4-5 of these)

  • then fill in the remain slots with the other posts (not related by category), newest first

Is there a simple-ish way to do this? I started down the path of separate queries, counting, and merging results but something about that feels more complicated that it should be. I'm wondering if this can be achieved via ordering or grouping?

Thanks in advance.

0

2 Answers 2

2

This is probably pretty much the same code you already have, but I can't think of a more elegant solution:

{% set relatedEntriesIds = craft.entries.section('news').relatedTo(entry).limit(8).ids() %}

{% if relatedEntriesIds|length < 8 %}
    {% set moreRelatedEntriesIds = craft.entries.section('news').limit(8 - relatedEntries|length).ids() %}
    {% set relatedEntriesIds = relatedEntriesIds|merge([moreRelatedEntriesIds]) %}
{% endif %}

{% set relatedEntries = craft.entries.id(relatedEntriesIds).fixedOrder(true) %}
4
  • Yeah, I think this is about as clean as it gets, and a good solution. It's also a nice example of something that is probably better to handle with PHP in a custom business logic plugin. Twig gets convoluted fairly quickly, even for something relatively simple as this. Jun 9, 2015 at 14:32
  • Do you think it's possible to reduce the amount of DB queries with PHP / make performance better, @mmikkel? You'd still have these criteria models, or is something "complex" like this possible with custom DbCommands and a single query?
    – carlcs
    Jun 9, 2015 at 14:42
  • Probably possible to do with one query, but also probably not worth the effort :) Possible performance gains aside, doing it in a plugin would make for a much cleaner template and more readable, isolated logic – both big wins in my book – even if the PHP solution would more or less "mirror" the Twig above. Preference is also relevant here, of course. Personally I find complex Twig pretty hard to read (especially when/if it bleeds into the markup). Jun 9, 2015 at 15:08
  • Looks like we're all ending up in the same place… I'll keep running with this approach. Thanks.
    – philzelnar
    Jun 9, 2015 at 15:49
0

it's now 2024 and I'm wondering what the best way to do this is.

Example Setup: News Article (with an entries field for 'Categories')

Desired result: Show 3 related (by 'category') entries, if not get any other news entry to fill up to 3.

Edit: I think I might have cracked it:

{% set relatedEntriesIds = craft.entries()
  .section('news')
  .relatedTo({
    targetElement: entry.newsCategory,
    field: 'newsCategory'
  })
  .id('not ' ~ entry.id)
  .limit(3)
  .ids()
%}

{% if relatedEntriesIds|length < 3 %}
    {% set moreRelatedEntriesIds = craft.entries.section('news').id('not ' ~ entry.id).limit(3).ids() %}
    {% set relatedEntriesIds = relatedEntriesIds|merge(moreRelatedEntriesIds)|unique %}
{% endif %}

{% set relatedEntries = craft.entries.id(relatedEntriesIds).limit(3).all() %}

Previous very verbose code solution:

{# First get any articles with the same category #}
{% set articleRelatedArticlesQuery = craft.entries()
  .section('news')
  .limit(3)
  .id('not ' ~ entry.id)
  .relatedTo({
    targetElement: entry.newsCategory.all(),
    field: 'newsCategory',
  }) %}

{# Grab their IDs #}
{% set articleRelatedArticlesIDs = articleRelatedArticlesQuery.ids() ??? null %}

{# Are there less than 3? If so, we need fallback articles #}
{% if articleRelatedArticlesQuery.count() < 3 %}

  {% set fallbackArticlesQuery = craft.entries()
    .section('news')
    .id('not ' ~ entry.id)
    .limit(3)
  %}

  {# If there are _any_ related matching entries, make sure they are removed from the fallback query #}
  {% if articleRelatedArticlesIDs %}

    {# Format the string to exlude the IDs #}
    {% set relatedArticleExclusionString = articleRelatedArticlesIDs|join(', not ') ??? null %}

    {# Update the query #}
    {% do fallbackArticlesQuery.id("and, not #{entry.id}, not #{relatedArticleExclusionString}") %}

    {# Grab the Fallback IDs #}
    {% set fallbackArticlesIDs = fallbackArticlesQuery.ids() ??? null %}

    {# Now merge the two sets of IDs #}
    {% set articleRelatedArticlesIDs = articleRelatedArticlesIDs|merge(fallbackArticlesIDs) ??? null %}

  {% else %}

    {% set articleRelatedArticlesIDs = fallbackArticlesQuery.ids() ??? null %}

  {% endif %}

{% endif %}

{# Now grab the related articles #}
{% set articleRelatedArticles = craft.entries.section('news').limit(3).id(articleRelatedArticlesIDs).all() ??? null %}

Can anyone help me with some better code here?

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.