extending twig
TRANSCRIPT
Extending Twig
Gerry Vandermaesen
About me
• @gerryvdm 🐦
• Web developer at King Foo 🐵 (Intracto Group)
• SensioLabs Certified Symfony Developer 👑
• Drupal noob 😟
Twig Templating Engine
• Fast 🏃
• Secure 🔑
• Flexible 🐍
• Replaces PHPTemplate in D8 👏
Basic syntax
<h1>{{ article.title }}</h1> <p>Published on {{ article.createdAt | date('d/m/Y') }}</p> {% if article.summary %} <p>{{ article.summary }}</p> {% endif %}<div>{{ article.content }}</div> <ul class="tags"> {% for tag in article.tags %} <li>{{ tag }}</li> {% endfor %}</ul>
Some cool Twig features
Template inheritance
{# parent.html.twig #}
<!doctype html> <html> <head> <title> {% block title %}DUG Belgium{% endblock %} </title> </head> <body> {% block body %}{% endblock %} </body> </html>
Template inheritance
{# child.html.twig #}{% extends "parent.html.twig" %}{% block title %}Blog - {{ parent() }}{% endblock %} {% block body %} <h1>Blog</h1> {% block content %}{% endblock %} {% endblock %}
Template inheritance
{# grandchild.html.twig #}{% extends "child.html.twig" %}{% block content %} <h3>{{ post.title }}</h3> <div>{{ post.content }}</div> {% endblock %}
Functions
{# get a PHP constant #}{{ constant("Some\\ClassName::CONSTANT_NAME") }}{# dump a variable's content #}{{ dump(foo) }}{# get random element #}{{ random(["apple", "banana"]) }}
Filters
{{ "some random title" | title }}{# outputs "Some Random Title" #}
{{ "now" | date_modify("+1 day") | date("d/m/Y") }}{# outputs "26/06/2016" #}{% set fruits = ["banana", "apple", "pear", "kiwi"] %}{{ fruits | slice(1, 2) | join(", ") }}{# outputs "apple, pear" #}{{ "combo chain" | upper | lower | upper | lower | upper }}{# outputs "COMBO CHAIN" #}
Tags
{% spaceless %} <h2> Lorem ipsum </h2> {% endspaceless %} {# outputs "<h2>Lorem ipsum</h2>" #}
Macros
{# macros.html.twig #}{% macro link_email(email, label) %} <a href="mailto:{{ email }}"> {{- label | default(email) -}} </a> {% endmacro %} {# foo.html.twig #}{% import "macros.html.twig" as macro %}<p>Contact me at {{ macro.link_email("[email protected]") }}</p>
Extending Twig
General workflow
• Implement an extension (extend \Twig_Extension)
• Register the extension in the Twig environment
• Profit 💰 💰 💰
Writing an extension
<?php// modules/twig_demo/src/Twig/ExampleExtension.phpnamespace Drupal\twig_demo\Twig;class ExampleExtension extends \Twig_Extension{ public function getName() { return 'example'; }}
Registering the extension
# modules/twig_demo/twig_demo.services.ymlservices: twig.example_extension: class: Drupal\twig_demo\Twig\ExampleExtension tags: - { name: twig.extension }
Adding custom functions<?phpclass ExampleExtension extends \Twig_Extension{ public function getFunctions() { return array( new \Twig_SimpleFunction( 'gravatar', array($this, ‘generateGravatarUrl') ) ); } public function generateGravatarUrl($email, $size = 100) { return sprintf('http://www.gravatar.com/avatar/%s?s=%s', md5($email), $size ); }}
Invoking our function
<p> <img src="{{ gravatar('[email protected]', 50) }}”> </p>
Adding custom filters<?phpclass ExampleExtension extends \Twig_Extension{ public function getFilters() { return array( new \Twig_SimpleFilter( 'highlight', array($this, 'highlight'), array('is_safe' => array('html' => true)) ) ); } public function highlight($subject, $search) { $highlighted = sprintf('<span style="background: yellow; font-weight: bold;">%s</span>', $search); return str_replace($search, $highlighted, $subject); }}
Invoking our filter
<p> {{ "Hello, world. Goodbye, world!” | highlight('world') }} </p>
Adding global variables<?phpclass ExampleExtension extends \Twig_Extension{ private $container; public function __construct(ContainerInterface $container) { $this->container = $container; } public function getGlobals() { return array( 'pi' => M_PI, 'modules' => array_keys( $this->container->getParameter('container.modules') ) ); }}
Using our globals
<p>Pi ~= {{ pi | round(6) }}</p> <p>{{ modules | join(', ') }}</p>
Defining custom tests<?phpclass ExampleExtension extends \Twig_Extension{ public function getTests() { return array( new \Twig_SimpleTest('today', array($this, 'testIfToday')) ); } public function testIfToday($value) { $today = new \DateTime('now'); if (!$value instanceof \DateTimeInterface) { $value = new \DateTime($value); } return $value->format('Y-m-d') === $today->format('Y-m-d'); }}
Using our custom test
{% if '2015-06-25' is today %} <p>It is Drupal User Group meeting today</p> {% endif %}{% if '2015-12-25' is today %} <p>It is Christmas today</p> {% endif %}
Defining custom tags
<?phpclass ExampleExtension extends \Twig_Extension{ public function getTokenParsers() { return array( new MarkdownTokenParser() ); }}
Writing a token parser<?phpnamespace Drupal\twig_demo\Twig;class MarkdownTokenParser extends \Twig_TokenParser{ public function parse(\Twig_Token $token) { $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); $body = $this->parser->subparse(array($this, 'decideMarkdownEnd'), true); $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); return new MarkdownNode($body, $token->getLine(), $this->getTag()); } public function decideMarkdownEnd(\Twig_Token $token) { return $token->test('endmarkdown'); } public function getTag() { return 'markdown'; }}
Writing a node class<?phpnamespace Drupal\twig_demo\Twig;class MarkdownNode extends \Twig_Node{ public function __construct(\Twig_NodeInterface $body, $line, $tag = 'markdown') { parent::__construct(array('body' => $body), array(), $line, $tag); } public function compile(\Twig_Compiler $compiler) { $compiler ->addDebugInfo($this) ->write("ob_start();\n") ->subcompile($this->getNode('body')) ->write("echo \\Michelf\\Markdown::defaultTransform(ob_get_clean());\n") ; }}
Questions?
• https://github.com/gerryvdm/dug
• http://www.slideshare.net/gerryvdm/extending-twig