5

ボックスを表すCSSを使用したdivがあり、htmlコードをラップしています。

<div class="box indent">
  <div class="padding">
    my code here
  </div>
</div>

すべての HTML ラッパー (ボックス、タブ、グリッドなど) が個別の twig ファイル内に配置される "layoutbundle" を作成しました。このようにして、他のバンドルのビューを他のレイアウトで実装できます。

しかし、私はインクルードにうんざりしています。すべての小さな HTML ラッパーにはインクルードが必要です。HTML コードをラップする簡単な方法があるのではないかと思います。

単純なボックスの例を見てみましょう。実際、いくつかのファイルを作成しました:

box.html.twigボックスを含み、コンテンツを含むファイル:

<div class="box indent">
  <div class="padding">
    {% include content %}
  </div>
</div>

私のボックスのコンテンツを含む、いくつかのボックスコンテンツ.html.twig ファイル。

最後に、次のようにしてビューにボックスを作成します。

{%
  include 'AcmeDemoBundle:layout:box.html.twig'
  with {
    'content': 'ReusableBundle:feature:xxx.html.twig'
  }
%}

次のようなラッパーを作成する方法はありますか:

a)新しいラッパーを一度宣言します:

{% wrapperheader "box" %}
    <div class="box indent">
      <div class="padding">
{% endwrapperheader %}

{% wrapperfooter "box" %}
      </div>
    </div>
{% endwrapperfooter %}

b)そして、私のページでは、次を使用します:

