Skip to content
This repository has been archived by the owner on Jan 21, 2020. It is now read-only.

Ability to attach simple filters via configuration #58

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

edigu
Copy link
Contributor

@edigu edigu commented Mar 19, 2019

Hello.

Few days ago I wanted to render a Money/Money instance in a twig template something like below:

<div> {{ customer.balance|money }} </div>

and needed to attach an additional custom filter into TwigEnvironment to achieve this goal. Altering the state of the environment somewhere else after it's creation bugged me a bit then decided to extend the whole factory which sounds also bad:

class MyTwigEnvironmentFactory extends ExpressiveTwigEnvironmentFactory
{
    public function __invoke(ContainerInterface $container): Environment
    {
        $environment = parent::__invoke($container);
        $environment->addFilter( ... );

        return $environment;

My actual filter implementation was so simple:

$filter = new TwigFilter('money', function (Money $money) {
    $currencies = ['EUR' => 2, 'USD' => 2];
    $formatter = new DecimalMoneyFormatter(new CurrencyList($currencies));

    return $formatter->format($money); // 
 })

Then I thought that I am literally doing a lot to achieve relatively small task and decided to introduce ability to attach simple filters using configuration.

This PR provides required functionality to attach custom filters into environment instance via configuration like following:

'twig' => [
    'filters' => [
         // user defined filters
         ['name' => 'greet', 'filter' => function($a) { return 'Hello ' . $a; } ],
         ['name' => 'mul', 'filter' => 'Math::multiply', 'options' => ['needs_context' => true] ],
         ['name' => 'money', 'filter' => ['MoneyHelper', 'format'] ],
         ['name' => 'uglify', 'filter' => \Lib\Twig\Filter\Uglify::class ],
     ],
],

I am open to hear opinions of other contributors to improve the implementation further.

Official docs for Twig Filters: https://twig.symfony.com/doc/2.x/advanced.html#filters

@edigu edigu changed the title Twig filters Ability to attach simple filters via configuration Mar 19, 2019
@weierophinney
Copy link
Member

Until we review this, there's another way you can accomplish this that doesn't require extension: use a delegator factory on the Twig\Environment service, and add the filters there:

function (ContainerInterface $container, string $serviceName, callable $factory)
{
    $environment = $factory();
    $environment->addFilter(new TwigFilter(
        'money',
        function (Money $money) { /* ... */ }
    ));
    return $environment;
}

@edigu
Copy link
Contributor Author

edigu commented Mar 21, 2019

Thanks @weierophinney I just switched to delegator factory approach you've suggested and it worked nicely. It feels better than overriding the original factory in application code. But I still could not make sure which approach is more simpler/straightforward for everyone, creating a delegator factory or defining a few configuration options.

In any case documenting the way of introducing new twig filters makes sense since I needed to read whole TwigEnvironmentFactory code to understand and find correct place to inject my filters.

If delegator factories are way to go, I am also okay with ignoring this patch, even if it's working and %100 tested.

@geerteltink geerteltink modified the milestone: 2.4.0 Jun 12, 2019
@geerteltink
Copy link
Member

geerteltink commented Jun 12, 2019

I'm not sure if we should add this.

First of all, with anonymous functions you can not cache your config anymore:

['name' => 'greet', 'filter' => function($a) { return 'Hello ' . $a; } ],

Also it's really easy to create a filter or use an existing one: https://github.com/twigphp/cssinliner-extension/blob/master/src/CssInlinerExtension.php

return [
    'dependencies' => [
        'factories' => [
            CssInlinerExtension::class => InvokableFactory::class,
        ],
    ],

    'twig' => [
        // ...
        'extensions' => [
            CssInlinerExtension::class,
        ],
    ],
];

Another example for a money filter: https://gist.github.com/xtreamwayz/7e54e28338772de765d9d478a6f52318

@weierophinney
Copy link
Member

This repository has been closed and moved to mezzio/mezzio-twigrenderer; a new issue has been opened at mezzio/mezzio-twigrenderer#1.

@weierophinney
Copy link
Member

This repository has been moved to mezzio/mezzio-twigrenderer. If you feel that this patch is still relevant, please re-open against that repository, and reference this issue. To re-open, we suggest the following workflow:

  • Squash all commits in your branch (git rebase -i origin/{branch})
  • Make a note of all changed files (`git diff --name-only origin/{branch}...HEAD
  • Run the laminas/laminas-migration tool on the code.
  • Clone mezzio/mezzio-twigrenderer to another directory.
  • Copy the files from the second bullet point to the clone of mezzio/mezzio-twigrenderer.
  • In your clone of mezzio/mezzio-twigrenderer, commit the files, push to your fork, and open the new PR.
    We will be providing tooling via laminas/laminas-migration soon to help automate the process.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants