Skip to content

Instantly share code, notes, and snippets.

@HeahDude
Last active March 23, 2018 22:54
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save HeahDude/f9aeb128fcac309dddc8 to your computer and use it in GitHub Desktop.
Save HeahDude/f9aeb128fcac309dddc8 to your computer and use it in GitHub Desktop.
MacroTwigExtension - Autoloading macro in templates for symfony 2.3+
{# app/Resources/views/Bundle/Controller/action.html.twig #}
{{ macro_list_ol(['un', 'deux', 'trois']) }}
{% set hash = { 'one': 'un', 'two': 2, 'posts': posts } %}
{{ macro_list_ul(hash) }}
{% macro article(art) %}
<article>
<h3>{{ art.title }}</h3>
<p>{{ art.content }}</p>
<div class="comments">{{ macro_list_ol(art.comments) }}</div> {# autoloaded from macro #}
<article>
{% endmacro %}
{% import _self as macro %}
{{ macro.article(post) }} {# needs to be imported as current "_self" is not in autoloaded library #}
{# app/Resources/views/_includes/macros/list.twig #}
{% macro ol(array) %}
{% if array is iterable %}
<ol>
{% for item in array %}
<li>
{% if item is iterable %}
{% for sub_item in item %}{{ macro_list_ul(sub_item) }}{% endfor %}
{% else %}{{ item }}{% endif %}
</li>
{% endfor %}
</ol>
{% else %}
<ol><li>{{ array }}</li></ol>
{% endif %}
{% endmacro %}
{% macro ul(array) %}
{% if array is iterable %}
<ul>
{% for key, item in array %}
<li>{{ key }}:
{% if item is iterable %}
{% for sub_item in item %}{{ macro_list_ol(item) }}{% endfor %}
{% else %}
{{ item }}
{% endif %}
</li>
{% endfor %}
</ul>
{% else %}
<ul><li>{{ array }}</li></ul>
{% endif %}
{% endmacro %}
<?php
// src/AppBundle/Twig/MacroTwigExtension.php
namespace AppBundle\Twig;
class MacroAutoloadTwigExtension extends \Twig_Extension
{
public function getFunctions()
{
return array(
// "*"" is used to get "template_macro" as $macro as third argument
new \Twig_SimpleFunction('macro_*', array($this, 'twig_render_macro'), array(
'needs_environment' => true, // $env first argument will render the macro
'needs_context' => true, // $context second argument an array of view vars
'is_safe' => array('html'), // function returns escaped html
'is_variadic' => true, // and takes any number of arguments
))
);
}
public function twig_render_macro(\Twig_Environment $env, array $context, $macro, array $vars = array())
{
list($name, $func) = explode('_', $macro);
$notInContext = 0; // helps generate a unique context key
$varToContextKey = function ($var) use (&$context, $name, $func, &$notInContext) {
if (false !== $idx = array_search($var, $context, true)) {
return $idx;
}
// else the var does not belong to context
$key = '_'.$name.'_'.$func.'_'.++$notInContext;
$context[$key] = $var;
return $key;
};
$args = implode(', ', array_map($varToContextKey, $vars));
$twig = <<<EOT
{% import '_includes/macros/$name.twig' as $name %}
{{ $name.$func($args) }}
EOT;
try {
$html = $env->createTemplate($twig)->render($context);
} catch (\Twig_Error $e) {
$e->setTemplateFile(sprintf('_includes/macros/%s.twig', $name));
throw $e;
}
return $html;
}
public function getName()
{
return 'macro_autoload_extension';
}
}
# app/config/sevices.yml
services:
app.macro_autoload_extension:
class: AppBundle\Twig\MacroAutoloadExtension
public: false
tags:
- { name: twig.extension }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment