Symfony, How to generate Asset URL in a Twig Extension class?
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);
}
}
pmoubed
Updated on June 27, 2022Comments
-
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 almost 12 yearsWhat should I inject in config.yml ? "@what" ?
-
MDrollette almost 12 yearsThe service id is "templating.helper.assets". So @templating.helper.assets should do it
-
pmoubed almost 12 yearsIt will complain about the scope if you try to inject templating.helper.assets
-
MDrollette almost 12 yearsah, 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 almost 12 yearsYes, I did inject the container "@service_container" and I used $this->container->get('templating.helper.assets') . Thanks.
-
GergelyPolonkai almost 12 yearsadd 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 about 11 yearsIs there a reference/source for that service?
-
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 almost 10 yearsThank you for this. I much prefer this method over injecting the container
-
althaus over 8 yearsThis will trigger a deprecation warning nowadays which can be fixed by implementing the
Twig_Extension_InitRuntimeInterface
or reworking the code with injection of theTwig_Environment
: twig.sensiolabs.org/doc/advanced.html#environment-aware-filters -
pabgaran about 7 yearsThe templating.helper.assets service was replaced in Symfony 3.0.* by assets.packages github.com/Gregwar/ImageBundle/issues/92
-
James almost 5 yearsScopes were deprecated and no-one (ideally..) should be using 2.8 now symfony.com/blog/…