Twig icon indicating copy to clipboard operation
Twig copied to clipboard

Trivial arrow filter

Open donquixote opened this issue 3 years ago • 7 comments

There are a few twig filters that allow arrow functions as argument: map, filter, reduce. All of these apply the arrow function to array values (or keys).

I wonder, could we make a trivial filter that applies the arrow function to the original piped argument?

{{ set object = value|arrow(val => val ? {value: val} : {}) }}

The main purpose would be as part of a longer pipe chain.

I noticed this can already be done in userland, but it seems like a useful addition to core, or not?

      new TwigFilter('arrow', [self::class, 'arrow'], ['needs_environment' => true]),
  public static function arrow(Environment $env, $arg, $arrow) {
    twig_check_arrow_in_sandbox($env, $arrow, 'arrow', 'filter');
    return $arrow($arg);
  }

donquixote avatar Apr 07 '22 17:04 donquixote

Related:

  • #3192 was rejected.
  • #3402 was rejected.

I think the arrow() filter proposed here is quite basic and uncontroversial, is it not?

donquixote avatar Apr 07 '22 17:04 donquixote

I don't think this filter belongs to the core. In most cases, the same result can be achieved by using a Twig expression directly.

stof avatar Apr 07 '22 17:04 stof

I don't think this filter belongs to the core. In most cases, the same result can be achieved by using a Twig expression directly.

But then we can't chain it up with other filters easily. E.g. to have a macro inside a pipe.

{{ items
    |filter(item => ..)
    |map(item => ..)
    |join(', ')
    |arrow(markup => (markup|trim is not empty) ? _self.wrap('div'))
}}

Or to call a macro from apply:

{% set title %}
<h3>Hello</h3>
{% endset %}
{% apply arrow(markup => (markup|trim is not empty) ? title ~ markup : '') %}
  {{ output }}
{% endapply %}

(btw the ~ operator loses the TwigMarkup class, but this is another story - see #3679)

donquixote avatar Apr 07 '22 18:04 donquixote

To make this work this one line: https://github.com/twigphp/Twig/blob/3.x/src/ExpressionParser.php#L483 from:

                    foreach ($this->parseArguments() as $n) {

to:

                    foreach ($this->parseArguments(false, false, true) as $n) {

then closures all work in the context we want, e.g.:

    {% set collection = collect(['a', 'b', 'c']) %}
    {% set contains = collection.contains((value, key) => value == 'a') %}

with a nice, clean, normal syntax. I'd love to see this happen, too.

khalwat avatar Aug 02 '22 12:08 khalwat

+1 for this.

As a consumer of Twig, I just want it to work as it looks like it should. And it looks like it should support closures. Why does it matter if there are other ways of doing it?

MattWilcox avatar Mar 22 '23 16:03 MattWilcox

This is a solution for Craft CMS users: https://github.com/nystudio107/craft-closure

khalwat avatar Mar 22 '23 16:03 khalwat

+1 from me on this too!

The change proposed by @khalwat seems clean!

thupsi avatar Mar 22 '23 18:03 thupsi