tutorial formato español php symfony tags twig wrapper

php - formato - symfony twig



¿Cómo ajustar el código HTML sin incluir nuevos archivos en Twig? (3)

El método de bloque

Este método fue propuesto por Sebastiaan Stok en GitHub.

Esta idea usa la función de bloque . Escribe el contenido del bloque dado y se puede llamar varias veces.

Archivo Wrappers:

{# 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 %}

Página de características:

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

La extensión de envoltura con macros

Esta idea fue propuesta por Charles en GitHub.

Primero declaras una macro en un archivo macro.html.twig .

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

Amd entonces, en lugar de llamar a {{ macros.box(''my content'') }} (ver el documento desarrolla una etiqueta {% wrap %} que manejará la macro llamada, con lo que está entre [% wrap %} y {% endwrap %} como parámetro.

Esta extensión fue fácil de desarrollar. Pensé que podría ser difícil acceder a las macros, pero de hecho, se almacenan en el contexto como objetos y las llamadas se pueden compilar fácilmente.

Solo algunos cambios: utilizaremos la siguiente sintaxis:

{# 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 %}

En el siguiente código, no olvides cambiar los espacios de nombres si quieres que funcione.

Primero, agregue la extensión en su 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 }

Dentro de tu paquete, crea un directorio de Twig.

Agregue la extensión, devolverá el nuevo TokenParser (en inglés: declarará la nueva etiqueta).

Twig / Extension / 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''; } }

A continuación, agregue el TokenParser en sí mismo, se cumplirá cuando el analizador encuentre una etiqueta {% wrap %} . Este TokenParser verificará si la etiqueta se llama correctamente (para nuestro ejemplo, tiene 2 parámetros), almacena esos parámetros y obtiene el contenido entre {% wrap %} y {% endwrap%} `.

Twig / 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''; } }

A continuación, necesitamos un compilador (un nodo en el dialecto twig), que generará el código PHP asociado con nuestra etiqueta {% wrap %} .

Esta etiqueta es un alias de {{ macro_object.box(content) }} , así que escribí esa línea en una plantilla y observé el código resultante en el archivo php generado resultante (almacenado en su app/cache/dev/twig directory). Tengo :

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

Entonces mi compilador se convirtió en:

Twig / Node / 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"); } } }

Nota: para saber cómo funciona el subpaso / subcompilación, leo el código fuente de extensión spaceless espacio.

¡Eso es todo! Obtenemos un alias que nos permite usar macros con un cuerpo grande. Intentarlo:

macros.html.twig:

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

algunos 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 %}

Productos:

<div class="box indent"> <div class="padding"> test </div> </div> some macro in the same file

El wrapperheader, wrapperfooter, extensión de envoltura

Este es el método del que te hablo en mi pregunta. Puedes leerlo / implementarlo si quieres entrenarte con los analizadores de tokens, pero funcionalmente, eso es menos agradable que el método anterior.

En un archivo wrapper.html.twig , declaras todas las envolturas:

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

En sus características twig files, usa sus wrappers:

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

La siguiente extensión tiene 3 problemas:

  • No hay forma de almacenar datos (como variables de contexto) en Twig Environnement. Entonces, cuando defines un {% wrapperheader NAME %} , básicamente no tienes una forma clara de verificar si ya está definido un encabezado para NAME (en esta extensión, utilizo propiedades estáticas).

  • Cuando se include un archivo twig, se analiza en tiempo de ejecución, no de forma inmediata (me refiero a que la plantilla twig incluida se analiza mientras se ejecuta el archivo generado , y no cuando se analiza la etiqueta include ). Por lo tanto, no es posible saber si existe un contenedor en un archivo incluido previamente al analizar la etiqueta {% wrapper NAME %} . Si su envoltorio no existe, esta extensión solo muestra lo que está entre {% wrapper %} y {% endwrapper %} sin previo aviso.

  • La idea de esta extensión es: cuando el analizador cumple una etiqueta wrapperheader y wrapperfooter , el compilador almacena el contenido de la etiqueta en algún lugar para un uso posterior con la etiqueta wrapper . Pero el contexto de ramita se pasa a {% include %} como una copia, no por referencia. De modo que no es posible almacenar la información {% wrapperheader %} y {% wrapperfooter %} dentro de ese contexto, para un uso en el nivel superior (en archivos que incluyen archivos). Necesitaba usar un contexto global también.

Aquí está el código, tenga cuidado de cambiar sus espacios de nombres.

Primero, necesitamos crear una extensión que agregue nuevos analizadores de tokens a Twig.

Dentro de services.yml de un paquete, agregue las siguientes líneas para activar la extensión:

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 }

Dentro de tu paquete, crea un directorio de Twig.

Cree el siguiente archivo 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''; } }

Ahora necesitamos agregar los analizadores de tokens: nuestra sintaxis es {% wrapper NAME %} ... {% endwrapper %} y lo mismo con wrapperheader y wrapperfooter . Entonces esos analizadores de tokens se usan para declarar las etiquetas, para recuperar el NAME del contenedor y para recuperar el cuerpo (lo que hay entre wrapper y endwrapper`).