{% wrapper "box" %}
  {# here my content #}
{% endwrapper %}

Twig に新しいタグ拡張機能を追加する必要があると思いますが、最初に同様のことがネイティブで可能かどうかを知りたいです。

4

2 に答える 2

12

ブロック方式

この方法は、GitHubでSebastiaan Stokによって提案されました。

ブロック機能を利用したアイデアです。指定されたブロックの内容を書き込み、複数回呼び出すことができます。

ラッパーファイル:

{# src/Fuz/LayoutBundle/Resources/views/Default/wrappers.html.twig #}

{% block box_head %}
    <div class="box indent">
      <div class="padding">
{% enblock %}

{% block box_foot %}
      </div>
    </div>
{% enblock %}

特集ページ:

{{ block('box_head') }}
Some content
{{ block('box_foot') }}

マクロを使用したラップ拡張

このアイデアは、 CharlesによってGitHubで提案されました。

まず、ファイルでマクロを宣言しmacro.html.twigます。

{% macro box(content) %}
    <div class="box indent">
      <div class="padding">
         {{ content | raw }}
      </div>
    </div>
{% endmacro %}

Amd を呼び出す代わりに(ドキュメント{{ macros.box('my content') }}を参照して、マクロ呼び出しを処理するタグを作成します。{% wrap %}[% wrap %}{% endwrap %}

この拡張機能は簡単に開発できました。マクロにアクセスするのは難しいのではないかと思っていましたが、実際にはオブジェクトとしてコンテキストに格納されており、呼び出しは簡単にコンパイルできます。

いくつかの変更のみ: 次の構文を使用します。

{# to access a macro from an object #}
{% wrap macro_object macro_name %}
  my content here
{% endwrap %}

{# to access a macro declared in the same file #}
{% wrap macro_name %}
   macro
{% endwrap %}

次のコードでは、機能させたい場合は名前空間を変更することを忘れないでください!

まず、services.yml に拡張機能を追加します。

parameters:
    fuz_tools.twig.wrap_extension.class: Fuz\ToolsBundle\Twig\Extension\WrapExtension

services:
  fuz_tools.twig.wrap_extension:
    class: '%fuz_tools.twig.wrap_extension.class%'
    tags:
      - { name: twig.extension }

バンドル内に Twig ディレクトリを作成します。

拡張機能を追加すると、新しいタグが返されますTokenParser(英語では、新しいタグが宣言されます)。

小枝/拡張機能/WrapExtension.php:

<?php

// src/Fuz/ToolsBundle/Twig/Extension/WrapExtension.php

namespace Fuz\ToolsBundle\Twig\Extension;

use Fuz\ToolsBundle\Twig\TokenParser\WrapHeaderTokenParser;
use Fuz\ToolsBundle\Twig\TokenParser\WrapFooterTokenParser;
use Fuz\ToolsBundle\Twig\TokenParser\WrapTokenParser;

class WrapExtension extends \Twig_Extension
{

    public function getTokenParsers()
    {
        return array (
                new WrapTokenParser(),
        );
    }

    public function getName()
    {
        return 'wrap';
    }

}

{% wrap %}次に、TokenParser 自体を追加します。これは、パーサーがタグを見つけたときに満たされます。これTokenParserは、タグが正しく呼び出されているかどうかを確認し (この例では、2 つのパラメーターがあります)、それらのパラメーターを保存し{% wrap %}、{% endwrap %}` の間のコンテンツを取得します。

小枝/TokenParser/WrapTokenParser.php:

<?php

// src/Fuz/ToolsBundle/Twig/TokenParser/WrapTokenParser.php

namespace Fuz\ToolsBundle\Twig\TokenParser;

use Fuz\ToolsBundle\Twig\Node\WrapNode;

class WrapTokenParser extends \Twig_TokenParser
{

    public function parse(\Twig_Token $token)
    {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();

        $object = null;
        $name = $stream->expect(\Twig_Token::NAME_TYPE)->getValue();

        if ($stream->test(\Twig_Token::BLOCK_END_TYPE))
        {
            if (!$this->parser->hasMacro($name))
            {
                throw new \Twig_Error_Syntax("The macro '$name' does not exist", $lineno);
            }
        }
        else
        {
            $object = $name;
            $name = $stream->expect(\Twig_Token::NAME_TYPE)->getValue();
        }

        $this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);
        $body = $this->parser->subparse(array ($this, 'decideWrapEnd'), true);
        $this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);

        return new WrapNode($object, $name, $body, $token->getLine(), $this->getTag());
    }

    public function decideWrapEnd(\Twig_Token $token)
    {
        return $token->test('endwrap');
    }

    public function getTag()
    {
        return 'wrap';
    }

}

{% wrap %}次に、コンパイラ (twig 方言のノード) が必要です。コンパイラは、タグに関連付けられた PHP コードを生成します。

このタグは のエイリアスで{{ macro_object.box(content) }}あるため、その行をテンプレートに記述し、生成された php ファイル (ディレクトリに保存されている) で結果のコードを確認しましたapp/cache/dev/twig。私が得た:

echo $this->getAttribute($this->getContext($context, "(macro object name)"), "(name)", array("(body)"), "method");

だから私のコンパイラは次のようになりました:

小枝/ノード/WrapNode.php:

<?php

// src/Fuz/ToolsBundle/Twig/Node/WrapNode.php

namespace Fuz\ToolsBundle\Twig\Node;

class WrapNode extends \Twig_Node
{

    public function __construct($object, $name, $body, $lineno = 0, $tag = null)
    {
        parent::__construct(array ('body' => $body), array ('object' => $object, 'name' => $name), $lineno, $tag);
    }

    public function compile(\Twig_Compiler $compiler)
    {
        $compiler
           ->addDebugInfo($this)
           ->write('ob_start();');

        $compiler
           ->addDebugInfo($this)
           ->subcompile($this->getNode('body'));

        if (is_null($this->getAttribute('object')))
        {
            $compiler
               ->write(sprintf('echo $this->get%s(ob_get_clean());', $this->getAttribute('name')) . "\n");
        }
        else
        {
            $compiler
               ->write('echo $this->getAttribute($this->getContext($context, ')
               ->repr($this->getAttribute('object'))
               ->raw('), ')
               ->repr($this->getAttribute('name'))
               ->raw(', array(ob_get_clean()), "method");')
               ->raw("\n");
        }
    }

}

注:サブパース/サブコンパイルがどのように機能するかを知るために、spaceless拡張ソースコードを読みました。

それで全部です!大きなボディを持つマクロを使用できるようにするエイリアスを取得します。それを試すには:

macros.html.twig:

{% macro box(content) %}
    <div class="box indent">
      <div class="padding">
         {{ content | raw }} {# Don't forget the raw filter! #}
      </div>
    </div>
{% endmacro %}

いくつかのlayout.html.twig:

{% import "FuzLayoutBundle:Default:macros.html.twig" as macros %}

{% wrap macros box %}
test
{% endwrap %}

{% macro test(content) %}
some {{ content | raw }} in the same file
{% endmacro %}

{% wrap test %}
macro
{% endwrap %}

出力:

    <div class="box indent">
      <div class="padding">
test
      </div>
    </div>

some macro in the same file

wrapperheader、wrapperfooter、wrapper エクステンション

この方法は、私の質問で説明した方法です。トークンパーサーで自分自身をトレーニングしたい場合は、それを読んだり実装したりできますが、機能的には、以前の方法よりも良くありません。

ファイルで、すべてのラッパーを宣言しwrapper.html.twigます。

{% wrapperheader box %}
    <div class="box">
{% endwrapper %}
{% wrapperfooter box %}
    </div>
{% endwrapperfooter %}

features twig ファイルでは、ラッパーを使用します。

{% wrapper box %}
This is my content
{% endwrapper %}

次の拡張機能には 3 つの問題があります。

  • Twig 環境にデータ (コンテキスト変数など) を保存する方法はありません。したがって、 を定義する場合{% wrapperheader NAME %}、基本的に、 のヘッダーNAMEが既に定義されているかどうかを確認する明確な方法はありません (この拡張機能では、静的プロパティを使用します)。

  • 小枝ファイルの場合include、すぐにではなく実行時に解析されます (つまり、含まれている小枝テンプレートは、タグが解析されるときではなく、生成されたファイルが実行されるときに解析されます)。そのため、タグincludeを解析するときに、以前にインクルードされたファイルにラッパーが存在するかどうかを知ることはできません。{% wrapper NAME %}ラッパーが存在しない場合、この拡張機能はその間{% wrapper %}にあるもの{% endwrapper %}を予告なしに表示するだけです。

  • この拡張の考え方は次のとおりです。パーサーが and タグに遭遇するwrapperheaderwrapperfooter、コンパイラはタグのコンテンツをどこかに保存して、後でwrapperタグで使用できるようにします。{% include %}ただし、小枝のコンテキストは、参照ではなくコピーとして渡されます。そのため、上位レベル (ファイルを含むファイル内) で使用するために、そのコンテキスト内に情報{% wrapperheader %}を格納することはできません。{% wrapperfooter %}グローバル コンテキストも使用する必要がありました。

これがコードです。名前空間を変更するように注意してください。

まず、Twig に新しいトークン パーサーを追加する拡張機能を作成する必要があります。

バンドルの services.yml 内に、次の行を追加して拡張機能を有効にします。

parameters:
    fuz_tools.twig.wrapper_extension.class: Fuz\ToolsBundle\Twig\Extension\WrapperExtension

services:
  fuz_tools.twig.wrapper_extension:
    class: '%fuz_tools.twig.wrapper_extension.class%'
    tags:
      - { name: twig.extension }

バンドル内に Twig ディレクトリを作成します。

次の Twig\Extension\WrapperExtension.php ファイルを作成します。

<?php

// src/Fuz/ToolsBundle/Twig/Extension/WrapperExtension.php

namespace Fuz\ToolsBundle\Twig\Extension;

use Fuz\ToolsBundle\Twig\TokenParser\WrapperHeaderTokenParser;
use Fuz\ToolsBundle\Twig\TokenParser\WrapperFooterTokenParser;
use Fuz\ToolsBundle\Twig\TokenParser\WrapperTokenParser;

class WrapperExtension extends \Twig_Extension
{

    public function getTokenParsers()
    {
        return array(
            new WrapperHeaderTokenParser(),
            new WrapperFooterTokenParser(),
            new WrapperTokenParser(),
        );
    }

    public function getName()
    {
        return 'wrapper';
    }

}

次に、トークン パーサーを追加する必要があります。構文はand{% wrapper NAME %} ... {% endwrapper %}と同じです。したがって、これらのトークン パーサーは、タグの宣言、ラッパーの の取得、および本体 (と endwrapper` の間にあるもの) の取得に使用されます。wrapperheaderwrapperfooterNAMEwrapper

ラッパーのトークン パーサー: Twig\TokenParser\WrapperTokenParser.php:

<?php

// src/Fuz/ToolsBundle/Twig/TokenParser/WrapperTokenParser.php

namespace Fuz\ToolsBundle\Twig\TokenParser;

use Fuz\ToolsBundle\Twig\Node\WrapperNode;

class WrapperTokenParser extends \Twig_TokenParser
{

    public function parse(\Twig_Token $token)
    {
        $stream = $this->parser->getStream();
        $name = $stream->expect(\Twig_Token::NAME_TYPE)->getValue();
        $this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);
        $body = $this->parser->subparse(array($this, 'decideWrapperEnd'), true);
        $this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);
        return new WrapperNode($name, $body, $token->getLine(), $this->getTag());
    }

    public function decideWrapperEnd(\Twig_Token $token)
    {
        return $token->test('endwrapper');
    }

    public function getTag()
    {
        return 'wrapper';
    }

}

wrapperheader のトークン パーサー: Twig\TokenParser\WrapperHeaderTokenParser.php:

<?php

// src/Fuz/ToolsBundle/Twig/TokenParser/WrapperHeaderTokenParser.php

namespace Fuz\ToolsBundle\Twig\TokenParser;

use Fuz\ToolsBundle\Twig\Node\WrapperHeaderNode;

class WrapperHeaderTokenParser extends \Twig_TokenParser
{
    static public $wrappers = array ();

    public function parse(\Twig_Token $token)
    {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();

        $name = $stream->expect(\Twig_Token::NAME_TYPE)->getValue();
        if (in_array($name, self::$wrappers))
        {
            throw new \Twig_Error_Syntax("The wrapper '$name''s header has already been defined.", $lineno);
        }
        self::$wrappers[] = $name;

        $this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);
        $body = $this->parser->subparse(array($this, 'decideWrapperHeaderEnd'), true);
        $this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);
        return new WrapperHeaderNode($name, $body, $token->getLine(), $this->getTag());
    }

    public function decideWrapperHeaderEnd(\Twig_Token $token)
    {
        return $token->test('endwrapperheader');
    }

    public function getTag()
    {
        return 'wrapperheader';
    }

}

