Frontend Vol. 2
Mantas Kaveckas @ .NFQ Academy
Twig
Twig is the flexible, fast, and secure template engine for PHP.
Twig
Key Features
- Fast: Twig compiles templates down to plain optimized PHP code.
- Secure: Twig has a sandbox mode to evaluate untrusted template code.
- Flexible: Twig is allows the developer to define their own custom tags and filters.
Twig
Prerequisites
Twig needs at least PHP 7.0.0 to run.
Twig
Installation
composer require "twig/twig:^2.0"
Twig
Basic API Usage
require_once '/path/to/vendor/autoload.php';
$loader = new Twig_Loader_Array(array(
'index' => 'Hello {{ name }}!',
));
$twig = new Twig_Environment($loader);
echo $twig->render('index', array('name' => 'Fabien'));
Using Twig_Loader_Array
Twig
Basic API Usage
$loader = new Twig_Loader_Filesystem('/path/to/templates');
$twig = new Twig_Environment($loader, array(
'cache' => '/path/to/compilation_cache',
));
echo $twig->render('index.html', array('name' => 'Fabien'));
Using Twig_Loader_Filesystem
Twig
Twig is the default template engine for Symfony.
Twig
Synopsis
A template is simply a text file. It can generate any text-based format (HTML, XML, CSV, etc.). It doesn't have a specific extension.
Twig
Synopsis
A template contains variables or expressions, which get replaced with values when the template is evaluated, and tags, which control the logic of the template.
Twig
Synopsis
<!DOCTYPE html>
<html>
<head>
<title>My Webpage</title>
</head>
<body>
<ul id="navigation">
{% for item in navigation %}
<li><a href="{{ item.href }}">{{ item.caption }}</a></li>
{% endfor %}
</ul>
<h1>My Webpage</h1>
{{ a_variable }}
</body>
</html>
Minimal template
Twig
IDEs Integration
Many IDEs support syntax highlighting and auto-completion for Twig:
- PhpStorm (native as of 2.1)
- Eclipse via the Twig plugin
- Sublime Text via the Twig bundle
- Many more...
Twig
Variables
The application passes variables to the templates for manipulation in the template.
Twig
Variables
{{ foo.bar }}
{{ foo['bar'] }}
You can use a dot to access attributes of a variable (methods or properties of a PHP object, or items of a PHP array), or "subscript" syntax:
Twig
Setting Variables
{% set foo = 'foo' %}
{% set foo = [1, 2] %}
{% set foo = {'foo': 'bar'} %}
Variable assignments use the set tag:
Twig
Filters
The following example removes all HTML tags from the name and title-cases it:
{{ name|striptags|title }}
Twig
Filters
Filters that accept arguments have parentheses around the arguments. This example will join a list by commas:
{{ list|join(', ') }}
Twig
Filters
To apply a filter on a section of code, wrap it in the filter tag:
{% filter upper %}
This text becomes uppercase
{% endfilter %}
Twig
Functions
Functions can be called to generate content. Functions are called by their name followed by parentheses and may have arguments.
{% for i in range(0, 3) %}
{{ i }},
{% endfor %}
Twig
Control Structure
Conditionals (i.e. if/elseif/else), for-loops, as well as things like blocks.
Twig
Control Structure
For example, to display a list of users provided in a variable called users, use the for tag:
<h1>Members</h1>
<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% endfor %}
</ul>
Twig
Control Structure
The if tag can be used to test an expression:
{% if users|length > 0 %}
<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% endfor %}
</ul>
{% endif %}
Twig
Comments
{# note: disabled template because we no longer use this
{% for user in users %}
...
{% endfor %}
#}
No magic here:
Twig
Including other Templates
{{ include('sidebar.html') }}
The include function is useful to include a template and return the rendered content of that template into the current one:
Twig
Including other Templates
{% for box in boxes %}
{{ include('render_box.html') }}
{% endfor %}
Twig
Template Inheritance
The most powerful part of Twig is template inheritance.
Twig
Template Inheritance
Template inheritance allows you to build a base "skeleton" template that contains all the common elements of your site and defines blocks that child templates can override.
Twig
Template Inheritance
<!DOCTYPE html>
<html>
<head>
{% block head %}
<link rel="stylesheet" href="style.css" />
<title>{% block title %}{% endblock %} - My Webpage</title>
{% endblock %}
</head>
<body>
<div id="content">{% block content %}{% endblock %}</div>
<div id="footer">
{% block footer %}
© Copyright 2011 by <a href="http://domain.invalid/">you</a>.
{% endblock %}
</div>
</body>
</html>
base.html
Twig
Template Inheritance
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ parent() }}
<style type="text/css">
.important { color: #336699; }
</style>
{% endblock %}
{% block content %}
<h1>Index</h1>
<p class="important">
Welcome to my awesome homepage.
</p>
{% endblock %}
child.html
Twig
Template Inheritance
- The extends tag is the key here. It tells the template engine that this template "extends" another template.
- When the template system evaluates this template, first it locates the parent.
- The extends tag should be the first tag in the template.
Twig
Template Inheritance
It's possible to render the contents of the parent block by using the parent function:
{% block sidebar %}
<h3>Table Of Contents</h3>
...
{{ parent() }}
{% endblock %}
Twig
Macros
- Macros are comparable with functions in regular programming languages.
- They are useful to reuse often used HTML fragments to not repeat yourself.
Twig
Macros
{# forms.html #}
{% macro input(name, value, type, size) %}
<input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
{% endmacro %}
{# page.html #}
{% import "forms.html" as forms %}
<p>{{ forms.input('username') }}</p>
JavaScript
The world's most misunderstood programming language.
JavaScript
Building Blocks
- Number
- String
- Boolean
- Symbol (new in ES2015)
-
Object
- Function
- Array
- Date
- RegExp
- null
- undefined
JavaScript
Numbers
- Numbers in JavaScript are "double-precision 64-bit format IEEE 754 values".
- There's no such thing as an integer in JavaScript.
JavaScript
Numbers
Be a little careful with your arithmetic if you're used to math in C or Java:
0.1 + 0.2 == 0.30000000000000004;
JavaScript
Numbers
Although the standard arithmetic operators are supported:
Math.sin(3.5);
var circumference = 2 * Math.PI * r;
JavaScript
Numbers
You can convert a string to an integer using the built-in parseInt() function:
parseInt('123', 10); // 123
parseInt('010', 10); // 10
JavaScript
Numbers
If you want to convert a binary number to an integer, just change the base:
parseInt('11', 2); // 3
JavaScript
Numbers
A special value called NaN (short for "Not a Number") is returned if the string is non-numeric:
parseInt('hello', 10); // NaN
JavaScript
Numbers
You can test for NaN using the built-in isNaN() function:
isNaN(NaN); // true
JavaScript
Strings
- Strings in JavaScript are sequences of Unicode characters.
- More accurately, they are sequences of UTF-16 code units.
- Each code unit is represented by a 16-bit number. Each Unicode character is represented by either 1 or 2 code units.
JavaScript
Strings
To find the length of a string (in code units), access its length property:
'hello'.length; // 5
JavaScript
Strings
So strings like objects too? They have methods as well that allow you to manipulate the string and access information about the string:
'hello'.charAt(0); // "h"
'hello, world'.replace('hello', 'goodbye'); // "goodbye, world"
'hello'.toUpperCase(); // "HELLO"
JavaScript
Variables
New variables in JavaScript are declared using one of three keywords: let, const, or var.
JavaScript
Variables
let allows you to declare block-level variables. The declared variable is available from the function block it is enclosed in.
let a;
let name = 'Simon';
// myLetVariable is *not* visible out here
for (let myLetVariable = 0; myLetVariable < 5; myLetVariable++) {
// myLetVariable is only visible in here
}
// myLetVariable is *not* visible out here
JavaScript
Variables
const allows you to declare variables whose values are never intended to change. The variable is available from the function block it is declared in.
const Pi = 3.14; // variable Pi is set
Pi = 1; // will throw an error because you cannot change a constant variable.
JavaScript
Variables
var is the most common declarative keyword. A variable declared with the var keyword is available from the function block it is declared in.
var a;
var name = 'Simon';
// myVarVariable *is* visible out here
for (var myVarVariable = 0; myVarVariable < 5; myVarVariable++) {
// myVarVariable is visible to the whole function
}
// myVarVariable *is* visible out here
JavaScript
Operators
x += 5;
x = x + 5;
'hello' + ' world'; // "hello world"
'3' + 4 + 5; // "345"
3 + 4 + '5'; // "75"
123 == '123'; // true
1 == true; // true
123 === '123'; // false
1 === true; // false
JavaScript
Control structures
var name = 'kittens';
if (name == 'puppies') {
name += ' woof';
} else if (name == 'kittens') {
name += ' meow';
} else {
name += '!';
}
name == 'kittens meow';
Text
Conditional statements are supported by if and else; you can chain them together if you like:
JavaScript
Control structures
while (true) {
// an infinite loop!
}
var input;
do {
input = get_input();
} while (inputIsNotValid(input));
Text
JavaScript
Control structures
for (var i = 0; i < 5; i++) {
// Will execute 5 times
}
for (let value of array) {
// do something with value
}
for (let property in object) {
// do something with object property
}
JavaScript
Control structures
var name = o && o.getName();
var name = otherName || 'default';
The && and || operators use short-circuit logic, which means whether they will execute their second operand is dependent on the first.
JavaScript
Control structures
var allowed = (age > 18) ? 'yes' : 'no';
JavaScript has a ternary operator for conditional expressions:
JavaScript
Control structures
switch (action) {
case 'draw':
drawIt();
break;
case 'eat':
eatIt();
break;
default:
doNothing();
}
The switch statement can be used for multiple branches based on a number or string:
JavaScript
Objects
JavaScript objects can be thought of as simple collections of name-value pairs. As such, they are similar to:
- Dictionaries in Python.
- Hashes in Perl and Ruby.
- Hash tables in C and C++.
- HashMaps in Java.
- Associative arrays in PHP.
JavaScript
Objects
There are two basic ways to create an empty object:
// Constructor syntax
var obj = new Object();
// Object literal syntax
var obj = {};
JavaScript
Objects
Object literal syntax can be used to initialize an object in its entirety:
var obj = {
name: 'Carrot',
'for': 'Max',
details: {
color: 'orange',
size: 12
}
}
JavaScript
Objects
Attribute access can be chained together:
obj.details.color; // orange
obj['details']['size']; // 12
JavaScript
Objects
The following example creates an object prototype - Person, and instance of that prototype - You.
function Person(name, age) {
this.name = name;
this.age = age;
}
// Define an object
var You = new Person('You', 24);
// We are creating a new person named "You"
// (that was the first parameter, and the age..)
JavaScript
Objects
Once created, an object's properties can again be accessed in one of two ways:
obj.name = 'Simon';
var name = obj.name;
// And...
obj['name'] = 'Simon';
var name = obj['name'];
JavaScript
Arrays
- Arrays in JavaScript are actually a special type of object.
- They work very much like regular objects (numerical properties can naturally be accessed only using [] syntax) but they have one magic property called 'length'.
- This is always one more than the highest index in the array.
JavaScript
Arrays
One way of creating arrays is as follows:
var a = new Array();
a[0] = 'dog';
a[1] = 'cat';
a[2] = 'hen';
a.length; // 3
A more convenient notation is to use an array literal:
var a = ['dog', 'cat', 'hen'];
a.length; // 3
JavaScript
Arrays
Note that array.length isn't necessarily the number of items in the array. Consider the following:
var a = ['dog', 'cat', 'hen'];
a[100] = 'fox';
a.length; // 101
JavaScript
Arrays
If you query a non-existent array index, you'll get a value of undefined returned:
typeof a[90]; // undefined
JavaScript
Arrays
If you take the above into account, you can iterate over an array using the following:
for (var i = 0; i < a.length; i++) {
// Do something with a[i]
}
JavaScript
Arrays
You can iterate over an array using a for...in loop. Note that if someone added new properties to Array.prototype, they will also be iterated over by this loop. Therefore this method is "not" recommended.
JavaScript
Arrays
Another way of iterating over an array that was added with ECMAScript 5 is forEach():
['dog', 'cat', 'hen'].forEach(function(currentValue, index, array) {
// Do something with currentValue or array[index]
});
JavaScript
Arrays
If you want to append an item to an array simply do it like this:
a.push(item);
JavaScript
Arrays
Arrays come with a number of methods. See also the full documentation for array methods.
- a.toString()
- a.concat(item1[, item2[, ...[, itemN]]])
- a.join(sep)
- a.pop()
- a.reverse()
- a.slice(start[, end])
- etc...
JavaScript
Functions
Along with objects, functions are the core component in understanding JavaScript. The most basic function couldn't be much simpler:
function add(x, y) {
var total = x + y;
return total;
}
JavaScript
Functions
The named parameters turn out to be more like guidelines than anything else. You can call a function without passing the parameters it expects, in which case they will be set to undefined.
add(); // NaN
// You can't perform addition on undefined
JavaScript
Functions
You can also pass in more arguments than the function is expecting:
add(2, 3, 4); // 5
// added the first two; 4 was ignored
JavaScript
Functions
That may seem a little silly, but functions have access to an additional variable inside their body called arguments, which is an array-like object holding all of the values passed to the function.
function add() {
var sum = 0;
for (var i = 0, j = arguments.length; i < j; i++) {
sum += arguments[i];
}
return sum;
}
add(2, 3, 4, 5); // 14
JavaScript
Functions
This is pretty useful, but it does seem a little verbose. To diminish this code a bit more we can look at substituting the use of the arguments array through Spread syntax.
function avg(...args) {
var sum = 0;
for (let value of args) {
sum += value;
}
return sum / args.length;
}
avg(2, 3, 4, 5); // 3.5
JavaScript
Functions
The avg() function takes a comma separated list of arguments — but what if you want to find the average of an array? You could just rewrite the function as follows:
function avgArray(arr) {
var sum = 0;
for (var i = 0, j = arr.length; i < j; i++) {
sum += arr[i];
}
return sum / arr.length;
}
avgArray([2, 3, 4, 5]); // 3.5
JavaScript
Functions
JavaScript lets you create anonymous functions.
var avg = function() {
var sum = 0;
for (var i = 0, j = arguments.length; i < j; i++) {
sum += arguments[i];
}
return sum / arguments.length;
};
JavaScript
Functions
It's extremely powerful, as it lets you put a full function definition anywhere that you would normally put an expression. This enables all sorts of clever tricks. Here's a way of "hiding" some local variables:
var a = 1;
var b = 2;
(function() {
var b = 3;
a += b;
})();
a; // 4
b; // 2
JavaScript
Functions
JavaScript allows you to call functions recursively. This is particularly useful for dealing with tree structures, such as those found in the browser DOM.
function countChars(elm) {
if (elm.nodeType == 3) { // TEXT_NODE
return elm.nodeValue.length;
}
var count = 0;
for (var i = 0, child; child = elm.childNodes[i]; i++) {
count += countChars(child);
}
return count;
}
JavaScript
Functions
This highlights a potential problem with anonymous functions: how do you call them recursively if they don't have a name?
JavaScript
Functions
JavaScript lets you name function expressions. You can use named IIFEs (Immediately Invoked Function Expressions) as shown below:
var charsInBody = (function counter(elm) {
if (elm.nodeType == 3) { // TEXT_NODE
return elm.nodeValue.length;
}
var count = 0;
for (var i = 0, child; child = elm.childNodes[i]; i++) {
count += counter(child);
}
return count;
})(document.body);
JavaScript
Custom objects
In classic OOP, objects are collections of data and methods that operate on that data.
JavaScript
Custom objects
- JavaScript is a prototype-based language that contains no class statement, as you'd find in C++ or Java.
- Instead, JavaScript uses functions as classes.
JavaScript
Custom objects
Let's consider a person object with first and last name fields. There are two ways in which the name might be displayed: as "first last" or as "last, first".
JavaScript
Custom objects
Using the functions and objects discussed previously, we could display the data like this:
function makePerson(first, last) {
return {
first: first,
last: last
};
}
function personFullName(person) {
return person.first + ' ' + person.last;
}
function personFullNameReversed(person) {
return person.last + ', ' + person.first;
}
s = makePerson('Simon', 'Willison');
personFullName(s); // "Simon Willison"
personFullNameReversed(s); // "Willison, Simon"
JavaScript
Custom objects
This works, but it's pretty ugly. You end up with dozens of functions in your global namespace.
JavaScript
Custom objects
What we really need is a way to attach a function to an object. Since functions are objects, this is easy:
function makePerson(first, last) {
return {
first: first,
last: last,
fullName: function() {
return this.first + ' ' + this.last;
},
fullNameReversed: function() {
return this.last + ', ' + this.first;
}
};
}
s = makePerson('Simon', 'Willison');
s.fullName(); // "Simon Willison"
s.fullNameReversed(); // "Willison, Simon"
JavaScript
Custom objects
Used inside a function, this refers to the current object. What that actually means is specified by the way in which you called that function. If you called it using dot notation or bracket notation on an object, that object becomes this.
If dot notation wasn't used for the call, this refers to the global object.
JavaScript
Custom objects
Note that this is a frequent cause of mistakes. For example:
s = makePerson('Simon', 'Willison');
var fullName = s.fullName;
fullName(); // undefined undefined
JavaScript
Custom objects
When we call fullName() alone, without using s.fullName(), this is bound to the global object. Since there are no global variables called first or last we get undefined for each one.
JavaScript
Custom objects
We can take advantage of the this keyword to improve our makePerson function:
function Person(first, last) {
this.first = first;
this.last = last;
this.fullName = function() {
return this.first + ' ' + this.last;
};
this.fullNameReversed = function() {
return this.last + ', ' + this.first;
};
}
var s = new Person('Simon', 'Willison');
JavaScript
Custom objects
We have introduced another keyword: new. new is strongly related to this.
JavaScript
Custom objects
- new creates a brand new empty object.
- And then calls the function specified, with this set to that new object.
JavaScript
Custom objects
Functions that are designed to be called by new are called constructor functions. Common practice is to capitalize these functions as a reminder to call them with new.
JavaScript
Custom objects
Every time we create a person object we are creating two brand new function objects within it — wouldn't it be better if this code was shared?
function personFullName() {
return this.first + ' ' + this.last;
}
function personFullNameReversed() {
return this.last + ', ' + this.first;
}
function Person(first, last) {
this.first = first;
this.last = last;
this.fullName = personFullName;
this.fullNameReversed = personFullNameReversed;
}
JavaScript
Custom objects
We are creating the method functions only once, and assigning references to them inside the constructor. Can we do any better than that? The answer is yes:
function Person(first, last) {
this.first = first;
this.last = last;
}
Person.prototype.fullName = function() {
return this.first + ' ' + this.last;
};
Person.prototype.fullNameReversed = function() {
return this.last + ', ' + this.first;
};
JavaScript
Custom objects
Person.prototype is an object shared by all instances of Person. It forms part of a lookup chain: any time you attempt to access a property of Person that isn't set, JavaScript will check Person.prototype to see if that property exists there instead. As a result, anything assigned to Person.prototype becomes available to all instances of that constructor via the this object.
JavaScript
Custom objects
JavaScript lets you modify something's prototype at any time in your program, which means you can add extra methods to existing objects at runtime:
s = new Person('Simon', 'Willison');
s.firstNameCaps(); // TypeError on line 1: s.firstNameCaps is not a function
Person.prototype.firstNameCaps = function firstNameCaps() {
return this.first.toUpperCase();
};
s.firstNameCaps(); // "SIMON"
JavaScript
Custom objects
Interestingly, you can also add things to the prototype of built-in JavaScript objects. Let's add a method to String that returns that string in reverse:
var s = 'Simon';
s.reversed(); // TypeError on line 1: s.reversed is not a function
String.prototype.reversed = function reversed() {
var r = '';
for (var i = this.length - 1; i >= 0; i--) {
r += this[i];
}
return r;
};
s.reversed(); // nomiS
JavaScript
Custom objects
Our new method even works on string literals!
'This can now be reversed'.reversed(); // desrever eb won nac sihT
Mind === Blown.
QA & Thank You!
Twig
By Mantas Kaveckas
Twig
- 1,019