El analizador de tokens para el contenedor: 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''; } }

El analizador de tokens para 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''; } }

El analizador de tokens para 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''; } }

Los analizadores de tokens recuperan toda la información requerida, ahora necesitamos compilar esa información en PHP. Este código PHP será generado por el motor twig dentro de una implementación Twig_Template (puede encontrar clases generadas en su directorio de caché). Genera código en un método y el contexto de los archivos incluidos no está disponible (porque la matriz de contexto no se proporciona por referencia). De esta forma, no es posible acceder a lo que está dentro del archivo incluido sin un contexto global. Es por eso que aquí, uso atributos estáticos ... Eso no es nada agradable, pero no sé cómo evitarlos (si tiene ideas, hágamelo saber :)).

Compilador de la etiqueta contenedora: 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"); } }

Compilador para la etiqueta 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"); } }

Compilador de la etiqueta 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"); } }

La implementación está bien ahora. ¡Vamos a intentarlo!

Crea una vista llamada wrappers.html.twig:

{# src/Fuz/LayoutBundle/Resources/views/Default/wrappers.html.twig #} {% wrapperheader demo %} HEAD {% endwrapperheader %} {% wrapperfooter demo %} FOOT {% endwrapperfooter %}

Crea una vista llamada lo que quieres.html.twig:

{# src/Fuz/HomeBundle/Resources/views/Default/index.html.twig #} {% include ''FuzLayoutBundle:Default:wrappers.html.twig'' %} {% wrapper demo %} O YEAH {% endwrapper %}

Esto aparece:

HEAD O YEAH FOOT

Tengo divs con CSS que representan recuadros, envuelven el código html.

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

Creé un "paquete de diseño" en el que cada contenedor de HTML (como cuadros, pestañas, cuadrículas, etc.) se almacena en archivos twig separados. De esta forma, las vistas en otros paquetes pueden implementarse con otros diseños.

Pero me canso de incluye. Cada pequeño contenedor html requiere una inclusión, y me pregunto si existe una forma más simple de ajustar el código HTML.

Veamos un ejemplo con una caja simple. En realidad, creé varios archivos:

Un archivo box.html.twig que contiene el cuadro e incluye el contenido:

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

Varios archivos .html.twig con contenido de cuadro, que contienen el contenido de mis cuadros.

Y finalmente, creo un cuadro en una vista haciendo:

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

¿Hay alguna manera de crear contenedores como, por ejemplo:

a) Declaro una vez un nuevo contenedor:

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

b) Y luego en mis páginas, uso:

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

Creo que tendré que agregar nuevas extensiones de etiquetas en Twig, pero primero quiero saber si algo similar es posible de forma nativa.


¿Has probado varitas variables?

http://twig.sensiolabs.org/doc/templates.html#setting-variables

No lo he intentado, pero puedes hacer un archivo de variables, incluirlo en la parte superior de tus plantillas e imprimir los vars que necesites luego ...

Ejemplo: boxes.html.twig

{% set coolBox = ''<div class="box indent"><div class="padding">'' %} {% set endDiv = ''</div></div>'' %} {% set notcoolBox = ''<div class="not Cool Box"><div class="padding">'' %} ... Moar cool boxes

template.html.twig

{% include boxes.html.twig %} My template {{ coolBox }} This is inside a cool box {{ endDiv }}


Hay un método bastante sencillo con variables y macros de Twig.

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

Crea una macro:

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

Y llámalo así:

{% set content %} my code here {% endset %} {{ _self.box(content) }}

No innovador, pero una solución ligeramente diferente.