wrapperfooter のトークン パーサー: Twig\TokenParser\WrapperFooterTokenParser.php:

<?php

// src/Fuz/ToolsBundle/Twig/TokenParser/WrapperFooterTokenParser.php

namespace Fuz\ToolsBundle\Twig\TokenParser;

use Fuz\ToolsBundle\Twig\Node\WrapperFooterNode;

class WrapperFooterTokenParser extends \Twig_TokenParser
{

    static public $wrappers = array ();

    public function parse(\Twig_Token $token)
    {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();

        $name = $stream->expect(\Twig_Token::NAME_TYPE)->getValue();
        if (in_array($name, self::$wrappers))
        {
            throw new \Twig_Error_Syntax("The wrapper '$name''s footer has already been defined.", $lineno);
        }
        self::$wrappers[] = $name;

        $this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);
        $body = $this->parser->subparse(array($this, 'decideWrapperFooterEnd'), true);
        $this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);
        return new WrapperFooterNode($name, $body, $token->getLine(), $this->getTag());
    }

    public function decideWrapperFooterEnd(\Twig_Token $token)
    {
        return $token->test('endwrapperfooter');
    }

    public function getTag()
    {
        return 'wrapperfooter';
    }

}

トークン パーサーは必要な情報をすべて取得します。これらの情報を PHP にコンパイルする必要があります。この PHP コードは、Twig_Template 実装内の twig エンジンによって生成されます (生成されたクラスはキャッシュ ディレクトリにあります)。メソッドでコードを生成し、インクルードされたファイルのコンテキストは使用できません (コンテキスト配列が参照によって与えられないため)。このように、グローバル コンテキストがなければ、インクルード ファイルの内容にアクセスすることはできません。そのため、ここでは静的属性を使用しています... まったく良くありませんが、それらを回避する方法がわかりません (アイデアがあれば教えてください! :))。

