1

I am working on a website for a radio that publishes its programs as podcasts with episodes. Each of the 30 or so programs potentially has a lot of episodes.

  • Each entry in the episodes channel has a one to one relation with a program entry through an episodeProgram entry field (episodes are sources and programs are targets)
  • I want to display the 6 programs with the most recently published episodes on the homepage

What I did:

  • get all episodes (most recent first)
  • get the ids of related programs
  • query the programs channel using those ids and fixedOrder to get programs with the most recently published episodes
{% set allEpisodes = craft.entries()
  .section("episodes")
  .with(["episodeProgram"])
  .orderBy("postDate DESC")
  .limit(null)
  .collect() %}

{% set recentProgramsIds = allEpisodes|map(item => item.episodeProgram.collect().first().id)|unique %}     

{% set recentPrograms = craft.entries()
  .section("programs")
  .with(["programImage"])
  .id(recentProgramsIds)
  .fixedOrder(true)
  .limit(6)
  .collect() %}

{% for item in recentPrograms %}
  {% if loop.first %}<ul class="l-grid  l-grid--gap  l-grid--2cols@small  l-grid--3cols@large  u-margin-bottom-xl">{% endif %}
    <li>

      {% include "_components/program.twig" with {
        title: item.commonMediumTitle,
        url: item.url,
        image: item.programImage.collect().first(),
        id: item.id
      } only %}

    </li>
  {% if loop.last %}</ul>{% endif %}
{% else %}
  <p>No program found</p>
{% endfor %}

Everything seems to work fine but I have two issues/questions:

1. Syntax for Laravel methods using arrow functions in twig ?

My code works with the twig |map and |unique filters and I cannot for the life of me figure out how to make it work with the Laravel collections methods (probably my noobness)

I would like to understand why / how to correct it.

Here are the two things I tried:

{% set recentProgramsIds = allEpisodes.map(item => item.episodeProgram.collect().first().id).unique() %}

Error: Twig\Error\SyntaxError: Arguments must be separated by a comma. Unexpected token "arrow function" of value "=>" ("punctuation" expected with value ","). in ... index.twig:62

{% set recentProgramsIds = allEpisodes.map((item, key) => item.episodeProgram.collect().first().id).unique() %}     

Error: Twig\Error\SyntaxError: An opened parenthesis is not properly closed. Unexpected token "punctuation" of value "," ("punctuation" expected with value ")"). in ... index.twig:62

2. Is a single query possible ?

Second question, is there a more performant way to query the programs channel for entries with the most recently posted related entries of the episodes channel (perhaps using Yii query builders).

2
  • Can you provide the error message?
    – Ben Croker
    May 11 at 7:15
  • Added it as an edit above May 11 at 11:44

1 Answer 1

2
  1. Syntax for Laravel methods using arrow functions in twig ?

You're not doing anything wrong, this approach is simply not supported by Twig. Arrow functions in Twig can only be used with built-in filters and functions that expect them – they can't be used as callables, so they can't be passed as parameters into functions. This comes down the implementation details, but because it's not supported, the parser straight up throw the unexpected token error.

I don't think this caveat is documented anywhere, but it's mentioned in this issue:

It's not supported. Arrow functions are limited in scope.

So using the appropriate Twig filters like map and unique is the correct approach here.


  1. Is a single query possible ?

It's possible, but a bit involved. You need to use two joins to join the content table through the relations junction table to connect join the programs with the associated episodes. Then you can order by the joined the postDate column directly.

Something like this:

{% set episodes = craft.entries().section('programs')
    .innerJoin('{{%relations}} programs_relation', '[[programs_relation.targetId]] = [[elements.id]]')
    .innerJoin('{{%entries}} episodes_entries', '[[episodes_entries.id]] = [[programs_relation.sourceId]]')
    .orderBy('episodes_entries.postDate DESC')
    .limit(5)
%}

The problem with this is that it creates a lot of duplicate results. Probably because the limit is only applied to the sub-query and the inner join creates multiple rows since multiple episodes can be related to a single program. Maybe someone smarter than me can explain how to limit the query to only return unique program entries …

1
  • 1
    Right. Makes sense. Will keep it as is and probably limit the episodes query to 100 episodes or so. That should give me a large enough sample to get 6 programs for the homepage. Thanks for shiming in May 11 at 18:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.