Reputation: 27618
I have a Twig extension in my Symfony project which renders a twitter widget. Currently, in my extension, I have this:
public function twitter($handle, $number = 5)
{
return "<div data-tweet-row=\"<div><a href='http://twitter.com/{screen_name}/status/{id}'>{tweet}</a><span class='datetime'>{datetime}</span></div>\" data-twitter-header=\"<div class='header'><img src='https://api.twitter.com/1/users/profile_image?screen_name={screen_name}&size=normal' /><h4><a href='http://twitter.com/{screen_name}'>@{screen_name}</a></h4></div>\" id=\"twitter\"> </div><script>$('#twitter').biff_twitter({screen_name:'$handle',count:$number});</script>";
}
However I really don't like this solution, I'd rather have the HTML saved under views
and then load the file from within my extension.
I have access to the container from within the extension using:
private $container;
public function __construct(ContainerInterface $container){
$this->container = $container;
}
So I need something like
$view_file = $this->container->get('...')->view('InternalSocialBundle:twitter_placeholder.html.twig')
Upvotes: 1
Views: 1292
Reputation: 27618
Ok, so I resolved the issue that I had but it was sort of my fault for not testing this fully. @Cyprian's solution does indeed work absolutely fine when you're not doing anything complicated and just using the extension as intended.
My problem was that I was using this evaluate extension which was loading a template from the database, therefore it was twig that was doing all of the loading (and therefore was only interpreting the first param of $this->render()
as a string
- it made no attempt to load it using the symfony naming pattern).
This means my extension would work fine when using the above method in an actual twig file, but not when I used the {{body|evaluate}}
extension.
The solution to this problem was as follows:
In my services.yml file, not only did I need service_container
, but also twig.loader
so it looked something liked:
class: Test\SocialBundle\Twig\SocialExtension
arguments: [@service_container,@twig.loader]
tags:
- { name: 'twig.extension' }
I then have this as the twig extensions constructor:
public function __construct(ContainerInterface $container, \Symfony\Bundle\TwigBundle\Loader\FilesystemLoader $loader)
{
$this->container = $container;
$this->loader = $loader;
}
In my getFilters()
definition:
public function getFilters()
{
return array(
'twitter' => new \Twig_Filter_Method($this, 'twitter',array(
'is_safe' => array(
'evaluate' => true
),
'needs_environment' => true,
)
)
)
}
and finally, before I returned the rendered output:
public function twitter(\Twig_Environment $environment, $id)
{
$social_item = array(); // get my data here from my service;
$environment->setLoader($this->loader);
return $environment->render("TestSocialBundle:Front:twitter_placeholder.html.twig",array());
}
This ensured that I was always using the \Symfony\Bundle\TwigBundle\Loader\FilesystemLoader
loader, and not \Twig_Loader_String
which was being used in my case.
Upvotes: 1
Reputation: 11374
Of course you have totally right. Name of the service you are looking for is templating
.
So this will work for You:
$html = $this->container->get('templating')->render('InternalSocialBundle:twitter_placeholder.html.twig');
edit:
This is an working example:
class FooExtension extends \Twig_Extension
{
private $container;
public function setContainer(\Symfony\Component\DependencyInjection\Container $container)
{
$this->container = $container;
}
public function foo()
{
return $this->container->get('templating')->render('CogiPortfolioBundle:Portfolio:tmp.html.twig');
}
public function getFunctions()
{
return array(
'foo' => new \Twig_Function_Method($this, 'foo', array('is_safe' => array('html')))
);
}
/* (...) */
}
The service definition:
<service id="cogi.foo.ext" class="Cogi\PortfolioBundle\TwigExtension\FooExtension">
<call method="setContainer">
<argument type="service" id="service_container" />
</call>
<tag name="twig.extension" />
</service>
And know if I call in any template {{ foo() }} it renders html from tmp.twig.html
. And on the other side - if in my extension I put "path" to non existing template in render
method then an exception is thrown.
I hope this helps.
Upvotes: 1