ラッパータグのコンパイラ: Twig\Nodes\WrapperNode.php

<?php

// src/Fuz/ToolsBundle/Twig/Node/WrapperNode.php

namespace Fuz\ToolsBundle\Twig\Node;

class WrapperNode extends \Twig_Node
{

    public function __construct($name, $body, $lineno = 0, $tag = null)
    {
        parent::__construct(array ('body' => $body), array ('name' => $name), $lineno, $tag);
    }

    public function compile(\Twig_Compiler $compiler)
    {
        $compiler
           ->addDebugInfo($this)
           ->write('if (isset(\\')
           ->raw(__NAMESPACE__)
           ->raw('\WrapperHeaderNode::$headers[')
           ->repr($this->getAttribute('name'))
           ->raw('])) {')
           ->raw("\n")
           ->indent()
           ->write('echo \\')
           ->raw(__NAMESPACE__)
           ->raw('\WrapperHeaderNode::$headers[')
           ->repr($this->getAttribute('name'))
           ->raw('];')
           ->raw("\n")
           ->outdent()
           ->write('}')
           ->raw("\n");

        $compiler
           ->addDebugInfo($this)
           ->subcompile($this->getNode('body'));

        $compiler
           ->addDebugInfo($this)
           ->write('if (isset(\\')
           ->raw(__NAMESPACE__)
           ->raw('\WrapperFooterNode::$footers[')
           ->repr($this->getAttribute('name'))
           ->raw('])) {')
           ->raw("\n")
           ->indent()
           ->write('echo \\')
           ->raw(__NAMESPACE__)
           ->raw('\WrapperFooterNode::$footers[')
           ->repr($this->getAttribute('name'))
           ->raw('];')
           ->raw("\n")
           ->outdent()
           ->write('}')
           ->raw("\n");
    }

}

wrapperheader タグのコンパイラ: Twig\Nodes\WrapperHeaderNode.php

<?php

// src/Fuz/ToolsBundle/Twig/Node/WrapperHeaderNode.php

namespace Fuz\ToolsBundle\Twig\Node;

/**
 * @author alain tiemblo
 */
class WrapperHeaderNode extends \Twig_Node
{

    static public $headers = array();

    public function __construct($name, $body, $lineno = 0, $tag = null)
    {
        parent::__construct(array ('body' => $body), array ('name' => $name), $lineno, $tag);
    }

    public function compile(\Twig_Compiler $compiler)
    {
        $compiler
           ->write("ob_start();")
           ->raw("\n")
           ->subcompile($this->getNode('body'))
           ->write(__CLASS__)
           ->raw('::$headers[')
           ->repr($this->getAttribute('name'))
           ->raw('] = ob_get_clean();')
           ->raw("\n");
    }

}

wrapperfooter タグのコンパイラ: Twig\Nodes\WrapperFooterNode.php

<?php

// src/Fuz/ToolsBundle/Twig/Node/WrapperFooterNode.php

namespace Fuz\ToolsBundle\Twig\Node;

class WrapperFooterNode extends \Twig_Node
{

    static public $footers = array();

    public function __construct($name, $body, $lineno = 0, $tag = null)
    {
        parent::__construct(array ('body' => $body), array ('name' => $name), $lineno, $tag);
    }

    public function compile(\Twig_Compiler $compiler)
    {
        $compiler
           ->write("ob_start();")
           ->raw("\n")
           ->subcompile($this->getNode('body'))
           ->write(__CLASS__)
           ->raw('::$footers[')
           ->repr($this->getAttribute('name'))
           ->raw('] = ob_get_clean();')
           ->raw("\n");
    }

}

これで実装はOKです。試してみよう!

wrappers.html.twig という名前のビューを作成します。

{# src/Fuz/LayoutBundle/Resources/views/Default/wrappers.html.twig #}

{% wrapperheader demo %}
HEAD
{% endwrapperheader %}

{% wrapperfooter demo %}
FOOT
{% endwrapperfooter %}

what you want.html.twig という名前のビューを作成します。

{# src/Fuz/HomeBundle/Resources/views/Default/index.html.twig #}

{% include 'FuzLayoutBundle:Default:wrappers.html.twig' %}

{% wrapper demo %}
O YEAH
{% endwrapper %}

これが表示されます:

HEAD O YEAH FOOT

于 2013-04-14T15:22:20.610 に答える
5

Twig 変数とマクロを使用したかなり単純な方法があります。

<div class="box indent">
  <div class="padding">
    my code here
  </div>
</div>

マクロを作成します。

{% macro box(content) %}
    <div class="box indent">
        <div class="padding">
            {{ content }}
        </div>
    </div>
{% endmacro %}

そして、次のように呼び出します。

{% set content %}
    my code here
{% endset %}

{{ _self.box(content) }}

特にエレガントではありませんが、コードの山は少なくなります!

于 2016-12-08T19:10:52.720 に答える