Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Making Sense of Twig

15,194 views

Published on

These are the slides for a workshop I’ve given at a couple conferences, explaining how Twig works to people who don’t necessarily come from a programming background.

Published in: Software, Technology, Business

Making Sense of Twig

  1. 1. Making Sense of Twig Brandon Kelly
  2. 2. What is Twig?
  3. 3. It’s a templating language. ! All templating features are available globally to all templates in any context, as part of the language. ! It’s not up to each and every application feature to provide its own tags.
  4. 4. It’s super powerful. ! - Many ways to stay DRY - Custom variables - Functions - Filters - It knows math - Whitespace control - Extensible
  5. 5. Templates get compiled into PHP, so it’s super fast.
  6. 6. Twig even makes debugging a piece of cake.
  7. 7. Used by some popular apps: ! - LemonStand - Symfony - Craft - others
  8. 8. twig.sensiolabs.org
  9. 9. The Language
  10. 10. Types of Tags
  11. 11. <html> <head> <title>{{ title }}</title> </head> <body> <h1>{{ title }}</h1> {% block main %} <p>Hey!</p> {% endblock %} </body> </html>
  12. 12. <html> <head> <title>{{ title }}</title> </head> <body> <h1>{{ title }}</h1> {% block main %} <p>Hey!</p> {% endblock %} </body> </html>
  13. 13. <html> <head> <title>{{ title }}</title> </head> <body> <h1>{{ title }}</h1> {% block main %} <p>Hey!</p> {% endblock %} </body> </html>
  14. 14. Twig code always lives within one of these three tag pairs: ! {# ... #} {{ ... }} {% ... %}
  15. 15. {# ... #} Comment Tags
  16. 16. Comment tags are like HTML comments, except they won’t show up in the page source. ! {# This won’t make it to the page source #} ! <!-- This will -->
  17. 17. {{ ... }} Output Tags
  18. 18. Output tags output stuff to the browser. ! {{ title }} ! {{ "Howdy" }} ! {{ 84 / 2 }}
  19. 19. {% ... %} Logic Tags
  20. 20. Logic tags (or just “tags”) control the logic of the template: ! - Conditionals - Loops - Variable definitions - Macros - Template includes - etc.
  21. 21. The syntax varies from tag to tag.
  22. 22. Some are just a single word. ! {% requireLogin %}
  23. 23. Some take parameters. ! {% exit 404 %}
  24. 24. Some have a closing tag. ! {% block content %} <p>Hey</p> {% endblock %}
  25. 25. Some even have nested tags. ! {% if foo %} <p>Something</p> {% else %} <p>Something else</p> {% endif %}
  26. 26. It really all depends on the tag. ! The only thing they have in common is that they all start with “{%”, followed by the tag name. ! {% tagname ...
  27. 27. Twig comes with several built-in tags. ! {% autoescape %} {% block %} {% filter %} {% do %} {% embed %} {% extends %} {% autoescape %} {% flush %} {% for %} {% from %} {% if %} {% import %} {% include %} {% macro %} {% sandbox %} {% set %} {% spaceless %} {% use %} {% verbatim %}
  28. 28. {% autoescape %} ! Escapes text for HTML. ! {% autoescape %} <p>Hey</p> {% endautoescape %} => &lt;p&gt;Hey&lt;/p&gt;
  29. 29. {% autoescape %} (Cont.) ! ...or Javascript ! {% autoescape "js" %} http://foo.com {% endautoescape %} => httpx3Ax2Fx2Ffoo.com
  30. 30. {% spaceless %} ! Removes any whitespace between Twig/HTML tags. ! {%- spaceless %} <p>Hey there</p> {% endspaceless -%} => <p>Hey there</p>
  31. 31. {% verbatim %} ! Defines template code that Twig should output as-is, without parsing. ! {% verbatim %} <p>Type “{{ foo }}”.</p> {% endverbatim %} => <p>Type “{{ foo }}”.</p>
  32. 32. You never put a Twig tag inside another Twig tag. ! Bad: ! {{ "Hey {{ firstName }}" }} ! Good: ! {{ "Hey " ~ firstName }}
  33. 33. Values, Expressions,
 and Variables
  34. 34. Twig supports five different types of values. ! Strings: "Hey" / 'Hey' Numbers: 42 / 3.14 Booleans: true / false Arrays: ['a', 'b', 'c'] Objects: { foo: 'bar' }
  35. 35. An expression is either a solo value, or multiple values combined to form another value. ! "Hey" => "Hey" "Hey "~"there" => "Hey there" 10 => 10 true => true ['a','b','c'] => ['a','b','c'] "Day "~1 => "Day 1" 10*(5+5) => 100
  36. 36. Variables are values that get set to a name, to be referenced later. ! Use the {% set %} tag to create them. ! {% set foo = "foo" %} {% set a = 42 %} {% set foobar = foo~"bar" %}
  37. 37. The {% set %} tag can also be used as a tag pair. ! {% set foo %} <p>foo</p> {% endset %} ! That’s the same as: ! {% set foo = "n <p>foo</p>n " %}
  38. 38. Variables are one tool for keeping our templates DRY. ! {% set title = "About Us" %} ... <title>{{ title }}</title> ... <h1>{{ title }}</h1>
  39. 39. Filters
  40. 40. Filters modify values. ! They can uppercase text, merge arrays, and lots of other things.
  41. 41. To pass a value through a filter, type a pipe (“|”) after the value, followed by the filter name. ! "foo"|upper => "FOO" 21.3|round => 21 ['a','b','c']|length => 3
  42. 42. Some accept arguments. ! "foobar"|slice(0,3) => foo
  43. 43. You can even chain them. ! "foobar"|slice(0,3)|upper => FOO
  44. 44. You can add filters to variables, too. ! {% set foo = "foo" %} {{ foo|length }} => 3
  45. 45. You can use them in pretty much any context you can think of. ! {{ value|filter }} ! {% set foo = value|filter %} ! {% do func(value|filter) %}
  46. 46. Filters only modify the value directly before the filter. ! "foo"~"bar"|upper => fooBAR ! ("foo"~"bar")|upper => FOOBAR
  47. 47. Twig comes with tons of built-in filters: |abs |batch |capitalize |covert_encoding |date |date_modify |default |escape |first |format |join |json_encode |keys |last |length |lower |nl2br |number_format |merge |upper |raw |replace |reverse |round |slice |sort |split |striptags |title |trim |url_encode
  48. 48. |length ! Finds the length of a string or array. ! {{ "foobar"|length }} => 6 ! {{ [1, 2, 3]|length }} => 3
  49. 49. |upper & |lower ! Modify the casing of a string. ! {{ "foo"|upper }} => FOO ! {{ "BAR"|lower }} => bar
  50. 50. |raw ! Protects a string from getting escaped. ! {{ "<p>Hey</p>" }} => &lt;p&gt;Hey&lt;/&gt; ! {{ "<p>Hey</p>"|raw }} => <p>Hey</p>
  51. 51. |date ! Formats a date. ! {{ now|date("F j, Y") }} => April 23, 2014
  52. 52. Functions
  53. 53. Functions perform functions. ! To call one, type its name, followed by parentheses. ! {{ parent() }}
  54. 54. Many functions accept arguments: ! random(1, 10)
  55. 55. Some of them are global: ! {{ dump(foo) }} ! And they can also be nested within objects: ! {{ craft.isLocalized() }}
  56. 56. Twig comes with a few global functions built-in: attribute() block() constant() cycle() date() dump() include() max() min() parent() random() range() source() template_from_string()
  57. 57. min() & max() ! Returns the smallest/largest value in a given array. ! {{ min([1, 2, 3]) }} => 1 ! {{ max([1, 2, 3]) }} => 3
  58. 58. random() ! {{ random("foobar") }} => f/o/b/a/r ! {{ random([1, 2, 3]) }} => 1/2/3 ! {{ random(10) }} => 1/2/3/4/5/6/7/8/9/10
  59. 59. range() ! Creates a range of numbers as an array. ! {% set r = range(1, 5) %} => [1, 2, 3, 4, 5] ! {% set r = [1..5] %} => [1, 2, 3, 4, 5]
  60. 60. dump() ! Outputs information about a given variable. Helpful when debugging. ! {{ dump(foo) }} ! (In Craft, dump() is only available in Dev Mode.)
  61. 61. Conditionals
  62. 62. You can prevent certain parts of your template from executing unless a certain condition is met by wrapping it in conditional tags.
  63. 63. Conditionals always open with an {% if %} tag, and close with an {% endif %} tag. ! {% if user %} <p>Hey there handsome!</p> {% endif %}
  64. 64. You can also specify template code to be executed if the condition doesn’t pass, using the {% else %} tag. ! {% if user %} <p>Hey there handsome!</p> {% else %} <p>Have we met?</p> {% endif %}
  65. 65. There’s also an {% elseif %} tag if you need fallback conditions. ! {% if user %} <p>Hey there handsome!</p> {% elseif username %} <p>Is this {{ username }}?</p> {% else %} <p>Have we met?</p> {% endif %}
  66. 66. Conditionals can be nested. ! {% if user %} {% if user.male %} <p>Hey there handsome!</p> {% else %} <p>Hey pretty lady!</p> {% endif %} {% elseif username %} <p>Is this {{ username }}?</p> {% else %} <p>Have we met?</p> {% endif %}
  67. 67. A single condition can be made up of multiple expressions joined together with “and” or “or”. ! {% if foo and bar %} ! {% if foo or bar %}
  68. 68. You can negate a condition by typing “not” before it. ! {% if not foo %}
  69. 69. You can also group expressions together using parentheses. ! {% if foo and ( foo == "foo" or foo == "bar" ) %}
  70. 70. Tests
  71. 71. Tests are little conditional helpers. They don’t have to be used within conditionals, but usually are.
  72. 72. To write a test, add either “is” or “is not” after a variable, followed by the test name. ! {% if foo is defined %} ! {% if foo is not empty %}
  73. 73. Twig comes with a few tests built-in. ! is constant is defined is divisible by is empty is even is iterable is null is odd is same as
  74. 74. is defined ! Tests whether a variable exists at all, without Twig getting mad at you if it’s not. ! {% if foo is defined %}
  75. 75. is divisible by ! Tests whether a number is divisible by another. ! {% if 5 is divisible by(2) %} ! That’s similar to writing: ! {% if 5 % 2 == 0 %}
  76. 76. is even & is odd ! Tests whether a number is even/odd. ! {% if 5 is even %} ! {% if 5 is odd %}
  77. 77. is empty ! Tests whether a variable is “empty”. ! {% if foo is not empty %} ! That’s the same as writing: ! {% if foo %}
  78. 78. is same as ! Tests whether two variables have the same *type*. ! {% if 5 == "5" %} => true ! {% if 5 is same as "5" %} => false
  79. 79. Working with Arrays and Objects
  80. 80. Arrays and objects both contain multiple values.
  81. 81. Arrays contain values in a specific order, and have an inherent numerical index. ! {% set arr = ['a','b','c'] %} ! {{ arr[0] }} => 'a' {{ arr[1] }} => 'b' {{ arr[2] }} => 'c'
  82. 82. Objects contain key-value pairs, and the order is generally less important. ! {% set obj = { foo: "Foo", bar: "Bar" } %} ! {{ obj.foo }} => "Foo" {{ obj.bar }} => "Bar"
  83. 83. You can merge arrays together with the ‘merge’ filter. ! {% set a1 = ['a','b'] %} {% set a2 = ['c','d'] %} {% set a3 = a1|merge(a2) %} ! => ['a','b','c','d']
  84. 84. You can merge objects together too. ! {% set o1 = {foo: "Foo"} %} {% set o2 = {bar: "Bar"} %} {% set o3 = o1|merge(o2) %} ! => {foo: "Foo", bar: "Bar"}
  85. 85. You can grab part of an array with the “slice” filter. ! {% set a1 = ['a','b','c'] %} {% set a2 = a1|slice(0, 2) %} ! => ['a','b'] ! A shortcut is also available: ! {% set a2 = a1[0:2] %}
  86. 86. Looping through
 arrays and objects
  87. 87. You can loop through arrays with the {% for %} tag. ! The syntax is: ! {% for itemname in myarray %} ... {% endfor %} ! The 2nd param (“itemname”) is whatever you want to call each item within the array.
  88. 88. Example 1: Age field ! <select name="age"> {% for age in [0..150] %} <option> {{ age }}</option> {% endfor %} </select>
  89. 89. Example 2: Exp. Year field ! {% set y1 = now.year %} {% set y2 = y1 + 10 %} ! <select name="exp_year"> {% for year in [y1..y2] %} <option> {{ year }}</option> {% endfor %} </select>
  90. 90. Example 3: Navigation ! {% set nav = [ { title: "Home", uri: "" }, { title: "About", uri: "about" } ] %} ! <nav> {% for item in nav %} <a href="{{ url(item.uri) }}"> {{ item.title }}</a> {% endfor %} </nav>
  91. 91. DRY Templating
  92. 92. Includes
  93. 93. Templates can include others. Include Template Parent Template Parent Template
  94. 94. Use the {% include %} tag to include another template wherever the tag is placed. ! {% include "some/template" %}
  95. 95. By default, any variables available to the “parent” template will also be available in the included template. ! {% set foo = "foo" %} {% include "myinclude" %} ! myinclude.html: {{ foo }} => foo
  96. 96. You can define additional variables just for the included template using the “with” param. ! {% include "myinclude" with { foo: "foo" } %} ! some/template.html: {{ foo }} => foo
  97. 97. To *only* pass certain variables to the included template, use the “only” param. ! {% set foo = "foo" %} {% include "myinclude" with { bar: "bar" } only %} ! some/template.html: {% if foo is defined %} => false
  98. 98. Extending Templates
  99. 99. A template can extend another, overriding parts of it (called “blocks”). Parent Template Block Child Template Child Template
  100. 100. Define overridable areas in the parent template using the {% block %} tag. ! <body> {% block body %} <p>Default content</p> {% endblock %} </body>
  101. 101. Use the {% extends %} tag within your child template to tell it which template it extends. ! {% extends "layout" %}
  102. 102. Override the parent’s blocks by creating new blocks in the child template with the same name. ! {% extends "layout" %} ! {% block body %} <p>Override content</p> {% endblock %}
  103. 103. You can output the parent block’s content using the parent() function. ! {% extends "layout" %} ! {% block body %} {{ parent() }} <p>Additional content</p> {% endblock %}
  104. 104. Multi-level inheritance is totally possible.
  105. 105. Embedding Templates
  106. 106. Twig also lets you “embed” other templates, which is similar to including them, except you get to override the child template’s blocks in the process.
  107. 107. Use the {% embed %} tag to embed another template. ! {% embed "promo" %} {% block body %} {{ parent() }} <span class="ribbon"> 50% off! </span> {% endblock %} {% endembed %}
  108. 108. Macros
  109. 109. Macros are like includes that are defined right within another template.
  110. 110. Use the {% macro %} tag to define them. ! {% macro errors(list) %} {% if list|length %} <ul class="errors"> {% for error in list %} <li>{{ error }}</li> {% endfor %} </ul> {% endif %} {% endmacro %}
  111. 111. You must import macros before you can use them, with either {% from %} or {% import %}. ! {% from _self import errors %} {{ errors(entry.allErrors) }} ! ! {% import _self as m %} {{ m.errors(entry.allErrors) }}
  112. 112. You can import macros from other templates, too. ! {% from "macros" import errors %} {{ errors(entry.allErrors) }} ! ! {% import "macros" as m %} {{ m.errors(entry.allErrors) }} !
  113. 113. The End.

×