我想分享我自己解决这个问题的方法。我实现了自己的 Twig 扩展,它实现了自定义标签widget
(我使用 Twig 标签embed
作为源)。
扩大
小部件节点.php:
namespace Artprima\Bundle\TwigWidgetTagBundle\Twig\Extension;
/**
* Class WidgetNode
*
* @author Denis V
*
* @package Artprima\Bundle\TwigWidgetTagBundle\Twig\Extension
*/
class WidgetNode extends \Twig_Node_Include
{
// we don't inject the module to avoid node visitors to traverse it twice (as it will be already visited in the main module)
public function __construct($filename, $index, \Twig_Node_Expression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null)
{
parent::__construct(new \Twig_Node_Expression_Constant('not_used', $lineno), $variables, $only, $ignoreMissing, $lineno, $tag);
$this->setAttribute('filename', $filename);
$this->setAttribute('index', $index);
}
/**
* Compiles the node to PHP.
*
* @param $compiler \Twig_Compiler A Twig_Compiler instance
*/
public function compile(\Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this);
if ($this->getAttribute('ignore_missing')) {
$compiler
->write("try {\n")
->indent()
;
}
$this->addGetTemplate($compiler);
$compiler->raw('->displayBlock(');
$compiler->string('widget');
$compiler->raw(', ');
$this->addTemplateArguments($compiler);
$compiler->raw(");\n");
if ($this->getAttribute('ignore_missing')) {
$compiler
->outdent()
->write("} catch (Twig_Error_Loader \$e) {\n")
->indent()
->write("// ignore missing template\n")
->outdent()
->write("}\n\n")
;
}
}
protected function addGetTemplate(\Twig_Compiler $compiler)
{
$compiler
->write("\$this->env->loadTemplate(")
->string($this->getAttribute('filename'))
->raw(', ')
->string($this->getAttribute('index'))
->raw(")")
;
}
}
WidgetTokenParser.php:
namespace Artprima\Bundle\TwigWidgetTagBundle\Twig\Extension;
/**
* Class WidgetTokenParser
*
* @author Denis V
*
* @package Artprima\Bundle\TwigWidgetTagBundle\Twig\Extension
*/
class WidgetTokenParser extends \Twig_TokenParser_Include
{
/**
* Parses a token and returns a node.
*
* @param \Twig_Token $token A Twig_Token instance
*
* @return \Twig_NodeInterface A Twig_NodeInterface instance
*/
public function parse(\Twig_Token $token)
{
$stream = $this->parser->getStream();
$parent = $this->parser->getExpressionParser()->parseExpression();
list($variables, $only, $ignoreMissing) = $this->parseArguments();
// inject a fake parent to make the parent() function work
$stream->injectTokens(array(
new \Twig_Token(\Twig_Token::BLOCK_START_TYPE, '', $token->getLine()),
new \Twig_Token(\Twig_Token::NAME_TYPE, 'extends', $token->getLine()),
new \Twig_Token(\Twig_Token::STRING_TYPE, '__parent__', $token->getLine()),
new \Twig_Token(\Twig_Token::BLOCK_END_TYPE, '', $token->getLine()),
));
$module = $this->parser->parse($stream, array($this, 'decideBlockEnd'), true);
// override the parent with the correct one
$module->setNode('parent', $parent);
$this->parser->embedTemplate($module);
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
return new WidgetNode($module->getAttribute('filename'), $module->getAttribute('index'), $variables, $only, $ignoreMissing, $token->getLine(), $this->getTag());
}
public function decideBlockEnd(\Twig_Token $token)
{
return $token->test('endwidget');
}
/**
* Gets the tag name associated with this token parser.
*
* @return string The tag name
*/
public function getTag()
{
return 'widget';
}
}
模板标签扩展.php:
namespace Artprima\Bundle\TwigWidgetTagBundle\Twig\Extension;
/**
* Class TemplateTagsExtension
*
* @author Denis V
*
* @package Artprima\Bundle\TwigWidgetTagBundle\Twig\Extension
*/
class TemplateTagsExtension extends \Twig_Extension
{
/**
* @inheritdoc
*/
public function getTokenParsers()
{
return array(
new WidgetTokenParser(),
);
}
/**
* Returns the name of the extension.
*
* @return string The extension name
*/
public function getName()
{
return 'template_tags';
}
}
服务.yml:
parameters:
artprima.twig.extension.template_tags.class: Artprima\Bundle\TwigWidgetTagBundle\Twig\Extension\TemplateTagsExtension
services:
artprima.twig.extension.template_tags:
class: %artprima.twig.extension.template_tags.class%
tags:
- { name: twig.extension }
使用示例
视图/块/widget.html.twig:
{# please note, that only "widget" block is rendered, all other blocks can be used inside the "widget" block #}
{# if you don't define the "widget" block, nothing will be rendered #}
{% block widget %}
<div class="{{ block('widget_box_class') }}">
{{ block('widget_header') }}
{{ block('widget_body') }}
</div>
{% endblock %}
{% block widget_header %}
<div class="{{ block('widget_header_class') }}">
{{ block('widget_title') }}
{% if display_toolbar is defined and display_toolbar %}{{ block('widget_toolbar') }}{% endif %}
</div>
{% endblock %}
{% block widget_body %}
<div class="{{ block('widget_main_class') }}">
{{ block('widget_main') }}
</div>
{% endblock %}
{% block widget_title %}
<h5 class="widget-title">{{ block('widget_title_text') }}</h5>
{% endblock %}
{% block widget_title_text %}(undefined){% endblock %}
{% block widget_toolbar %}
<div class="widget-toolbar">
{{ block('widget_toolbar_inner') }}
</div>
{% endblock %}
{% block widget_toolbar_inner %}{% endblock %}
{% block widget_box_class %}{% spaceless %}widget-box{% endspaceless %}{% endblock %}
{% block widget_main_class %}{% spaceless %}widget-main{% endspaceless %}{% endblock %}
{% block widget_main %}{% endblock %}
{% block widget_header_class %}{% spaceless %}widget-header{% endspaceless %}{% endblock %}
视图/仪表板/小部件/sample.html.twig
{% widget "ArtprimaSampleBundle:Blocks:widgets.html.twig" %}
{% block widget_title_text %}{{ "widget.records_creation_history"|trans }}{% endblock %}
{% block widget_main_class %}{% spaceless %}no-padding {{ parent() }}{% endspaceless %}{% endblock %}
{% block widget_main %}
<table class="table table-striped table-bordered table-hover no-margin-bottom">
<thead>
<tr>
<th>Description</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ "widget.number_of_countries.created"|trans }}</td>
<td>{{ dashboard.countries.created }}</td>
</tr>
<tr>
<td>{{ "widget.number_of_users.created"|trans }}</td>
<td>{{ dashboard.users.created }}</td>
</tr>
</tbody>
</table>
{% endblock %}
{% endwidget %}
概括
因此,如您所见,使用我的扩展程序,可以包含一个模板并重用其中的块。如果您需要多个小部件,您可以widget
使用相同的源模板在模板中有多个标签,并且块内容不会重叠。本质上,它就像使用 Twig's 嵌入模板embed
(我使用这个标签作为我的扩展的源),但唯一(和主要)不同 - 它只呈现名为“widget”的块。所有其他块都被忽略,但可以在“小部件”块内使用。