Open EvanK opened 12 years ago
So it looks like the reason for this is that it doesn't use the existing twig service in symfony, it creates its own service presumably so it can use its own loader class of LK\TwigstringBundle\Loader\String
.
While an understandable limitation, is there a way to load extensions specifically for this bundle instead of (or in addition to) the twig engine that symfony uses for template rendering?
+1 on this.
The path() extension doesn't seem to work either.
any solutions there? workarounds?
+1 to this as well, being able to use path and url inside templates would be incredibly useful. For now I'm going to have to generate the route beforehand and pass it in as a variable.
i made a simple but dirty workaround for this. I don't use the TwigstringBundle anymore and use a own twig loader instead. (until there is a symfony solution to use multiple twig loaders in a project). If a template with the ending .twig is submitted, it uses the FilesystemLoader and if not.. it just uses the string.
### loader
namespace Project\Bundle\BackendBundle\Twig\Loader;
use Symfony\Bundle\TwigBundle\Loader\FilesystemLoader;
class Loader extends FilesystemLoader
{
public function getSource($name){
$ext = pathinfo($name, PATHINFO_EXTENSION);
if($ext == 'twig'){
return parent::getSource($name);
}else{
return $name;
}
}
public function getCacheKey($name)
{
$ext = pathinfo($name, PATHINFO_EXTENSION);
if($ext == 'twig'){
return parent::getCacheKey($name);
}else{
return $name;
}
}
public function isFresh($name, $time)
{
$ext = pathinfo($name, PATHINFO_EXTENSION);
if($ext == 'twig'){
return parent::isFresh($name, $time);
}else{
return true;
}
}
}
### service
<service id="twig.loader" class="Project\Bundle\BackendBundle\Twig\Loader\Loader">
<argument type="service" id="templating.locator" />
<argument type="service" id="templating.name_parser" />
</service>
a problem is, that you have to add the path of the symfony brige resources by yourself:
### Weekend4two\Bundle\BackendBundle\DependencyInjection\ProjectBackendExtension.php
$container->getDefinition('twig.loader')->addMethodCall('addPath', array(__DIR__.'/../../../../../vendor/symfony/src/Symfony/Bridge/Twig/Resources/views/Form'));
i know it's not beautiful.. but as i said, when there is a solution in symfony, you can just switch..
The bundle does not load any of the twig extensions...simply copying the config's of the default Symfony twig bundle doesn't work as the bundle does not support the adding of extensions via tags (which is what the default Symfony twig bundle is using).
So first we need to make the bundle support tags; to do that add the following class to the bundle
\LK\TwigstringBundle\DependencyInjection\Compiler\TwigEnvironmentPass.php:
<?php
namespace LK\TwigstringBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
class TwigEnvironmentPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (false === $container->hasDefinition('twig2')) {
return;
}
$definition = $container->getDefinition('twig2');
// Extensions must always be registered before everything else.
// For instance, global variable definitions must be registered
// afterward. If not, the globals from the extensions will never
// be registered.
$calls = $definition->getMethodCalls();
$definition->setMethodCalls(array());
foreach ($container->findTaggedServiceIds('twig2.extension') as $id => $attributes) {
$definition->addMethodCall('addExtension', array(new Reference($id)));
}
$definition->setMethodCalls(array_merge($definition->getMethodCalls(), $calls));
}
}
This class is a copy of the symfony class at https://github.com/symfony/TwigBundle/blob/master/DependencyInjection/Compiler/TwigEnvironmentPass.php, with just the tags renamed to 'twig2.extension' instead.
Now we need to add this class as a compiler pass:
\LK\TwigstringBundle\LKTwigstringBundle.php:
<?php
namespace LK\TwigstringBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class LKTwigstringBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new DependencyInjection\Compiler\TwigEnvironmentPass());
}
}
Once this is done just add the extensions you need at \LK\TwigstringBundle\Resources\config\services.xml.
eg. I needed the routing extension myself so here is my current services.xml:
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="twig2.class">Twig_Environment</parameter>
<parameter key="twig2.loader.class">LK\TwigstringBundle\Loader\String</parameter>
<parameter key="templating.engine.twig2.class">Symfony\Bundle\TwigBundle\TwigEngine</parameter>
<parameter key="twig2.extension.routing.class">Symfony\Bridge\Twig\Extension\RoutingExtension</parameter>
</parameters>
<services>
<service id="twig2" class="%twig2.class%" public="true">
<argument type="service" id="twig2.loader" />
<argument>%twig.options%</argument>
</service>
<service id="twig2.loader" class="%twig2.loader.class%" public="false">
<argument type="service" id="templating.locator" />
<argument type="service" id="templating.name_parser" />
</service>
<service id="templating.engine.twig2" class="%templating.engine.twig2.class%" public="false">
<argument type="service" id="twig2" />
<argument type="service" id="templating.name_parser" />
<argument type="service" id="templating.globals" />
</service>
<service id="twig2.extension.routing" class="%twig2.extension.routing.class%" public="false">
<tag name="twig2.extension" />
<argument type="service" id="router" />
</service>
</services>
</container>
After the doing the above I can successfully call the 'path' function in my 'string' twig templates. (More about tagging here: http://symfony.com/doc/current/cookbook/service_container/tags.html)
You can find the other extensions that come with the default symfony install here https://github.com/symfony/TwigBundle/blob/master/Resources/config/twig.xml
While the above worked for me, after looking at the default twig bundle which lots of stuff like a cache, cache warmer already built in (and I'm not sure if those would work with this bundle without major additions) I'm thinking it may be a better just to write the 'string' twig content to a temporary file in the cache, and just the use the default twig service on that file...
Thank you very much hsarwar, this worked fine for me :). However, the best would be that Twigstring bundle offers some more functionalities related to filters, functions, etc., e.g.:
For instance, I use twigstring to handle emails. Each email has a template and several models (one for each language). I want to protect models from any filters/function misusage...
Would be nice ^^
I have filters being loaded from both the official Text twig extension and a custom extension I've written, both configured via my
config.yml
:They are being used in normal twig templates without issue:
However, trying to use them with the TwigstringBundle throws a Twig_Error_Syntax exception with a message of
The filter "truncate" does not exist
: