Symfony, How to generate Asset URL in a Twig Extension class?

11,245

Solution 1

You can use the templating.helper.assets service directly.

use Symfony\Component\DependencyInjection\ContainerInterface;

public function __construct(ContainerInterface $container)
{
    $this->container = $container;
}

and use it like so:

$this->container->get('templating.helper.assets')->getUrl($iconlink);

Injecting just the templating.helper.assets directly does not work in this case because the twig extension cannot be in the request scope. See the documentation here: https://symfony.com/doc/2.3/cookbook/service_container/scopes.html#using-a-service-from-a-narrower-scope

Solution 2

I didn't want to deal with the Dependency Injection Container. This is what I did:

use Twig_Environment as Environment;

class MyTwigExtension extends \Twig_Extension
{
    protected $twig;
    protected $assetFunction;

    public function initRuntime(Environment $twig)
    {
        $this->twig = $twig;
    }

    protected function asset($asset)
    {
        if (empty($this->assetFunction)) {
             $this->assetFunction = $this->twig->getFunction('asset')->getCallable();
        }
        return call_user_func($this->assetFunction, $asset);
    }

I've looked at Twig_Extension class code, and found this initRuntime method there, to be overriden in our custom Extension class. It receives the Twig_Environment as an argument! This object has a getFunction method, which returns a Twig_Function instance. We only need to pass the function name (asset, in our case).

The Twig_Function object has a getCallable method, so we finally can have a callable asset function.

I've gone a bit further creating an asset method for my own extension class. Anywhere else on it, I can simply call $this->asset() and obtain the same result as {{ asset() }} in the templates.

EDIT: The getFunction call at initRuntime throws a scope exception when clearing the cache. So I moved it to the custom asset method. It works fine.

Solution 3

Here's a simple and clean way for Symfony 2.8:

services.yml:

app.twig_extension:
    class: Path\To\AcmeExtension
    arguments:
        assets: "@templating.helper.assets"

In the TWIG extension:

use Symfony\Bundle\FrameworkBundle\Templating\Helper\AssetsHelper;

class AcmeExtension
{
    protected $assets;

    public function __construct(AssetsHelper $assets)
    {
        $this->assets = $assets;
    }
}

Then you can use it in any function of the extension like this:

$this->assets->getUrl('myurl');

Solution 4

In Symfony 5.3 that worked for me: (just do what the assets extension does and inject Packages)


use Symfony\Component\Asset\Packages;
use Twig\Extension\AbstractExtension;
use Twig\Extension\ExtensionInterface;

class AppExtension extends AbstractExtension implements ExtensionInterface
{
    public function __construct(Packages $packages)
    {
        $this->packages = $packages;
    }

    // ... your other methods

    private function asset($path, $packageName = null)
    {
        return $this->packages->getUrl($path, $packageName);
    }
}
Share:
11,245
pmoubed
Author by

pmoubed

Updated on June 27, 2022

Comments

  • pmoubed
    pmoubed almost 2 years

    I have one class which extends \Twig_Extension like below :

    class MYTwigExtension extends \Twig_Extension
    {
    
        protected $doctrine;
    
        protected $router;
    
    
        public function __construct(RegistryInterface $doctrine , $router)
        {
            $this->doctrine = $doctrine;
            $this->router = $router;
    
        }
    
        public function auth_links($user , $request)
        {
           // Some other codes here ...
    
           // HOW TO GENERATE $iconlink which is like '/path/to/an/image'
    
           $html .= "<img src=\"$iconlink\" alt=\"\" />  ";  
    
           echo $html;
        }
    
    }
    

    My question is How to generate Asset links in a Twig Extension ? I would like a replacement for ASSET helper in my class. Bassically I have no idea what I have to inject or use here ! Thanks in advance.

     <img src="{{ asset('img/icons/modules/timesheet.png') }}" alt="" /> 
    
  • pmoubed
    pmoubed almost 12 years
    What should I inject in config.yml ? "@what" ?
  • MDrollette
    MDrollette almost 12 years
    The service id is "templating.helper.assets". So @templating.helper.assets should do it
  • pmoubed
    pmoubed almost 12 years
    It will complain about the scope if you try to inject templating.helper.assets
  • MDrollette
    MDrollette almost 12 years
    ah, I see, you will have to add scope: request to your service definition. Alternatively, you can inject the container itself and then calling $this->container->get('templating.helper.assets') should also work.
  • pmoubed
    pmoubed almost 12 years
    Yes, I did inject the container "@service_container" and I used $this->container->get('templating.helper.assets') . Thanks.
  • GergelyPolonkai
    GergelyPolonkai almost 12 years
    add scope: request to your service definition, and Symfony 2.1 will bark a huge error message in your face saying Scope Widening Injection detected: The definition "twig" references the service "bb.twig.extension" which belongs to a narrower scope. Generally, it is safer to either move "twig" to scope "request" or alternatively rely on the provider pattern by injecting the container itself, and requesting the service "bb.twig.extension" each time it is needed. In rare, special cases however that might not be necessary, then you can set the reference to strict=false to get rid of this error.
  • alex88
    alex88 about 11 years
    Is there a reference/source for that service?
  • rbhalla
    rbhalla almost 10 years
    @MDrollette when injecting the container and using get('templating.helper.assets') I still get You cannot create a service ("templating.helper.assets") of an inactive scope ("request") when running assetic:dump
  • rbhalla
    rbhalla almost 10 years
    Thank you for this. I much prefer this method over injecting the container
  • althaus
    althaus over 8 years
    This will trigger a deprecation warning nowadays which can be fixed by implementing the Twig_Extension_InitRuntimeInterface or reworking the code with injection of the Twig_Environment: twig.sensiolabs.org/doc/advanced.html#environment-aware-filt‌​ers
  • pabgaran
    pabgaran about 7 years
    The templating.helper.assets service was replaced in Symfony 3.0.* by assets.packages github.com/Gregwar/ImageBundle/issues/92
  • James
    James almost 5 years
    Scopes were deprecated and no-one (ideally..) should be using 2.8 now symfony.com/blog/…