如何导入多个宏?

时间:2016-02-01 17:46:20

标签: symfony macros twig

我想优雅地从一个地方导入多个宏。

我创建了一个名为" macros.twig"的文件。并将其包含在我的模板中:

{% include "_includes/macros" %}

在该文件中,我希望导入所有可用的宏,如下所示:

{% import "_includes/macros/snippets" as snippets %}
{% import "_includes/macros/timestamp" as timestamp %}
{% import "_includes/macros/telephone" as telephone %}
{% import "_includes/macros/subscribe" as subscribe %}
{% import "_includes/macros/image" as image %}
{% import "_includes/macros/admin" as admin %}

这种模块化方法假设可以更轻松地管理我想要全局使用的宏;没有弄乱我主要布局的头部。

目前,当我以这种方式调用宏时,我得到一个"变量"订阅"不存在" 错误。

一次导入多个宏的首选方法是什么?

由于

2 个答案:

答案 0 :(得分:3)

Twig中的

macro标记是一种可用于避免代码重复的函数,适用于具有{% import _self as macro %}的单个模板,或者用于使用一组控制器的一些不同模板之间共享相同的观点变量。

如果您需要在树枝中全局使用某个功能,最好创建一个\Twig_SimpleFunction

请参阅http://twig.sensiolabs.org/doc/advanced.html#functionshttp://symfony.com/doc/current/cookbook/templating/twig_extension.html

根据评论

编辑

无论如何,你可以将这样的东西用于自动加载宏:

<?php
// src/AppBundle/Twig/MacroAutoloadExtension.php

namespace AppBundle\Twig;

class MacroAutoloadExtension 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, 'getMacro'), 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 getMacro(\Twig_Environment $env, array $context, $macro, array $vars = array())
    {
        list($name, $func) = explode('_', $macro);

        $notInContext = 0; // helps generate 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/macro/%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 }
写一些宏:
{# 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 %}
            {{ key }}:
            {% if item is iterable %}
                {% for sub_item in item %}{{ macro_list_ul(sub_item) }}{% endfor %}
            {% else %}{{ item }}{% endif %}
    {% endfor %}
    </ul>
    {% else %}
        <ul><li>{{ array }}</li></ul>
    {% endif %}
{% endmacro %}

然后您可以在视图中随处使用:

{{ macro_list_ol(['un', 'deux', 'trois']) }}

或:

{% set hash = { 'one': 1, 'two': 'deux', 'posts': posts } %} 
{{ macro_list_ul(hash) }}

加成

通常当您使用_self或其他模板导入模板(一个文件)中的宏时,如果您需要设置标记中的宏,则该宏不可用,因为set标记具有不同于_self的范围(即使它共享上下文):

{# /app/Resources/views/includes/macro/outer.html.twig #}
{% macro function(args) %}
    ...
{% endmacro %}

{# /app/Resources/views/Bundle/Controller/action.html.twig #}
{% macro inner_macro(arg1, arg2) %}
    {# render something #}
    {# cannot access context of this view, only args #}
{% endmacro %}

{% import _self as inner %}
{% import '/includes/macro/outer_macro.html.twig' as outer %} {# cannot access context either %}

...

{% set some_var %}
    {# can access context but neither outer or inner #}
    {{ inner.inner_macro('yes', 64) }} {# will not work #}
    {# you need to do import _self as inner again %}

    {# this is fix by both my answer and the one by @KalZekdor #}
    {{ macro_outer_function(var_from_context) }} {# will work #}
{% endset %}

{{ some_var }}

您甚至可以在不使用导入的情况下从宏调用宏。

更新

我创建了gist

答案 1 :(得分:1)

这对我来说有点困难,但是我把一个很好的捆绑包与一个自动加载宏的事件监听器组合在一起。

namespace App\Common\UIExtensionBundle\Listeners;

use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Bundle\TwigBundle\TwigEngine;

class UIExtenderListener
{
    private $macroNamespace = 'ui';

    public function __construct(\Twig_Environment $oTwig, $aMacros)
    {
        $this->twig = $oTwig;

        //Macros
        $this->macros = $aMacros;
    }

    public function onKernelRequest(GetResponseEvent $oEvent)
    {
        $templates = [];
        foreach ($this->macros as $macro => $template)
        {
            $templates[$macro] = $this->twig->loadTemplate($template);
        }

        $this->twig->addGlobal($this->macroNamespace, $templates);

    }
}

捆绑包的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="uiext.macros" type="collection">
            <parameter key="test">UIXBundle:ui:test.html.twig</parameter>
            <parameter key="list">UIXBundle:ui:list.html.twig</parameter>
            <parameter key="entity">UIXBundle:ui:entity.html.twig</parameter>
        </parameter>
    </parameters>

    <services>
        <service id="uiext.extender" class="App\Common\UIExtensionBundle\Listeners\UIExtenderListener" scope="container">
            <tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="1000" />
            <argument type="service" id="twig"/>
            <argument>%uiext.macros%</argument>
        </service>
    </services>
</container>

这里是views\ui\list.html.twig文件:

{% macro ol(arr) %}
    <ol>
        {% for item in arr %}
            <li>{{ item }}</li>
        {% endfor %}
    </ol>
{% endmacro %}

{% macro ul(arr) %}
    <ul>
        {% for item in arr %}
            <li>{{ item }}</li>
        {% endfor %}
    </ul>
{% endmacro %}

然后,从任何树枝模板中,只需添加{{ ui.list.ul(listArr) }}