【问题标题】:How to import multiple macros?如何导入多个宏?
【发布时间】:2016-02-01 17:46:20
【问题描述】:

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

我创建了一个名为“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 %}

这种模块化方法是为了更容易管理我想全局使用的宏;不会弄乱我的主要布局的头部。

目前,当我以这种方式调用宏时,我收到 “变量“订阅”不存在” 错误。

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

谢谢

【问题讨论】:

  • 你的include语句不应该包含macros.twig文件吗?
  • 是的...我遇到了同样的问题...我经历的解决方案有点复杂,最终制作了 Twig Extension 捆绑包。

标签: symfony macros twig


【解决方案1】:

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

如果你需要在 twig 中全局使用一个函数,你最好创建一个\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 标记中使用宏,则该宏不可用,因为set 标记的作用域与 @ 不同987654334@(即使它共享上下文):

{# /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

【讨论】:

  • 使用手动插入和输出 html 的函数首先违背了使用像 Twig 这样的模板引擎的目的。我执行上述操作是为了扩展 Twig 功能,例如添加一些 php 函数(例如 get_class)、定义测试或过滤器,但这不是从任何 twig 模板访问宏的最佳方式。您希望您的 sn-ps 等以您的模板语言(即 Twig)编写,因此您隔离了责任域。当你的懂 html 而几乎不知道 twig 的设计师需要修改一个 sn-p 时会发生什么?
  • @KalZekdor 我已经更新了我的答案,感谢您指出这一点
  • 更新:重构了可调用的过滤参数
  • 当我第一次看到这个答案时,我很难理解它在做什么。我是一个 PHP 初学者,在过去的两周里,我一直在弄清楚它是如何工作的……一行一行。使用“
  • 很高兴有帮助 ;) 你应该检查要点,因为今天有人分叉了它并使它变得非常简单,这也应该有帮助:)
【解决方案2】:

这对我来说也有点困难,但我将一个很好的包与一个自动加载宏的事件侦听器放在一起。

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);

    }
}

bundle 的 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) }}

【讨论】:

  • 我认为您的回答很好,但您可能希望避免为每个请求加载所有模板。通过制作自定义的树枝扩展,您只需在树枝模板中调用它时动态加载您需要的模板,这与“正常”过程几乎相同。
  • @Heah 如果您有数千个宏,是的,这可能是一个问题,但 Twig 的效率相当高。宏的 JIT 加载似乎是过早优化的情况,增加了很多复杂性,只是为了勉强维持一两毫秒。
  • 谢谢你,Kal,过去几周我也一直在研究你的工作。虽然我感谢您对 Heah 解决问题的一些方法的担忧,但我确实看到了不需要 XML 文件的价值。但是,您的方法仍然有效。所以谢谢你,事实证明它非常有用。
  • 是的,但是如果您有其他方法想要检索宏列表,您可以在侦听器的 __construct 中实现它,而不是将它们作为服务配置数据传递。无论哪种方式,很高兴你让它工作,但这些信息可能对其他尝试类似事情的人有用。
猜你喜欢
  • 2012-11-15
  • 2019-04-30
  • 2018-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多