SlideShare a Scribd company logo
1 of 175
Download to read offline
Twig, los mejores trucos y
técnicas avanzadas
Javier Eguíluz

Universitat Jaume I · Castellón · 15-16 junio 2012 · desymfony.com
¡muchas gracias a nuestros patrocinadores!
 PATROCINADOR
   PLATINO


PATROCINADORES
         ORO

PATROCINADORES
      PLATA

PATROCINADORES
    BRONCE
Agenda
 • Buenas prácticas
 • Técnicas avanzadas
 • Trucos y   cosas nuevas
Código
El código fuente de los ejemplos
más avanzados de esta ponencia
está disponible en:

https://github.com/javiereguiluz/
desymfony!twig
PERSONALIZACIÓN
          EXTREMA DE
         FORMULARIOS

1.*     BÁSICO INTERMEDIO AVANZADO
<div>
  {{ form_label(form.nombre) }}
  {{ form_errors(form.nombre) }}
  {{ form_widget(form.nombre) }}
</div>
{% extends '::base.html.twig' %}
{% form_theme form _self %}

{% block integer_widget %}
  <div class="integer_widget">
     {% set type = type|default('number') %}
     {{ block('field_widget') }}
  </div>
{% endblock %}

{% block content %}
  {# render the form #}
  {{ form_row(form.age) }}
{% endblock %}
{% extends '::base.html.twig' %}
{% form_theme form _self %}

{% block integer_widget %}
  <div class="integer_widget">
     {% set type = type|default('number') %}
     {{ block('field_widget') }}
  </div>
{% endblock %}

{% block content %}
  {# render the form #}
  {{ form_row(form.age) }}
{% endblock %}
{% extends '::base.html.twig' %}
{% form_theme form _self %}

{% block integer_widget %}
  <div class="integer_widget">
     {% set type = type|default('number') %}
     {{ block('field_widget') }}
  </div>
{% endblock %}

{% block content %}
  {# render the form #}
  {{ form_row(form.age) }}
{% endblock %}
{% form_theme form _self %}

{% block _formulario_nombre_widget %}
  <div class="especial"><strong>
     {{ block('field_widget') }}
  </strong></div>
{% endblock %}

{{ form_widget(form.nombre) }}
{% form_theme form _self %}

{% block _formulario_nombre_widget %}
  <div class="especial"><strong>
     {{ block('field_widget') }}
  </strong></div>
{% endblock %}

{{ form_widget(form.nombre) }}
class UsuarioType extends AbstractType
{
   public function buildForm(FormBuilder $builder, array $options)
   {
     $builder
        ->add('nombre')
        ->add('apellidos')
        ->add('email')
        ->...
        ->add('ciudad')
     ;
   }

    public function getName()
    {
      return 'cupon_backendbundle_usuariotype';
    }
}
class UsuarioType extends AbstractType
{
   public function buildForm(FormBuilder $builder, array $options)
   {
     $builder
        ->add('nombre')
        ->add('apellidos')
        ->add('email')
        ->...
        ->add('ciudad')
     ;
   }

    public function getName()
    {
      return 'cupon_backendbundle_usuariotype';
    }
}
<strong>Label</strong>: Valor
<strong>Label</strong>: Valor

<strong>
{{ form.nombreCampo.vars.label }}
</strong>:
{{ form. nombreCampo.vars.value }}
<strong>Label</strong>: Valor

<strong>
{{ form.nombreCampo.vars.label }}
</strong>:
{{ form. nombreCampo.vars.value }}
CONSTANTES



1.*   BÁSICO INTERMEDIO AVANZADO
{{ constant() }}
{{ constant('SymfonyComponent
HttpKernelKernel::VERSION') }}
{{ constant('SymfonyComponent
HttpKernelKernel::VERSION') }}
namespace SymfonyComponentHttpKernel;

abstract class Kernel implements KernelInterface
{
  // ...

    const VERSION = '2.0.15';
}
namespace SymfonyComponentHttpKernel;

abstract class Kernel implements KernelInterface
{
  // ...

    const VERSION        = '2.1.0-DEV';
    const VERSION_ID     = '20100';
    const MAJOR_VERSION = '2';
    const MINOR_VERSION = '1';
    const RELEASE_VERSION = '0';
    const EXTRA_VERSION = 'DEV';
}
REDEFINE LOS
         FILTROS POR
             DEFECTO

1.*   BÁSICO INTERMEDIO AVANZADO
{{ array|sort }}
asort()     array_multisort()
arsort()    natcasesort()
krsort()    natsort()
ksort()     rsort()
rsort()     shuffle()
shuffle()   uasort()
sort()      uksort()
usort()
¿Dónde se definen
los filtros, etiquetas
  y tags de Twig?
lib/twig/Extesion/Core.php
class Twig_Extension_Core extends Twig_Extension {
   public function getTokenParsers() {
     return array(
        new Twig_TokenParser_For(),
        new Twig_TokenParser_If(),
        new Twig_TokenParser_Extends(),
        new Twig_TokenParser_Include(),
        new Twig_TokenParser_Block(),
        // ...
     );
   }

  public function getFilters() {
    $filters = array(
       'format'     => new Twig_Filter_Function('sprintf'),
       'replace' => new Twig_Filter_Function('strtr'),
       'abs'      => new Twig_Filter_Function('abs'),
{{ array|sort }}
/**
 * Sorts an array.
 *
 * @param array $array An array
 */
function twig_sort_filter($array)
{
   asort($array);

    return $array;
}
natcasesort()
a0, A1, a2, a10, ...
class MiCoreExtension
      extends Twig_Extension_Core {
   // ...
}
class MiCoreExtension extends Twig_Extension_Core {
    public function getFilters() {
      // ...
    }
}
class MiCoreExtension extends Twig_Extension_Core {
    public function getFilters() {
      return array_merge(
         parent::getFilters(),
         array( ... )
      );
    }
}
$twig = new Twig_Environment($loader, array( ... ));
$twig->addExtension(new MiCoreExtension());
class MiCoreExtension extends Twig_Extension_Core
{
   public function getFilters()
   {
     return array_merge(parent::getFilters(), array(
         'sort' => new Twig_Filter_Method($this, 'sortFilter')
     ));
   }

    public function sortFilter($array)
    {
      natcasesort($array);
      return $array;
    }
}
{{ array|sort }}
function twig_sort_filter($array)
{
  natcasesort($array);
  return $array;
}
TODO SON
         EXPRESIONES


1.5   BÁSICO INTERMEDIO AVANZADO
{% include 'seccion_' ~ oferta.categoria ~ '.twig' %}


{% for i in (1+3)//2..2**(3-1) %}

{% endfor %}
{% set ofertas = {
  ('oferta' ~ oferta.id):           '...',
  (3 ~ '_' ~ oferta.nombre|length): '...',
  (2**2+1):                         '...'
} %}
{% set ofertas = {
  ('oferta' ~ oferta.id):           '...',
  (3 ~ '_' ~ oferta.nombre|length): '...',
  (2**2+1):                         '...'
} %}
{% set ofertas = {
  oferta7040: '...',
  3_57: '...',
  5: '...'
} %}
EMBED




1.8   BÁSICO INTERMEDIO AVANZADO
{% include %}   {% extends %}



       {% embed %}
{% include '...' %}
{% extends '...' %}
{% embed '...' %}
{% embed "lateral.twig" %}
  {% block principal %}
    ...
  {% endblock %}
{% endembed %}
{% embed "lateral.twig" %}
  {% block secundario %}
    ...
  {% endblock %}
{% endembed %}
{% include 'plantilla.twig' %}

{% embed 'plantilla.twig' %}
{% endembed %}
ESCAPING
          AUTOMÁTICO


1.8   BÁSICO INTERMEDIO AVANZADO
{% filter escape('js') %}
  <script type="text/javascript">
     var texto = "<p>Lorem ipsum dolor sit amet</p>";
     alert(texto);
  </script>
{% endfilter %}


{% filter escape('html') %}
  <script type="text/javascript">
     var texto = "<p>Lorem ipsum dolor sit amet</p>";
     alert(texto);
  </script>
{% endfilter %}
x3cscript typex3dx22textx2fjavascriptx22x3ex0a
var texto x3d x22x3cpx3eLorem ipsum dolor sit amet
x3cx2fpx3ex22x3bx0a alertx28textox29x3bx0a
x3cx2fscriptx3ex0a



&lt;script type=&quot;text/javascript&quot;&gt;
   var texto = &quot;&lt;p&gt;Lorem ipsum dolor sit
                amet&lt;/p&gt;&quot;;
   alert(texto);
&lt;/script&gt;
{{ variable|e }}
{{ variable|e('html') }}
{{ variable|escape('js') }}
{{ variable|e('js') }}

{% autoescape %} ........... {% endautoescape %}
{% autoescape 'html' %} ... {% endautoescape %}
{% autoescape 'js' %} ....... {% endautoescape %}
{% autoescape false %} .... {% endautoescape %}
$twig = new Twig_Environment($loader, array(
    'autoescape' => function($nombre_plantilla) {
      return decide_escape($nombre_plantilla);
    }
));
$twig = new Twig_Environment($loader, array(
    'autoescape' => function($nombre_plantilla) {
      return decide_escape($nombre_plantilla);
    }
));
function decide_escape($plantilla) {
  $extension = substr($plantilla, strrpos($plantilla, '.') + 1);

    switch ($extension) {
      'js':
         return 'js';
      default:
         return 'html';
    }
}
RANDOM



1.6   BÁSICO INTERMEDIO AVANZADO
{{ random() }}
{{ random() }}
{{ random(10) }}
{{ random() }}
{{ random(10) }}
{{ random("abcde") }}
{{ random() }}
{{ random(10) }}
{{ random("abcde") }}
{{ random(['a', 'b', 'c']) }}
FUNCIONES
             DINÁMICAS


1.5   BÁSICO INTERMEDIO AVANZADO
the_ID()
the_title()
the_time()
the_content()
the_category()
the_shortlink()
the_ID()
the_title()
the_time()
the_content()
the_category()
the_shortlink()
the_ID()
the_title()
the_time()
the_content()
the_category()
the_shortlink()

the_*()
$twig->addFunction(
     'the_*',
     new Twig_Function_Function('wordpress')
);

function wordpress($funcion, $opciones)
{
  // ...
}
$twig->addFunction(
     'the_*',
     new Twig_Function_Function('wordpress')
);

function wordpress($funcion, $opciones)
{
  // ...
}
{{ the_ID() }}
{{ the_ID() }}
function wordpress('ID', array()) { ... }
{{ the_ID() }}
function wordpress('ID', array()) { ... }


{{ the_content() }}
{{ the_ID() }}
function wordpress('ID', array()) { ... }


{{ the_content() }}
function wordpress('content', array())
{ ... }
{{ the_title('<h3>', '</h3>') }}
function wordpress('title', array(
   '<h3>', '</h3>'
)) { ... }
*_*_link()
next_image_link()
next_post_link()
next_posts_link()
previous_image_link()
previous_post_link()
previous_posts_link()
php_*()
FILTROS
             DINÁMICOS


1.5   BÁSICO INTERMEDIO AVANZADO
{{ 'now'|fecha_corta }}
{{ 'now'|fecha_larga }}

fecha_*()
$twig->addFilter(
     'fecha_*',
     new Twig_Filter_Function('fecha')
);

function fecha($funcion, $opciones)
{
  // ...
}
VARIABLES
              GLOBALES


1.*   BÁSICO INTERMEDIO AVANZADO
{{ app.security }}
{{ app.user }}

{{ app.request }}
{{ app.session }}

{{ app.environment }}
{{ app.debug }}
{{ _charset }}
{{ _context }}
{{ _self }}
{{ _charset }}


   UTF-8
{{ _context }}
{% for i in _context|keys %}
  {{ i }}
{% endfor %}
{{ _context }}
{% for i in _context|keys %}
  {{ i }}              app
                      assetic
{% endfor %}          _parent
                      oferta
                      ciudad_por_defecto
                      ciudadSeleccionada
                      expirada
                      ...
{{ _context }}
{% for i in _context|keys %}
  {{ i }}              app
                      assetic
{% endfor %}          _parent
                      oferta
                      ciudad_por_defecto
                      ciudadSeleccionada
                      expirada
                      ...
{{ _context }}


{{ variable|mi_filtro(_context) }}
{{ _context }}


{{ variable|mi_filtro(_context) }}

                 SÓLO SI NO HAY
                 OTRO REMEDIO
{{ _self }}


Twig_Template
{{ _self }}

{{ _self.getTemplateName }}
OfertaBundle:Default:includes/
oferta.html.twig
{{ _self }}

{{ _self.blocks|keys|join(", ") }}
title, stylesheets, rss, javascripts,
id, body
{{ _self }}

{{ _self.blocks|keys|join(", ") }}
title, stylesheets, rss, javascripts,
id, body
                    SÓLO SI NO HAY
                    OTRO REMEDIO
EL SANDBOX



1.*   BÁSICO INTERMEDIO AVANZADO
DEMO
DATE



1.6   BÁSICO INTERMEDIO AVANZADO
{{ 'now'|date("d/m/Y H:i:s") }}
{{ 'now'|date("d/m/Y H:i:s",
              "America/Argentina/Buenos_Aires") }}
{{ 'now'|date("d/m/Y H:i:s", "America/Havana") }}
{{ 'now'|date("d/m/Y H:i:s", "America/Caracas") }}
{{ 'now'|date("d/m/Y H:i:s", "America/Lima") }}
España:   11/06/2012 10:45:22
Argentina: 11/06/2012 05:45:22
Cuba:     11/06/2012 04:45:22
Venezuela: 11/06/2012 04:15:22
Perú:     11/06/2012 03:45:22
$twig = new Twig_Environment($loader);

$twig->getExtension('core')
     ->setDateFormat('d-m-Y H:i:s', '%d días');

$twig->getExtension('core')
     ->setTimezone('America/Montevideo');
date()
{{ date() }}
{{ date() }}
{{ date()|date() }}
{{ date() }}
{{ date()|date() }}
{{ date()|date("d/m/Y") }}
Tu invitación caduca el
{{ date('+2days')|date }}
Tu invitación caduca el
{{ date('+2days')|date }}


{% if date(fechaNacimiento) < date('-18years') %}
  ¡Eres menor de edad!
{% endif %}
La última sorpresa de
#deSymfony se desvelará el
{{ date('next Monday')|date() }}
OPERADORES
             PROPIOS


1.*   BÁSICO INTERMEDIO AVANZADO
$loader = new Twig_Loader_Filesystem(...);
$twig = new Twig_Environment($loader, array(...));

$twig->addExtension(new OperatorsExtension());
Operador "quédate con el mayor"

  {{ 5 >> 2 }}       5
  {{ 4 >> 20 }}      20
class OperatorsExtension extends Twig_Extension
{
   public function getName() { return 'OperatorsExtension'; }

    public function getOperators()
    {
      return array(
            array(),
            array('>>' => array(
                    'precedence' => 20,
                          'class' => 'DesymfonyOperatorsMaxOperator',
                  'associativity' => Twig_ExpressionParser::OPERATOR_LEFT
              )
        ));
    }
}
class OperatorsExtension extends Twig_Extension
{
   public function getName() { return 'OperatorsExtension'; }

    public function getOperators()
    {
      return array(
            array(),
            array('>>' => array(
                    'precedence' => 20,
                          'class' => 'DesymfonyOperatorsMaxOperator',
                  'associativity' => Twig_ExpressionParser::OPERATOR_LEFT
              )
        ));
    }
}
class OperatorsExtension extends Twig_Extension
{
   public function getName() { return 'OperatorsExtension'; }

    public function getOperators()
    {
      return array(
            array(),
            array('>>' => array(
                    'precedence' => 20,
                          'class' => 'DesymfonyOperatorsMaxOperator',
                  'associativity' => Twig_ExpressionParser::OPERATOR_LEFT
              )
        ));
    }
}
2 +3**2 / 4 .. 4-5//2
  Operador   Precedence
    !=       20
    +        30
    **       200
{{ a >> b }}   max($a, $b);

   TWIG            PHP
class MaxOperator extends Twig_Node_Expression_Binary
{
   public function compile(Twig_Compiler $compiler)
   {
     $compiler
        ->raw('max(')
        ->subcompile($this->getNode('left'))
        ->raw(', ')
        ->subcompile($this->getNode('right'))
        ->raw(')')
     ;
   }
class MaxOperator extends Twig_Node_Expression_Binary
{
   public function compile(Twig_Compiler $compiler)
   {
     $compiler
        ->raw('max(')
        ->subcompile($this->getNode('left'))
        ->raw(', ')
        ->subcompile($this->getNode('right'))
        ->raw(')')
     ;
   }
                                 max($a, $b);
// line 23
echo twig_escape_filter($this->env,
     max(5, 2), "html", null, true
);
$compiler
  ->raw()       (literal)
  ->write()     (indentado)
  ->string()    (entrecomillado)
  ->indent()    (indentar)
  ->outdent()   (desindentar)
;
Operador "cambia claves por valores"

 PHP    array_flip(range('a', 'z'))

TWIG    {% for i in <->('a'..'z') %}
          {{ i }},
        {% endfor %}
Operador "cambia claves por valores"

 PHP    array_flip(range('a', 'z'))

TWIG    {% for i in <->('a'..'z') %}
          {{ i }},
        {% endfor %}
class OperatorsExtension extends Twig_Extension
{
   public function getName() { return 'OperatorsExtension'; }

    public function getOperators()
    {
      return array(
            array('<->' => array(
                 'precedence' => 50,
                       'class' => 'DesymfonyOperatorsFlipOperator',
              ),
              array()
        ));
    }
}
class OperatorsExtension extends Twig_Extension
{
   public function getName() { return 'OperatorsExtension'; }

    public function getOperators()
    {
      return array(
            array('<->' => array(
                 'precedence' => 50,
                       'class' => 'DesymfonyOperatorsFlipOperator',
              ),
              array()
        ));
    }
}
class OperatorsExtension extends Twig_Extension
{
   public function getName() { return 'OperatorsExtension'; }

    public function getOperators()
    {
      return array(
            array('<->' => array(
                 'precedence' => 50,
                       'class' => 'DesymfonyOperatorsFlipOperator',
              ),
              array()
        ));
    }
}
namespace DesymfonyOperators;

class FlipOperator extends Twig_Node_Expression_Unary
{

  public function compile(Twig_Compiler $compiler)
  {
    $compiler
       ->raw("array_flip(")
       ->subcompile($this->getNode('node'))
       ->raw(")")
    ;
  }
namespace DesymfonyOperators;

class FlipOperator extends Twig_Node_Expression_Unary
{

  public function compile(Twig_Compiler $compiler)
  {
    $compiler
       ->raw("array_flip(")
       ->subcompile($this->getNode('node'))
       ->raw(")")
    ;
  }
                     array_flip($coleccion);
// line 17
$context['_parent'] = (array) $context;
$context['_seq'] = twig_ensure_traversable(array_flip(range("a", "z")));
foreach ($context['_seq'] as $context["_key"] => $context["i"]) {
  // line 18
  echo "        ";
  if (isset($context["i"])) { $_i_ = $context["i"]; } else { $_i_ = null; }
  echo twig_escape_filter($this->env, $_i_, "html", null, true);
  echo ",";
}
// line 17
$context['_parent'] = (array) $context;
$context['_seq'] = twig_ensure_traversable(array_flip(range("a", "z")));
foreach ($context['_seq'] as $context["_key"] => $context["i"]) {
  // line 18
  echo "        ";
  if (isset($context["i"])) { $_i_ = $context["i"]; } else { $_i_ = null; }
  echo twig_escape_filter($this->env, $_i_, "html", null, true);
  echo ",";
}
SUPER CACHÉ



1.*   BÁSICO INTERMEDIO AVANZADO
# mkfs -q /dev/ram1 65536
# mkdir -p /twigcache
# mount /dev/ram1 /twigcache




Inspirado por: http://www.cyberciti.biz/faq/howto!create!linux!ram!disk!filesystem/
$twig = new Twig_Environment(
   $loader,
   array('cache' => '/twigcache')
);

# app/config/config.yml
twig:
  cache: '/twigcache'
ETIQUETAS
                PROPIAS


1.*   BÁSICO INTERMEDIO AVANZADO
etiquetas
operadores     #1
                          tests
   #2                    #3
{% source 'simple.twig' %}

{% source '../../../composer.json' %}
1. Clase Token Parser
   TWIG         TWIG

2. Clase Node
   TWIG         PHP
$loader = new Twig_Loader_Filesystem(...);
$twig = new Twig_Environment($loader, array(...));

$twig->addTokenParser(new SourceTokenParser());
{% source '...' %}

class SourceTokenParser extends Twig_TokenParser
{
   public function getTag()
   {
     return 'source';
   }
}
namespace DesymfonyTags;
use DesymfonyTagsSourceNode;

class SourceTokenParser extends Twig_TokenParser
{
   public function parse(Twig_Token $token)
   {
     $lineno = $token->getLine();
     $value = $this->parser->getExpressionParser()->parseExpression();

        $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);

        return new SourceNode($value, $lineno, $this->getTag());
    }
}
class SourceNode extends Twig_Node
{
    public function __construct(Twig_Node_Expression $value, $lineno, $tag = null)
    {
      parent::__construct(array('file' => $value), array(), $lineno, $tag);
    }

    public function compile(Twig_Compiler $compiler)
    {
            $compiler
              -> // ...
              ->write('echo file_get_contents(')
              ->subcompile($this->getNode('file'))
              ->raw(');');
        ;
    }
}
// line 3
echo file_get_contents("simple.twig");
// line 4
echo "n";

// line 5
echo file_get_contents("../../../composer.json");
// line 6
echo "n";
INTERPOLACIÓN



1.5   BÁSICO INTERMEDIO AVANZADO
La oferta cuesta 25.78 euros
(30.42 con IVA) y es válida
hasta el 10/06/2012
La oferta cuesta 25.78 euros
(30.42 con IVA) y es válida
hasta el 10/06/2012
La oferta cuesta 25.78 euros (30.42 con IVA) y es
válida hasta el 10/06/2012


                           ~
{{ 'La oferta cuesta ' ~ oferta.precio ~ ' euros (' ~
oferta.precio*1.18 ~ ' con IVA) y es válida hasta el ' ~
oferta.fechaExpiracion|date() }}
La oferta cuesta 25.78 euros (30.42 con IVA) y es
válida hasta el 10/06/2012


                   format()
{{ 'La oferta cuesta %.2f euros (%.2f con IVA) y es
válida hasta el %s'|format(oferta.precio,
oferta.precio*1.18, oferta.fechaExpiracion|date()) }}
La oferta cuesta 25.78 euros (30.42 con IVA) y es
válida hasta el 10/06/2012


                   replace()
{{ 'La oferta cuesta :precio euros (:total con IVA) y es
válida hasta el :fecha'|replace({ ':precio':
oferta.precio, ':total': oferta.precio*1.18, ':fecha':
oferta.fechaExpiracion|date() }) }}
La oferta cuesta 25.78 euros (30.42 con IVA) y es
válida hasta el 10/06/2012



{{ "La oferta cuesta #{oferta.precio}
euros (#{oferta.precio*1.18} con IVA)
y es válida hasta el
#{oferta.fechaExpiracion|date()}" }}
{{ "... #{ expresión } ..." }}
{{ "... #{ expresión } ..." }}
NUEVOS FILTROS Y
            ETIQUETAS


1.5     BÁSICO INTERMEDIO AVANZADO
{% flush %}
{% do %}
{{ 1 + 1 }}   2
{% do 1 + 1 %}    (nada)
{% set lista = ['a', 'b', 'c', 'd'] %}
{{ lista|shift }}


{% do lista|shift %}

Fuente: https://github.com/fabpot/Twig/issues/446
[:]
{% set lista = ['a', 'b', 'c', 'd'] %}

          {{ lista[1:] }}
{% set lista = ['a', 'b', 'c', 'd'] %}

         {{ lista[-1:] }}
{% set lista = ['a', 'b', 'c', 'd'] %}

         {{ lista[2:2] }}
{{ '@username'[1:] }}

{{ 'Lorem ipsum...'[-4:10] }}
operadores bitwise
{{ a b-and b }}   $a ^ $b
{{ a b-xor b }}   $a << $b
{{ a b-or b }}    $a >> $b
    TWIG            PHP
TWIG_TEMPLATE



1.*   BÁSICO INTERMEDIO AVANZADO
<html>
  <head> ... </head>

  <body>
    ...

    <span data-host="Darwin mbp.local 10.8.0 Darwin Kernel Version
10.8.0: Tue Jun 7 16:33:36 PDT 2011; root:xnu-1504.15.3~1/
RELEASE_I386 i386" data-elapsed="0.97804594039917 sec."
data-timestamp="1339609672.9781"></span>

  </body>
</html>
app/cache/dev/twig/
app/cache/dev/twig/
app/cache/dev/twig/09/fc/2d8a188dda8245d295e6324582f2.php

<?php

/* ::frontend.html.twig */
class __TwigTemplate_09fc2d8a188dda8245d295e6324582f2
extends Twig_Template
{
   public function __construct(Twig_Environment $env)
   {
     parent::__construct($env);

   $this->parent = $this->env->loadTemplate("::base.html.twig");

   $this->blocks = array(
     'stylesheets' => array($this, 'block_stylesheets'),
     'javascripts' => array($this, 'block_javascripts'),
     'body' => array($this, 'block_body'),
     'article' => array($this, 'block_article'),
class __TwigTemplate_09f...2f2
      extends Twig_Template
{
   // ...
}
namespace CuponBackendBundleController;
use SymfonyBundleFrameworkBundleControllerController;

class UsuarioController extends Controller
{
    public function indexAction()
    {
      //...

        return $this->render(
            'BackendBundle:Usuario:index.html.twig',
            array( ... )
         );
    }
}
Symfony/Bundle/FrameworkBundle/Controller/Controller.php


function render($view, $parameters, $response) {

    return $this->container->get('templating')
    ->renderResponse($view, $parameters, $response);

}
lib/Twig/Environment.php


public function render($name, array $context = array())
{
  return $this->loadTemplate($name)
              ->render($context);
}
return $this->render( ... );



render( ... )      Twig_Template
return $this->render( ... );



render( ... )      Twig_Template
<html>
  <head> ... </head>

  <body>
    ...

    <span data-host="Darwin mbp.local 10.8.0 Darwin Kernel Version
10.8.0: Tue Jun 7 16:33:36 PDT 2011; root:xnu-1504.15.3~1/
RELEASE_I386 i386" data-elapsed="0.97804594039917 sec."
data-timestamp="1339609672.9781"></span>

  </body>
</html>
base_template_class

$loader = new Twig_Loader_Filesystem(__DIR__.'/../Resources/views');
$twig = new Twig_Environment($loader, array(
    'base_template_class' => 'DesymfonyTemplateMiTwigTemplate',
));


# app/config/config.yml
twig:
  base_template_class: "DesymfonyTemplateMiTwigTemplate"
namespace DesymfonyTemplate;

abstract class MiTwigTemplate extends Twig_Template
{
    public function render(array $context)
    {
        $traza = sprintf('<span data-host="%s" data-elapsed="%s sec."
                         data-timestamp="%s"></span>',
           php_uname(),
           microtime(true)-$_SERVER['REQUEST_TIME'],
           microtime(true)
        );

        return str_replace('</body>', $traza."n</body>",
           parent::render($context)
        );
    }
}
TWIG LINTER



1.*   BÁSICO INTERMEDIO AVANZADO
$twig = new Twig_Environment($loader, array(..));

try {
   $twig->parse($twig->tokenize($plantilla));
   echo "[OK] La sintaxis de la plantilla es correcta";
} catch (Twig_Error_Syntax $e) {
   echo "[ERROR] La plantilla tiene errores de sintaxis";
}
$ php app/console twig:lint @MyBundle

$ php app/console twig:lint src/Cupon/
OfertaBundle/Resources/views/
index.html.twig
twig:lint
twig:lint

SYMFONY 2.1
twig:lint

 SYMFONY 2.1

DESYMFONY 2013
¡muchas gracias!
Contacto
 • javier.eguiluz@gmail.com
 • github.com/javiereguiluz
 • twitter.com/javiereguiluz
 • linkd.in/javiereguiluz

More Related Content

What's hot

Drupal Cms Prezentace
Drupal Cms PrezentaceDrupal Cms Prezentace
Drupal Cms PrezentaceTomáš Kafka
 
Adi Fatol - Ce e nou in PHP 5.3?
Adi Fatol - Ce e nou in PHP 5.3?Adi Fatol - Ce e nou in PHP 5.3?
Adi Fatol - Ce e nou in PHP 5.3?phpGeekMeet_ro
 
from new class and dependency injection to PSR-11 and Auto-wiring
from new class and dependency injection to PSR-11 and Auto-wiringfrom new class and dependency injection to PSR-11 and Auto-wiring
from new class and dependency injection to PSR-11 and Auto-wiringMilad Arabi
 
Clase 7 el modelo
Clase 7  el modeloClase 7  el modelo
Clase 7 el modelohydras_cs
 
Юнит тестирование в Zend Framework 2.0
Юнит тестирование в Zend Framework 2.0Юнит тестирование в Zend Framework 2.0
Юнит тестирование в Zend Framework 2.0zfconfua
 
JavascriptMVC
JavascriptMVCJavascriptMVC
JavascriptMVC4lb0
 
Introduction à Marionette
Introduction à MarionetteIntroduction à Marionette
Introduction à MarionetteRaphaël Lemaire
 
RxSwift 예제로 감잡기
RxSwift 예제로 감잡기RxSwift 예제로 감잡기
RxSwift 예제로 감잡기Yongha Yoo
 
Javascript and jQuery for Mobile
Javascript and jQuery for MobileJavascript and jQuery for Mobile
Javascript and jQuery for MobileIvano Malavolta
 
Palestra sobre MongoDB com PHP no PHP'n'Rio
Palestra sobre MongoDB com PHP no PHP'n'Rio Palestra sobre MongoDB com PHP no PHP'n'Rio
Palestra sobre MongoDB com PHP no PHP'n'Rio Suissa
 
Адаптация TInyMCE редактора под нужды клиента by Vitaly Nikolaev
Адаптация TInyMCE редактора под нужды клиента by Vitaly NikolaevАдаптация TInyMCE редактора под нужды клиента by Vitaly Nikolaev
Адаптация TInyMCE редактора под нужды клиента by Vitaly NikolaevWordCamp Kyiv
 
Testgetriebene Entwicklung mit JavaScript – Jax 2012
Testgetriebene Entwicklung mit JavaScript – Jax 2012Testgetriebene Entwicklung mit JavaScript – Jax 2012
Testgetriebene Entwicklung mit JavaScript – Jax 2012OPITZ CONSULTING Deutschland
 
2km Workshop: Desenvolvimento ágil com o CakePHP
2km Workshop: Desenvolvimento ágil com o CakePHP2km Workshop: Desenvolvimento ágil com o CakePHP
2km Workshop: Desenvolvimento ágil com o CakePHPCarlos Pires
 

What's hot (20)

jQuery for beginners
jQuery for beginnersjQuery for beginners
jQuery for beginners
 
Drupal Cms Prezentace
Drupal Cms PrezentaceDrupal Cms Prezentace
Drupal Cms Prezentace
 
Testování prakticky
Testování praktickyTestování prakticky
Testování prakticky
 
Adi Fatol - Ce e nou in PHP 5.3?
Adi Fatol - Ce e nou in PHP 5.3?Adi Fatol - Ce e nou in PHP 5.3?
Adi Fatol - Ce e nou in PHP 5.3?
 
from new class and dependency injection to PSR-11 and Auto-wiring
from new class and dependency injection to PSR-11 and Auto-wiringfrom new class and dependency injection to PSR-11 and Auto-wiring
from new class and dependency injection to PSR-11 and Auto-wiring
 
Jquery2
Jquery2Jquery2
Jquery2
 
Clase 7 el modelo
Clase 7  el modeloClase 7  el modelo
Clase 7 el modelo
 
Comparison Principle
Comparison PrincipleComparison Principle
Comparison Principle
 
Юнит тестирование в Zend Framework 2.0
Юнит тестирование в Zend Framework 2.0Юнит тестирование в Zend Framework 2.0
Юнит тестирование в Zend Framework 2.0
 
es6.concurrency()
es6.concurrency()es6.concurrency()
es6.concurrency()
 
JavascriptMVC
JavascriptMVCJavascriptMVC
JavascriptMVC
 
Introduction à Marionette
Introduction à MarionetteIntroduction à Marionette
Introduction à Marionette
 
RxSwift 예제로 감잡기
RxSwift 예제로 감잡기RxSwift 예제로 감잡기
RxSwift 예제로 감잡기
 
Extjs 4
Extjs 4Extjs 4
Extjs 4
 
Javascript and jQuery for Mobile
Javascript and jQuery for MobileJavascript and jQuery for Mobile
Javascript and jQuery for Mobile
 
Palestra sobre MongoDB com PHP no PHP'n'Rio
Palestra sobre MongoDB com PHP no PHP'n'Rio Palestra sobre MongoDB com PHP no PHP'n'Rio
Palestra sobre MongoDB com PHP no PHP'n'Rio
 
Sis quiz
Sis quizSis quiz
Sis quiz
 
Адаптация TInyMCE редактора под нужды клиента by Vitaly Nikolaev
Адаптация TInyMCE редактора под нужды клиента by Vitaly NikolaevАдаптация TInyMCE редактора под нужды клиента by Vitaly Nikolaev
Адаптация TInyMCE редактора под нужды клиента by Vitaly Nikolaev
 
Testgetriebene Entwicklung mit JavaScript – Jax 2012
Testgetriebene Entwicklung mit JavaScript – Jax 2012Testgetriebene Entwicklung mit JavaScript – Jax 2012
Testgetriebene Entwicklung mit JavaScript – Jax 2012
 
2km Workshop: Desenvolvimento ágil com o CakePHP
2km Workshop: Desenvolvimento ágil com o CakePHP2km Workshop: Desenvolvimento ágil com o CakePHP
2km Workshop: Desenvolvimento ágil com o CakePHP
 

More from Javier Eguiluz

deSymfony 2017: Symfony 4, Symfony Flex y el futuro de Symfony
deSymfony 2017: Symfony 4, Symfony Flex y el futuro de SymfonydeSymfony 2017: Symfony 4, Symfony Flex y el futuro de Symfony
deSymfony 2017: Symfony 4, Symfony Flex y el futuro de SymfonyJavier Eguiluz
 
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)Javier Eguiluz
 
Mastering Twig (DrupalCon Barcelona 2015)
Mastering Twig (DrupalCon Barcelona 2015)Mastering Twig (DrupalCon Barcelona 2015)
Mastering Twig (DrupalCon Barcelona 2015)Javier Eguiluz
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricksJavier Eguiluz
 
Twig, el nuevo motor de plantillas de Drupal 8
Twig, el nuevo motor de plantillas de Drupal 8Twig, el nuevo motor de plantillas de Drupal 8
Twig, el nuevo motor de plantillas de Drupal 8Javier Eguiluz
 
Silex, desarrollo web ágil y profesional con PHP
Silex, desarrollo web ágil y profesional con PHPSilex, desarrollo web ágil y profesional con PHP
Silex, desarrollo web ágil y profesional con PHPJavier Eguiluz
 
Twig avanzado (sf2Vigo)
Twig avanzado (sf2Vigo)Twig avanzado (sf2Vigo)
Twig avanzado (sf2Vigo)Javier Eguiluz
 
Desymfony 2012 - Concurso de diseño
Desymfony 2012 - Concurso de diseñoDesymfony 2012 - Concurso de diseño
Desymfony 2012 - Concurso de diseñoJavier Eguiluz
 
Desymfony 2011 - Tutorial #5: Backend
Desymfony 2011 - Tutorial #5: BackendDesymfony 2011 - Tutorial #5: Backend
Desymfony 2011 - Tutorial #5: BackendJavier Eguiluz
 
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasos
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasosDesymfony 2011 - Tutorial #1: Instalacion y primeros pasos
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasosJavier Eguiluz
 
Desymfony 2011 - Introducción a Symfony2
Desymfony 2011 - Introducción a Symfony2Desymfony 2011 - Introducción a Symfony2
Desymfony 2011 - Introducción a Symfony2Javier Eguiluz
 
Symfony2, Jornadas Symfony
Symfony2, Jornadas SymfonySymfony2, Jornadas Symfony
Symfony2, Jornadas SymfonyJavier Eguiluz
 
Curso Symfony - Anexos
Curso Symfony - AnexosCurso Symfony - Anexos
Curso Symfony - AnexosJavier Eguiluz
 
Curso Symfony - Clase 4
Curso Symfony - Clase 4Curso Symfony - Clase 4
Curso Symfony - Clase 4Javier Eguiluz
 
Curso Symfony - Clase 3
Curso Symfony - Clase 3Curso Symfony - Clase 3
Curso Symfony - Clase 3Javier Eguiluz
 
Curso Symfony - Clase 2
Curso Symfony - Clase 2Curso Symfony - Clase 2
Curso Symfony - Clase 2Javier Eguiluz
 

More from Javier Eguiluz (20)

deSymfony 2017: Symfony 4, Symfony Flex y el futuro de Symfony
deSymfony 2017: Symfony 4, Symfony Flex y el futuro de SymfonydeSymfony 2017: Symfony 4, Symfony Flex y el futuro de Symfony
deSymfony 2017: Symfony 4, Symfony Flex y el futuro de Symfony
 
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
 
Mastering Twig (DrupalCon Barcelona 2015)
Mastering Twig (DrupalCon Barcelona 2015)Mastering Twig (DrupalCon Barcelona 2015)
Mastering Twig (DrupalCon Barcelona 2015)
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricks
 
Twig, el nuevo motor de plantillas de Drupal 8
Twig, el nuevo motor de plantillas de Drupal 8Twig, el nuevo motor de plantillas de Drupal 8
Twig, el nuevo motor de plantillas de Drupal 8
 
Twig tips and tricks
Twig tips and tricksTwig tips and tricks
Twig tips and tricks
 
Silex, desarrollo web ágil y profesional con PHP
Silex, desarrollo web ágil y profesional con PHPSilex, desarrollo web ágil y profesional con PHP
Silex, desarrollo web ágil y profesional con PHP
 
Wallpaper Notifier
Wallpaper NotifierWallpaper Notifier
Wallpaper Notifier
 
Backend (sf2Vigo)
Backend (sf2Vigo)Backend (sf2Vigo)
Backend (sf2Vigo)
 
Twig avanzado (sf2Vigo)
Twig avanzado (sf2Vigo)Twig avanzado (sf2Vigo)
Twig avanzado (sf2Vigo)
 
Desymfony 2012 - Concurso de diseño
Desymfony 2012 - Concurso de diseñoDesymfony 2012 - Concurso de diseño
Desymfony 2012 - Concurso de diseño
 
Desymfony 2011 - Twig
Desymfony 2011 - TwigDesymfony 2011 - Twig
Desymfony 2011 - Twig
 
Desymfony 2011 - Tutorial #5: Backend
Desymfony 2011 - Tutorial #5: BackendDesymfony 2011 - Tutorial #5: Backend
Desymfony 2011 - Tutorial #5: Backend
 
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasos
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasosDesymfony 2011 - Tutorial #1: Instalacion y primeros pasos
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasos
 
Desymfony 2011 - Introducción a Symfony2
Desymfony 2011 - Introducción a Symfony2Desymfony 2011 - Introducción a Symfony2
Desymfony 2011 - Introducción a Symfony2
 
Symfony2, Jornadas Symfony
Symfony2, Jornadas SymfonySymfony2, Jornadas Symfony
Symfony2, Jornadas Symfony
 
Curso Symfony - Anexos
Curso Symfony - AnexosCurso Symfony - Anexos
Curso Symfony - Anexos
 
Curso Symfony - Clase 4
Curso Symfony - Clase 4Curso Symfony - Clase 4
Curso Symfony - Clase 4
 
Curso Symfony - Clase 3
Curso Symfony - Clase 3Curso Symfony - Clase 3
Curso Symfony - Clase 3
 
Curso Symfony - Clase 2
Curso Symfony - Clase 2Curso Symfony - Clase 2
Curso Symfony - Clase 2
 

Twig, los mejores trucos y técnicas avanzadas

  • 1. Twig, los mejores trucos y técnicas avanzadas Javier Eguíluz Universitat Jaume I · Castellón · 15-16 junio 2012 · desymfony.com
  • 2. ¡muchas gracias a nuestros patrocinadores! PATROCINADOR PLATINO PATROCINADORES ORO PATROCINADORES PLATA PATROCINADORES BRONCE
  • 3. Agenda • Buenas prácticas • Técnicas avanzadas • Trucos y cosas nuevas
  • 4. Código El código fuente de los ejemplos más avanzados de esta ponencia está disponible en: https://github.com/javiereguiluz/ desymfony!twig
  • 5. PERSONALIZACIÓN EXTREMA DE FORMULARIOS 1.* BÁSICO INTERMEDIO AVANZADO
  • 6. <div> {{ form_label(form.nombre) }} {{ form_errors(form.nombre) }} {{ form_widget(form.nombre) }} </div>
  • 7. {% extends '::base.html.twig' %} {% form_theme form _self %} {% block integer_widget %} <div class="integer_widget"> {% set type = type|default('number') %} {{ block('field_widget') }} </div> {% endblock %} {% block content %} {# render the form #} {{ form_row(form.age) }} {% endblock %}
  • 8. {% extends '::base.html.twig' %} {% form_theme form _self %} {% block integer_widget %} <div class="integer_widget"> {% set type = type|default('number') %} {{ block('field_widget') }} </div> {% endblock %} {% block content %} {# render the form #} {{ form_row(form.age) }} {% endblock %}
  • 9. {% extends '::base.html.twig' %} {% form_theme form _self %} {% block integer_widget %} <div class="integer_widget"> {% set type = type|default('number') %} {{ block('field_widget') }} </div> {% endblock %} {% block content %} {# render the form #} {{ form_row(form.age) }} {% endblock %}
  • 10. {% form_theme form _self %} {% block _formulario_nombre_widget %} <div class="especial"><strong> {{ block('field_widget') }} </strong></div> {% endblock %} {{ form_widget(form.nombre) }}
  • 11. {% form_theme form _self %} {% block _formulario_nombre_widget %} <div class="especial"><strong> {{ block('field_widget') }} </strong></div> {% endblock %} {{ form_widget(form.nombre) }}
  • 12. class UsuarioType extends AbstractType { public function buildForm(FormBuilder $builder, array $options) { $builder ->add('nombre') ->add('apellidos') ->add('email') ->... ->add('ciudad') ; } public function getName() { return 'cupon_backendbundle_usuariotype'; } }
  • 13. class UsuarioType extends AbstractType { public function buildForm(FormBuilder $builder, array $options) { $builder ->add('nombre') ->add('apellidos') ->add('email') ->... ->add('ciudad') ; } public function getName() { return 'cupon_backendbundle_usuariotype'; } }
  • 15. <strong>Label</strong>: Valor <strong> {{ form.nombreCampo.vars.label }} </strong>: {{ form. nombreCampo.vars.value }}
  • 16. <strong>Label</strong>: Valor <strong> {{ form.nombreCampo.vars.label }} </strong>: {{ form. nombreCampo.vars.value }}
  • 17. CONSTANTES 1.* BÁSICO INTERMEDIO AVANZADO
  • 20. {{ constant('SymfonyComponent HttpKernelKernel::VERSION') }} namespace SymfonyComponentHttpKernel; abstract class Kernel implements KernelInterface { // ... const VERSION = '2.0.15'; }
  • 21. namespace SymfonyComponentHttpKernel; abstract class Kernel implements KernelInterface { // ... const VERSION = '2.1.0-DEV'; const VERSION_ID = '20100'; const MAJOR_VERSION = '2'; const MINOR_VERSION = '1'; const RELEASE_VERSION = '0'; const EXTRA_VERSION = 'DEV'; }
  • 22. REDEFINE LOS FILTROS POR DEFECTO 1.* BÁSICO INTERMEDIO AVANZADO
  • 24. asort() array_multisort() arsort() natcasesort() krsort() natsort() ksort() rsort() rsort() shuffle() shuffle() uasort() sort() uksort() usort()
  • 25. ¿Dónde se definen los filtros, etiquetas y tags de Twig?
  • 26. lib/twig/Extesion/Core.php class Twig_Extension_Core extends Twig_Extension { public function getTokenParsers() { return array( new Twig_TokenParser_For(), new Twig_TokenParser_If(), new Twig_TokenParser_Extends(), new Twig_TokenParser_Include(), new Twig_TokenParser_Block(), // ... ); } public function getFilters() { $filters = array( 'format' => new Twig_Filter_Function('sprintf'), 'replace' => new Twig_Filter_Function('strtr'), 'abs' => new Twig_Filter_Function('abs'),
  • 27. {{ array|sort }} /** * Sorts an array. * * @param array $array An array */ function twig_sort_filter($array) { asort($array); return $array; }
  • 29. class MiCoreExtension extends Twig_Extension_Core { // ... }
  • 30. class MiCoreExtension extends Twig_Extension_Core { public function getFilters() { // ... } }
  • 31. class MiCoreExtension extends Twig_Extension_Core { public function getFilters() { return array_merge( parent::getFilters(), array( ... ) ); } }
  • 32. $twig = new Twig_Environment($loader, array( ... )); $twig->addExtension(new MiCoreExtension());
  • 33. class MiCoreExtension extends Twig_Extension_Core { public function getFilters() { return array_merge(parent::getFilters(), array( 'sort' => new Twig_Filter_Method($this, 'sortFilter') )); } public function sortFilter($array) { natcasesort($array); return $array; } }
  • 34. {{ array|sort }} function twig_sort_filter($array) { natcasesort($array); return $array; }
  • 35. TODO SON EXPRESIONES 1.5 BÁSICO INTERMEDIO AVANZADO
  • 36. {% include 'seccion_' ~ oferta.categoria ~ '.twig' %} {% for i in (1+3)//2..2**(3-1) %} {% endfor %}
  • 37. {% set ofertas = { ('oferta' ~ oferta.id): '...', (3 ~ '_' ~ oferta.nombre|length): '...', (2**2+1): '...' } %}
  • 38. {% set ofertas = { ('oferta' ~ oferta.id): '...', (3 ~ '_' ~ oferta.nombre|length): '...', (2**2+1): '...' } %}
  • 39. {% set ofertas = { oferta7040: '...', 3_57: '...', 5: '...' } %}
  • 40. EMBED 1.8 BÁSICO INTERMEDIO AVANZADO
  • 41. {% include %} {% extends %} {% embed %}
  • 45. {% embed "lateral.twig" %} {% block principal %} ... {% endblock %} {% endembed %}
  • 46. {% embed "lateral.twig" %} {% block secundario %} ... {% endblock %} {% endembed %}
  • 47. {% include 'plantilla.twig' %} {% embed 'plantilla.twig' %} {% endembed %}
  • 48. ESCAPING AUTOMÁTICO 1.8 BÁSICO INTERMEDIO AVANZADO
  • 49. {% filter escape('js') %} <script type="text/javascript"> var texto = "<p>Lorem ipsum dolor sit amet</p>"; alert(texto); </script> {% endfilter %} {% filter escape('html') %} <script type="text/javascript"> var texto = "<p>Lorem ipsum dolor sit amet</p>"; alert(texto); </script> {% endfilter %}
  • 50. x3cscript typex3dx22textx2fjavascriptx22x3ex0a var texto x3d x22x3cpx3eLorem ipsum dolor sit amet x3cx2fpx3ex22x3bx0a alertx28textox29x3bx0a x3cx2fscriptx3ex0a &lt;script type=&quot;text/javascript&quot;&gt; var texto = &quot;&lt;p&gt;Lorem ipsum dolor sit amet&lt;/p&gt;&quot;; alert(texto); &lt;/script&gt;
  • 51. {{ variable|e }} {{ variable|e('html') }} {{ variable|escape('js') }} {{ variable|e('js') }} {% autoescape %} ........... {% endautoescape %} {% autoescape 'html' %} ... {% endautoescape %} {% autoescape 'js' %} ....... {% endautoescape %} {% autoescape false %} .... {% endautoescape %}
  • 52. $twig = new Twig_Environment($loader, array( 'autoescape' => function($nombre_plantilla) { return decide_escape($nombre_plantilla); } ));
  • 53. $twig = new Twig_Environment($loader, array( 'autoescape' => function($nombre_plantilla) { return decide_escape($nombre_plantilla); } )); function decide_escape($plantilla) { $extension = substr($plantilla, strrpos($plantilla, '.') + 1); switch ($extension) { 'js': return 'js'; default: return 'html'; } }
  • 54. RANDOM 1.6 BÁSICO INTERMEDIO AVANZADO
  • 56. {{ random() }} {{ random(10) }}
  • 57. {{ random() }} {{ random(10) }} {{ random("abcde") }}
  • 58. {{ random() }} {{ random(10) }} {{ random("abcde") }} {{ random(['a', 'b', 'c']) }}
  • 59. FUNCIONES DINÁMICAS 1.5 BÁSICO INTERMEDIO AVANZADO
  • 63. $twig->addFunction( 'the_*', new Twig_Function_Function('wordpress') ); function wordpress($funcion, $opciones) { // ... }
  • 64. $twig->addFunction( 'the_*', new Twig_Function_Function('wordpress') ); function wordpress($funcion, $opciones) { // ... }
  • 66. {{ the_ID() }} function wordpress('ID', array()) { ... }
  • 67. {{ the_ID() }} function wordpress('ID', array()) { ... } {{ the_content() }}
  • 68. {{ the_ID() }} function wordpress('ID', array()) { ... } {{ the_content() }} function wordpress('content', array()) { ... }
  • 69. {{ the_title('<h3>', '</h3>') }} function wordpress('title', array( '<h3>', '</h3>' )) { ... }
  • 72. FILTROS DINÁMICOS 1.5 BÁSICO INTERMEDIO AVANZADO
  • 73. {{ 'now'|fecha_corta }} {{ 'now'|fecha_larga }} fecha_*()
  • 74. $twig->addFilter( 'fecha_*', new Twig_Filter_Function('fecha') ); function fecha($funcion, $opciones) { // ... }
  • 75. VARIABLES GLOBALES 1.* BÁSICO INTERMEDIO AVANZADO
  • 76. {{ app.security }} {{ app.user }} {{ app.request }} {{ app.session }} {{ app.environment }} {{ app.debug }}
  • 77. {{ _charset }} {{ _context }} {{ _self }}
  • 78. {{ _charset }} UTF-8
  • 79. {{ _context }} {% for i in _context|keys %} {{ i }} {% endfor %}
  • 80. {{ _context }} {% for i in _context|keys %} {{ i }} app assetic {% endfor %} _parent oferta ciudad_por_defecto ciudadSeleccionada expirada ...
  • 81. {{ _context }} {% for i in _context|keys %} {{ i }} app assetic {% endfor %} _parent oferta ciudad_por_defecto ciudadSeleccionada expirada ...
  • 82. {{ _context }} {{ variable|mi_filtro(_context) }}
  • 83. {{ _context }} {{ variable|mi_filtro(_context) }} SÓLO SI NO HAY OTRO REMEDIO
  • 85. {{ _self }} {{ _self.getTemplateName }} OfertaBundle:Default:includes/ oferta.html.twig
  • 86. {{ _self }} {{ _self.blocks|keys|join(", ") }} title, stylesheets, rss, javascripts, id, body
  • 87. {{ _self }} {{ _self.blocks|keys|join(", ") }} title, stylesheets, rss, javascripts, id, body SÓLO SI NO HAY OTRO REMEDIO
  • 88. EL SANDBOX 1.* BÁSICO INTERMEDIO AVANZADO
  • 89. DEMO
  • 90. DATE 1.6 BÁSICO INTERMEDIO AVANZADO
  • 91. {{ 'now'|date("d/m/Y H:i:s") }} {{ 'now'|date("d/m/Y H:i:s", "America/Argentina/Buenos_Aires") }} {{ 'now'|date("d/m/Y H:i:s", "America/Havana") }} {{ 'now'|date("d/m/Y H:i:s", "America/Caracas") }} {{ 'now'|date("d/m/Y H:i:s", "America/Lima") }}
  • 92. España: 11/06/2012 10:45:22 Argentina: 11/06/2012 05:45:22 Cuba: 11/06/2012 04:45:22 Venezuela: 11/06/2012 04:15:22 Perú: 11/06/2012 03:45:22
  • 93. $twig = new Twig_Environment($loader); $twig->getExtension('core') ->setDateFormat('d-m-Y H:i:s', '%d días'); $twig->getExtension('core') ->setTimezone('America/Montevideo');
  • 96. {{ date() }} {{ date()|date() }}
  • 97. {{ date() }} {{ date()|date() }} {{ date()|date("d/m/Y") }}
  • 98. Tu invitación caduca el {{ date('+2days')|date }}
  • 99. Tu invitación caduca el {{ date('+2days')|date }} {% if date(fechaNacimiento) < date('-18years') %} ¡Eres menor de edad! {% endif %}
  • 100. La última sorpresa de #deSymfony se desvelará el {{ date('next Monday')|date() }}
  • 101. OPERADORES PROPIOS 1.* BÁSICO INTERMEDIO AVANZADO
  • 102. $loader = new Twig_Loader_Filesystem(...); $twig = new Twig_Environment($loader, array(...)); $twig->addExtension(new OperatorsExtension());
  • 103. Operador "quédate con el mayor" {{ 5 >> 2 }} 5 {{ 4 >> 20 }} 20
  • 104. class OperatorsExtension extends Twig_Extension { public function getName() { return 'OperatorsExtension'; } public function getOperators() { return array( array(), array('>>' => array( 'precedence' => 20, 'class' => 'DesymfonyOperatorsMaxOperator', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT ) )); } }
  • 105. class OperatorsExtension extends Twig_Extension { public function getName() { return 'OperatorsExtension'; } public function getOperators() { return array( array(), array('>>' => array( 'precedence' => 20, 'class' => 'DesymfonyOperatorsMaxOperator', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT ) )); } }
  • 106. class OperatorsExtension extends Twig_Extension { public function getName() { return 'OperatorsExtension'; } public function getOperators() { return array( array(), array('>>' => array( 'precedence' => 20, 'class' => 'DesymfonyOperatorsMaxOperator', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT ) )); } }
  • 107. 2 +3**2 / 4 .. 4-5//2 Operador Precedence != 20 + 30 ** 200
  • 108. {{ a >> b }} max($a, $b); TWIG PHP
  • 109. class MaxOperator extends Twig_Node_Expression_Binary { public function compile(Twig_Compiler $compiler) { $compiler ->raw('max(') ->subcompile($this->getNode('left')) ->raw(', ') ->subcompile($this->getNode('right')) ->raw(')') ; }
  • 110. class MaxOperator extends Twig_Node_Expression_Binary { public function compile(Twig_Compiler $compiler) { $compiler ->raw('max(') ->subcompile($this->getNode('left')) ->raw(', ') ->subcompile($this->getNode('right')) ->raw(')') ; } max($a, $b);
  • 111. // line 23 echo twig_escape_filter($this->env, max(5, 2), "html", null, true );
  • 112. $compiler ->raw() (literal) ->write() (indentado) ->string() (entrecomillado) ->indent() (indentar) ->outdent() (desindentar) ;
  • 113. Operador "cambia claves por valores" PHP array_flip(range('a', 'z')) TWIG {% for i in <->('a'..'z') %} {{ i }}, {% endfor %}
  • 114. Operador "cambia claves por valores" PHP array_flip(range('a', 'z')) TWIG {% for i in <->('a'..'z') %} {{ i }}, {% endfor %}
  • 115. class OperatorsExtension extends Twig_Extension { public function getName() { return 'OperatorsExtension'; } public function getOperators() { return array( array('<->' => array( 'precedence' => 50, 'class' => 'DesymfonyOperatorsFlipOperator', ), array() )); } }
  • 116. class OperatorsExtension extends Twig_Extension { public function getName() { return 'OperatorsExtension'; } public function getOperators() { return array( array('<->' => array( 'precedence' => 50, 'class' => 'DesymfonyOperatorsFlipOperator', ), array() )); } }
  • 117. class OperatorsExtension extends Twig_Extension { public function getName() { return 'OperatorsExtension'; } public function getOperators() { return array( array('<->' => array( 'precedence' => 50, 'class' => 'DesymfonyOperatorsFlipOperator', ), array() )); } }
  • 118. namespace DesymfonyOperators; class FlipOperator extends Twig_Node_Expression_Unary { public function compile(Twig_Compiler $compiler) { $compiler ->raw("array_flip(") ->subcompile($this->getNode('node')) ->raw(")") ; }
  • 119. namespace DesymfonyOperators; class FlipOperator extends Twig_Node_Expression_Unary { public function compile(Twig_Compiler $compiler) { $compiler ->raw("array_flip(") ->subcompile($this->getNode('node')) ->raw(")") ; } array_flip($coleccion);
  • 120. // line 17 $context['_parent'] = (array) $context; $context['_seq'] = twig_ensure_traversable(array_flip(range("a", "z"))); foreach ($context['_seq'] as $context["_key"] => $context["i"]) { // line 18 echo " "; if (isset($context["i"])) { $_i_ = $context["i"]; } else { $_i_ = null; } echo twig_escape_filter($this->env, $_i_, "html", null, true); echo ","; }
  • 121. // line 17 $context['_parent'] = (array) $context; $context['_seq'] = twig_ensure_traversable(array_flip(range("a", "z"))); foreach ($context['_seq'] as $context["_key"] => $context["i"]) { // line 18 echo " "; if (isset($context["i"])) { $_i_ = $context["i"]; } else { $_i_ = null; } echo twig_escape_filter($this->env, $_i_, "html", null, true); echo ","; }
  • 122. SUPER CACHÉ 1.* BÁSICO INTERMEDIO AVANZADO
  • 123. # mkfs -q /dev/ram1 65536 # mkdir -p /twigcache # mount /dev/ram1 /twigcache Inspirado por: http://www.cyberciti.biz/faq/howto!create!linux!ram!disk!filesystem/
  • 124. $twig = new Twig_Environment( $loader, array('cache' => '/twigcache') ); # app/config/config.yml twig: cache: '/twigcache'
  • 125. ETIQUETAS PROPIAS 1.* BÁSICO INTERMEDIO AVANZADO
  • 126. etiquetas operadores #1 tests #2 #3
  • 127. {% source 'simple.twig' %} {% source '../../../composer.json' %}
  • 128. 1. Clase Token Parser TWIG TWIG 2. Clase Node TWIG PHP
  • 129. $loader = new Twig_Loader_Filesystem(...); $twig = new Twig_Environment($loader, array(...)); $twig->addTokenParser(new SourceTokenParser());
  • 130. {% source '...' %} class SourceTokenParser extends Twig_TokenParser { public function getTag() { return 'source'; } }
  • 131. namespace DesymfonyTags; use DesymfonyTagsSourceNode; class SourceTokenParser extends Twig_TokenParser { public function parse(Twig_Token $token) { $lineno = $token->getLine(); $value = $this->parser->getExpressionParser()->parseExpression(); $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); return new SourceNode($value, $lineno, $this->getTag()); } }
  • 132. class SourceNode extends Twig_Node { public function __construct(Twig_Node_Expression $value, $lineno, $tag = null) { parent::__construct(array('file' => $value), array(), $lineno, $tag); } public function compile(Twig_Compiler $compiler) { $compiler -> // ... ->write('echo file_get_contents(') ->subcompile($this->getNode('file')) ->raw(');'); ; } }
  • 133. // line 3 echo file_get_contents("simple.twig"); // line 4 echo "n"; // line 5 echo file_get_contents("../../../composer.json"); // line 6 echo "n";
  • 134. INTERPOLACIÓN 1.5 BÁSICO INTERMEDIO AVANZADO
  • 135. La oferta cuesta 25.78 euros (30.42 con IVA) y es válida hasta el 10/06/2012
  • 136. La oferta cuesta 25.78 euros (30.42 con IVA) y es válida hasta el 10/06/2012
  • 137. La oferta cuesta 25.78 euros (30.42 con IVA) y es válida hasta el 10/06/2012 ~ {{ 'La oferta cuesta ' ~ oferta.precio ~ ' euros (' ~ oferta.precio*1.18 ~ ' con IVA) y es válida hasta el ' ~ oferta.fechaExpiracion|date() }}
  • 138. La oferta cuesta 25.78 euros (30.42 con IVA) y es válida hasta el 10/06/2012 format() {{ 'La oferta cuesta %.2f euros (%.2f con IVA) y es válida hasta el %s'|format(oferta.precio, oferta.precio*1.18, oferta.fechaExpiracion|date()) }}
  • 139. La oferta cuesta 25.78 euros (30.42 con IVA) y es válida hasta el 10/06/2012 replace() {{ 'La oferta cuesta :precio euros (:total con IVA) y es válida hasta el :fecha'|replace({ ':precio': oferta.precio, ':total': oferta.precio*1.18, ':fecha': oferta.fechaExpiracion|date() }) }}
  • 140. La oferta cuesta 25.78 euros (30.42 con IVA) y es válida hasta el 10/06/2012 {{ "La oferta cuesta #{oferta.precio} euros (#{oferta.precio*1.18} con IVA) y es válida hasta el #{oferta.fechaExpiracion|date()}" }}
  • 141. {{ "... #{ expresión } ..." }}
  • 142. {{ "... #{ expresión } ..." }}
  • 143. NUEVOS FILTROS Y ETIQUETAS 1.5 BÁSICO INTERMEDIO AVANZADO
  • 146. {{ 1 + 1 }} 2 {% do 1 + 1 %} (nada)
  • 147. {% set lista = ['a', 'b', 'c', 'd'] %} {{ lista|shift }} {% do lista|shift %} Fuente: https://github.com/fabpot/Twig/issues/446
  • 148. [:]
  • 149. {% set lista = ['a', 'b', 'c', 'd'] %} {{ lista[1:] }}
  • 150. {% set lista = ['a', 'b', 'c', 'd'] %} {{ lista[-1:] }}
  • 151. {% set lista = ['a', 'b', 'c', 'd'] %} {{ lista[2:2] }}
  • 152. {{ '@username'[1:] }} {{ 'Lorem ipsum...'[-4:10] }}
  • 153. operadores bitwise {{ a b-and b }} $a ^ $b {{ a b-xor b }} $a << $b {{ a b-or b }} $a >> $b TWIG PHP
  • 154. TWIG_TEMPLATE 1.* BÁSICO INTERMEDIO AVANZADO
  • 155. <html> <head> ... </head> <body> ... <span data-host="Darwin mbp.local 10.8.0 Darwin Kernel Version 10.8.0: Tue Jun 7 16:33:36 PDT 2011; root:xnu-1504.15.3~1/ RELEASE_I386 i386" data-elapsed="0.97804594039917 sec." data-timestamp="1339609672.9781"></span> </body> </html>
  • 158. app/cache/dev/twig/09/fc/2d8a188dda8245d295e6324582f2.php <?php /* ::frontend.html.twig */ class __TwigTemplate_09fc2d8a188dda8245d295e6324582f2 extends Twig_Template { public function __construct(Twig_Environment $env) { parent::__construct($env); $this->parent = $this->env->loadTemplate("::base.html.twig"); $this->blocks = array( 'stylesheets' => array($this, 'block_stylesheets'), 'javascripts' => array($this, 'block_javascripts'), 'body' => array($this, 'block_body'), 'article' => array($this, 'block_article'),
  • 159. class __TwigTemplate_09f...2f2 extends Twig_Template { // ... }
  • 160. namespace CuponBackendBundleController; use SymfonyBundleFrameworkBundleControllerController; class UsuarioController extends Controller { public function indexAction() { //... return $this->render( 'BackendBundle:Usuario:index.html.twig', array( ... ) ); } }
  • 161. Symfony/Bundle/FrameworkBundle/Controller/Controller.php function render($view, $parameters, $response) { return $this->container->get('templating') ->renderResponse($view, $parameters, $response); }
  • 162. lib/Twig/Environment.php public function render($name, array $context = array()) { return $this->loadTemplate($name) ->render($context); }
  • 163. return $this->render( ... ); render( ... ) Twig_Template
  • 164. return $this->render( ... ); render( ... ) Twig_Template
  • 165. <html> <head> ... </head> <body> ... <span data-host="Darwin mbp.local 10.8.0 Darwin Kernel Version 10.8.0: Tue Jun 7 16:33:36 PDT 2011; root:xnu-1504.15.3~1/ RELEASE_I386 i386" data-elapsed="0.97804594039917 sec." data-timestamp="1339609672.9781"></span> </body> </html>
  • 166. base_template_class $loader = new Twig_Loader_Filesystem(__DIR__.'/../Resources/views'); $twig = new Twig_Environment($loader, array( 'base_template_class' => 'DesymfonyTemplateMiTwigTemplate', )); # app/config/config.yml twig: base_template_class: "DesymfonyTemplateMiTwigTemplate"
  • 167. namespace DesymfonyTemplate; abstract class MiTwigTemplate extends Twig_Template { public function render(array $context) { $traza = sprintf('<span data-host="%s" data-elapsed="%s sec." data-timestamp="%s"></span>', php_uname(), microtime(true)-$_SERVER['REQUEST_TIME'], microtime(true) ); return str_replace('</body>', $traza."n</body>", parent::render($context) ); } }
  • 168. TWIG LINTER 1.* BÁSICO INTERMEDIO AVANZADO
  • 169. $twig = new Twig_Environment($loader, array(..)); try { $twig->parse($twig->tokenize($plantilla)); echo "[OK] La sintaxis de la plantilla es correcta"; } catch (Twig_Error_Syntax $e) { echo "[ERROR] La plantilla tiene errores de sintaxis"; }
  • 170. $ php app/console twig:lint @MyBundle $ php app/console twig:lint src/Cupon/ OfertaBundle/Resources/views/ index.html.twig
  • 175. Contacto • javier.eguiluz@gmail.com • github.com/javiereguiluz • twitter.com/javiereguiluz • linkd.in/javiereguiluz