<p>You can use arrow functions pretty much everywhere, overcoming Twig's current limitations, via the Closure Plugin by NYStudio107 <a href="https://github.com/nystudio107/craft-closure" rel="nofollow noreferrer">https://github.com/nystudio107/craft-closure</a></p>
<blockquote>
<p>Craft Closure allows you to use arrow function closures anywhere,
which is especially useful with Laravel Collection methods, many of
which take a closure as a parameter.</p>
</blockquote>

CC BY-SA 4.0
2023-09-19T15:25:10.497Z
2023-09-19T15:25:10.497Z
pecus
<blockquote>
<ol>
<li>Syntax for Laravel methods using arrow functions in twig ?</li>
</ol>
</blockquote>
<p>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 <em>unexpected token</em> error.</p>
<p>I don't think this caveat is documented anywhere, but it's mentioned <a href="https://github.com/twigphp/Twig/issues/3402#issuecomment-704052291" rel="nofollow noreferrer">in this issue</a>:</p>
<blockquote>
<p>It's not supported. Arrow functions are limited in scope.</p>
</blockquote>
<p>So using the appropriate Twig filters like <code>map</code> and <code>unique</code> is the correct approach here.</p>
<hr />
<blockquote>
<ol start="2">
<li>Is a single query possible ?</li>
</ol>
</blockquote>
<p>It's possible, but a bit involved. You need to use two joins to join the <code>content</code> table through the <code>relations</code> junction table to connect join the programs with the associated episodes. Then you can order by the joined the <code>postDate</code> column directly.</p>
<p>Something like this:</p>
<pre class="lang-twig prettyprint-override"><code>{% 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)
%}
</code></pre>
<p>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 …</p>

CC BY-SA 4.0
2022-05-11T18:25:01.163Z
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
Jérôme Coupé
CC BY-SA 4.0
2022-05-11T14:25:20.147Z
2022-05-11T14:25:20.147Z
MoritzLost
<p>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.</p>
<ul>
<li>Each entry in the <code>episodes</code> channel has a one to one relation with a program entry through an <code>episodeProgram</code> entry field (episodes are sources and programs are targets)</li>
<li>I want to display the 6 programs with the most recently published episodes on the homepage</li>
</ul>
<p>What I did:</p>
<ul>
<li>get all episodes (most recent first)</li>
<li>get the ids of related programs</li>
<li>query the <code>programs</code> channel using those ids and <code>fixedOrder</code> to get programs with the most recently published episodes</li>
</ul>
<pre><code>{% set allEpisodes = craft.entries()
  .section(&quot;episodes&quot;)
  .with([&quot;episodeProgram&quot;])
  .orderBy(&quot;postDate DESC&quot;)
  .limit(null)
  .collect() %}

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

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

{% for item in recentPrograms %}
  {% if loop.first %}&lt;ul class=&quot;l-grid  l-grid--gap  l-grid--2cols@small  l-grid--3cols@large  u-margin-bottom-xl&quot;&gt;{% endif %}
    &lt;li&gt;

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

    &lt;/li&gt;
  {% if loop.last %}&lt;/ul&gt;{% endif %}
{% else %}
  &lt;p&gt;No program found&lt;/p&gt;
{% endfor %}
</code></pre>
<p>Everything seems to work fine but I have two issues/questions:</p>
<p><strong>1. Syntax for Laravel methods using arrow functions in twig ?</strong></p>
<p>My code works with the twig <code>|map</code> and <code>|unique</code> filters and I cannot for the life of me figure out how to make it work with the Laravel collections methods (probably my noobness)</p>
<p>I would like to understand why / how to correct it.</p>
<p>Here are the two things I tried:</p>
<pre><code>{% set recentProgramsIds = allEpisodes.map(item =&gt; item.episodeProgram.collect().first().id).unique() %}
</code></pre>
<p>Error: <code>Twig\Error\SyntaxError: Arguments must be separated by a comma. Unexpected token &quot;arrow function&quot; of value &quot;=&gt;&quot; (&quot;punctuation&quot; expected with value &quot;,&quot;). in ... index.twig:62</code></p>
<pre><code>{% set recentProgramsIds = allEpisodes.map((item, key) =&gt; item.episodeProgram.collect().first().id).unique() %}     
</code></pre>
<p>Error: <code>Twig\Error\SyntaxError: An opened parenthesis is not properly closed. Unexpected token &quot;punctuation&quot; of value &quot;,&quot; (&quot;punctuation&quot; expected with value &quot;)&quot;). in ... index.twig:62</code></p>
<p><strong>2. Is a single query possible ?</strong></p>
<p>Second question, is there a more performant way to query the <code>programs</code> channel for entries with the most recently posted related entries of the <code>episodes</code> channel (perhaps using Yii query builders).</p>

CC BY-SA 4.0
2022-05-11T07:15:46.293Z
Can you provide the error message?
Ben Croker
CC BY-SA 4.0
2022-05-11T11:44:50.643Z
Added it as an edit above
Jérôme Coupé
CC BY-SA 4.0
2022-12-13T11:15:19.577Z
Now it is possible to use laravel collections with arrow functions inside twig with https://github.com/nystudio107/craft-closure
tenshi
<relations><craft4><collections>
Craft 4: using the new laravel collections and related methods (in a performant way)
# Craft 4: using the new laravel collections and related methods (in a performant way)

(Asked by: username_0 on 2022-05-11)

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 %}{% endif %}
    

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

    
  {% if loop.last %}{% endif %}
{% else %}
  No program found
{% 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).

**Tags:** relations, craft4, collections
> **Comments:**
> **username_1**: Can you provide the error message?  
> **username_0**: Added it as an edit above  
> **username_2**: Now it is possible to use laravel collections with arrow functions inside twig with https://github.com/nystudio107/craft-closure  

---

## 2 answers

### Answer by username_4 on 2022-05-11. Score: 4 | Accepted answer

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.



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 …

> **Comments:**
> **username_0**: 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  

---

### Answer by username_3 on 2023-09-19. Score: 1

You can use arrow functions pretty much everywhere, overcoming Twig's current limitations, via the Closure Plugin by NYStudio107 <https://github.com/nystudio107/craft-closure>

> Craft Closure allows you to use arrow function closures anywhere,
> which is especially useful with Laravel Collection methods, many of
> which take a closure as a parameter.


---

