Twig: Selecting certain blocks and rendering them
I am intergrating the twig templating engine into a PHP application. In particular, I would like to use the twig engine to render forms.
Having had a look at how symfony2 uses twig to render form widgets, they have a huge template file which contains all the widgets like so:
(...)
{% block password_widget %}
{% spaceless %}
{% set type = type|default('password') %}
{{ block('field_widget') }}
{% endspaceless %}
{% endblock password_widget %}
{% block hidden_widget %}
开发者_如何学JAVA {% set type = type|default('hidden') %}
{{ block('field_widget') }}
{% endblock hidden_widget %}
{% block email_widget %}
{% spaceless %}
{% set type = type|default('email') %}
{{ block('field_widget') }}
{% endspaceless %}
{% endblock email_widget %}
{% block test_widget %}
{% spaceless %}
<div>
{{test}}
<div>
{% endspaceless %}
{% endblock test_widget %}
(...)
The question I have is how can I "grab" blocks out of this template and render them?
So far, I am able to load the template, and call get blocks to get all the blocks:
twig = new \Twig_Environment($loader, array('cache' => 'cache'));
$template = $twig->loadTemplate('view\form_div_layout.html.twig');
//var_dump($template->getBlocks()); //try getting all blocks
$template->displayBlock('test_widget', array('test' => 'test'));
echo $template->render();
Unfortunately, I am not able to render just the 'test_widget' block in this case. What should I do to retrieve the 'test_widget' block from the template and then insert it into a different template to render the whole form?
Turns out that one should use $template->renderBlock('blockname', array('test' => 'test'));
instead. This will make twig render that block and then return a string containing the markup for that block. One can then use echo to display it or insert it into other templates.
Full example:
$loader = new \Twig_Loader_Filesystem(array('/my-template-root'));
$twig = new \Twig_Environment($loader, array('debug' => true));
$template = $twig->loadTemplate('view\form_div_layout.html.twig');
$result = $template->renderBlock('blockname', array('test' => 'test'));
echo $result;
If you are using Symfony and want to be able to still have access to the global variables (app
, app.user
, etc) then this works great:
private function renderBlock($template, $block, $params = [])
{
/** @var \Twig\Environment $twig */
$twig = $this->get('twig');
/** @var \Twig\TemplateWrapper $template */
$template = $twig->load($template);
return $template->renderBlock($block, $twig->mergeGlobals($params));
}
I just added this has a private function on my controller. Works great. Thanks to @F21 for pointing me in the right direction.
This is great! I also wanted to add that there is a hasBlock
function which allows you to do validation before you attempt to do the template rendering. This allows you to validate the template is built as expected, or in my case have optional blocks. For what it's worth, here's an example from one of my console apps which sends notification emails
$templateContent = $this->getContainer()->get('twig')->loadTemplate('FTWGuildBundle:AuctionNotification:notificationEmail.html.twig');
$body = $templateContent->renderBlock('body', array('siteDomain' => $siteClient->getSiteDomain(), 'staticContentDomain' => $siteClient->getStaticContentDomain(), 'batch' => $batch->getNotifications(), 'auction_notification_lockout_period' => $this->getContainer()->getParameter('auction_notification_lockout_period')));
$subject = ($templateContent->hasBlock("subject")
? $templateContent->renderBlock("subject", array('batchSize' => $batch->getSize(), 'batch' => $batch))
: "Auction House Notifications");
Note: hasBlock won't work if the template extends or embeds a parent block.
see https://github.com/twigphp/Twig/issues/450
精彩评论