Skip to content

Commit 151acdf

Browse files
committed
Merge branch 'dev'
2 parents 0bed83b + ed1cbdd commit 151acdf

File tree

6 files changed

+618
-35
lines changed

6 files changed

+618
-35
lines changed

README.md

Lines changed: 311 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,324 @@ The Twig PatternEngine allows you to use [Twig](http://twig.sensiolabs.org) as t
44

55
## Installation
66

7-
Pattern Lab PHP uses [Composer](https://getcomposer.org/) to manage project dependencies. To install the Twig PatternEngine:
7+
The Twig PatternEngine comes pre-installed with the [Pattern Lab Standard Edition for Twig](https://github.com/pattern-lab/edition-php-twig-standard). Please start there for all your Twig needs.
8+
9+
### Composer
10+
11+
Pattern Lab PHP uses [Composer](https://getcomposer.org/) to manage project dependencies with Pattern Lab Editions. To add the Twig PatternEngine to the dependencies list for your Edition you can type the following in the command line at the base of your project:
812

913
composer require pattern-lab/patternengine-twig
1014

11-
## Using Twig Macros
15+
See Packagist for [information on the latest release](https://packagist.org/packages/pattern-lab/patternengine-twig).
16+
17+
## Overview
18+
19+
This document is broken into three parts:
20+
21+
* [Working with Patterns and Twig](#working-with-patterns-and-twig)
22+
* [Extending Twig Further](#extending-twig-further)
23+
* [Available Loaders for Plugin Developers](#available-loaders)
24+
25+
## Working with Patterns and Twig
26+
27+
Twig provides access to two features that may help you extend your patterns, [macros](http://twig.sensiolabs.org/doc/templates.html#macros) and layouts via[template inheritance](http://twig.sensiolabs.org/doc/templates.html#template-inheritance). The Twig PatternEngine also supports the [pattern partial syntax](http://patternlab.io/docs/pattern-including.html) to make including one pattern within another very easy.
28+
29+
* [Pattern includes](#pattern-includes)
30+
* [Macros](#macros)
31+
* [Template inheritance](#template-inheritance)
32+
33+
### Pattern includes
34+
35+
Pattern includes take advantage of the [pattern partial syntax](http://patternlab.io/docs/pattern-including.html) as a shorthand for referencing patterns from across the system without needing to rely on absolute paths. The format:
36+
37+
```
38+
{% include "[patternType]-[patternName]" }}
39+
```
40+
41+
For example, let's say we wanted to include the following pattern in a molecule:
42+
43+
```
44+
source/_patterns/00-atoms/03-images/02-landscape-16x9.twig
45+
```
46+
47+
The **pattern type** is _atoms_ (from `00-atoms`) and the **pattern name** is _landscape-16x9_ from (from `02-landscape-16x9.twig`). Pattern sub-types are never used in this format and any digits for re-ordering are dropped. The shorthand partial syntax for this pattern would be:
48+
49+
```
50+
{% include "atoms-landscape-16x9" %}
51+
```
52+
53+
### Macros
54+
55+
The requirements for using macros with Pattern Lab:
56+
57+
* Files must go in `source/_macros`
58+
* Files must have the extension `.macro.twig` (_this can be modified in the config_)
59+
* The filename will be used as the base variable name in Twig templates
60+
61+
**Please note:** ensure that there is no overlap between the keys for your macros and the keys for your data attributes. A macro with the name `forms.macro.twig` will conflict with a root key with the name `forms` in your JSON/YAML. Both are accessed via `{{ forms }}` in Twig.
62+
63+
An example of a simple macro called `forms.macro.twig` in `source/_macros`:
1264

13-
The Twig PatternEngine will automatically find and load Twig macros making them available to every pattern. To use this feature add your `.macro` files to `source/_macros/`. If your macro file is called `forms.macro` and it has a macro called `input()` you'd access it in your Twig templates as `{{ forms.input() }}`. **Please note:** ensure that there is no overlap between the keys for your macros and the keys for your data attributes.
65+
```twig
66+
{% macro input(name) %}
67+
<input type="radio" name="{{ name }}" value="Dave" /> {{ name }}
68+
{% endmacro %}
69+
```
70+
71+
Would be used like this in a pattern:
72+
73+
```twig
74+
{{ forms.input("First name") }}
75+
```
76+
77+
### Template inheritance
78+
79+
The requirements for using template inheritance with Pattern Lab:
80+
81+
* Files must go in `source/_layouts`
82+
* Files must have the extension `.twig`
83+
* The filename will be used as the reference in the `extends` tag
84+
85+
An example of a simple layout called `base.twig` in `source/_layouts`:
86+
87+
```twig
88+
<!DOCTYPE html>
89+
<html>
90+
<head>
91+
{% block head %}
92+
<link rel="stylesheet" href="style.css" />
93+
<title>{% block title %}{% endblock %} - My Webpage</title>
94+
{% endblock %}
95+
</head>
96+
<body>
97+
<div id="content">{% block content %}{% endblock %}</div>
98+
<div id="footer">
99+
{% block footer %}
100+
&copy; Copyright 2011 by <a href="http://domain.invalid/">you</a>.
101+
{% endblock %}
102+
</div>
103+
</body>
104+
</html>
105+
```
106+
107+
Would be used like this in a pattern:
108+
109+
```twig
110+
{% extends "base.twig" %}
111+
112+
{% block title %}Index{% endblock %}
113+
{% block head %}
114+
{{ parent() }}
115+
<style type="text/css">
116+
.important { color: #336699; }
117+
</style>
118+
{% endblock %}
119+
{% block content %}
120+
<h1>Index</h1>
121+
<p class="important">
122+
Welcome on my awesome homepage.
123+
</p>
124+
{% endblock %}
125+
```
126+
127+
## Extending Twig Further
128+
129+
Twig comes with a number of ways to extend the underlying template parser. You can you can add [extra tags](http://twig.sensiolabs.org/doc/advanced.html#tags), [filters](http://twig.sensiolabs.org/doc/advanced.html#filters), [tests](http://twig.sensiolabs.org/doc/advanced.html#tests), and [functions](http://twig.sensiolabs.org/doc/advanced.html#functions). The Twig PatternEngine tries to simplify these extensions by allowing you to create files in specific folders and then auto-load the extensions for you. Learn more about:
130+
131+
* [Filters](#filters)
132+
* [Functions](#functions)
133+
* [Tags](#tags)
134+
* [Tests](#tests)
14135

15-
## Enabling `dump()`
136+
You can also:
16137

17-
To use `dump()` set `twigDebug` in `configs/config.yml` to `true`.
138+
* [Enable `dump()`](#enable-dump)
139+
* [Modify the Default Date and Interval Formats](#modify-the-default-date-and-interval-formats)
18140

19-
## Modifying the Default Date and Interval Format
141+
### Filters
20142

21-
You can modify the default date and interval formats for Twig by editing the `twigDefaultDateFormat` and `twigDefaultIntervalFormat` in `configs/config.yml`. Set them to an empty string to use Twig's default formats. **Please note:** both must be set for this feature to work.
143+
The requirements for using filters with Pattern Lab:
144+
145+
* Files must go in `source/_twig-components/filters`
146+
* Files must have the extension `.filter.twig` (_this can be modified in the config_)
147+
* The filter **must** set the variable `$filter`
148+
* Only one filter per file (_e.g. can only set `$filter` once per file_)
149+
150+
An example function called `rot13.filter.twig` in `source/_twig-components/filters`:
151+
152+
```php
153+
<?php
154+
155+
$filter = new Twig_SimpleFilter('rot13', function ($string) {
156+
return str_rot13($string);
157+
});
158+
159+
?>
160+
```
161+
162+
Would be used like this in a pattern:
163+
164+
```twig
165+
{{ bar|rot13 }}
166+
```
167+
168+
### Functions
169+
170+
The requirements for using functions with Pattern Lab:
171+
172+
* Files must go in `source/_twig-components/functions`
173+
* Files must have the extension `.function.twig` (_this can be modified in the config_)
174+
* The function **must** set the variable `$function`
175+
* Only one function per file (_e.g. can only set `$function` once per file_)
176+
177+
An example function called `boo.function.twig` in `source/_twig-components/functions`:
178+
179+
```php
180+
<?php
181+
182+
$function = new Twig_SimpleFunction('boo', function ($string) {
183+
return $string." boo! ";
184+
});
185+
186+
?>
187+
```
188+
189+
Would be used like this in a pattern:
190+
191+
```twig
192+
{{ boo("ghost says what?") }}
193+
```
194+
195+
### Tests
196+
197+
The requirements for using tests with Pattern Lab:
198+
199+
* Files must go in `source/_twig-components/tests`
200+
* Files must have the extension `.test.twig` (_this can be modified in the config_)
201+
* The test **must** set the variable `$test`
202+
* Only one test per file (_e.g. can only set `$test` once per file_)
203+
204+
An example of a simple test called `red.test.twig` in `source/_twig-components/tests`:
205+
206+
```php
207+
<?php
208+
209+
$test = new Twig_SimpleTest('red', function ($value) {
210+
211+
if (isset($value["color"]) && $value["color"] == 'red') {
212+
return true;
213+
}
214+
215+
return false;
216+
});
217+
218+
?>
219+
```
220+
221+
Would be used like this in a pattern:
222+
223+
```twig
224+
{% if shirt is red %}
225+
Why did I ever sign-up with Starfleet?
226+
{% endif %}
227+
```
228+
229+
Where the JSON for the data to set `shirt` would be:
230+
231+
```json
232+
"shirt": {
233+
"color": "red"
234+
}
235+
```
236+
237+
**Reminder:** all data in Pattern Lab is stored as an array and _not_ as an object. So `$object->attribute` won't work in tests.
238+
239+
### Tags
240+
241+
The requirements for using tags with Pattern Lab:
242+
243+
* Files must go in `source/_twig-components/tags`
244+
* Files must have the extension `.tag.twig` (_this can be modified in the config_)
245+
* The filename **must** be reflected in class names. (e.g. `Project_{filename}_Node` and `Project_{filename}_TokenParser`)
246+
* Only one tag per file
247+
248+
Tags are the most complicated extension to set-up with Pattern Lab. Three steps are needed to define a new tag in Twig:
249+
250+
* Defining a Token Parser class (_responsible for parsing the template code_)
251+
* Defining a Node class (_responsible for converting the parsed code to PHP_)
252+
* Registering the tag.
253+
254+
Pattern Lab takes care of the registering for you based on the file name.
255+
256+
An example of a simple tag called `setdupe.tag.twig` in `source/_twig-components/tags` that mimics the default `set` tag. Please note all of the locations where class names incorporate the filename, `setdupe`.
257+
258+
```php
259+
<?php
260+
261+
// these files are loaded three times and we can't re-set a class
262+
if (!class_exists("Project_setdupe_Node")) {
263+
264+
class Project_setdupe_Node extends Twig_Node {
265+
266+
public function __construct($name, Twig_Node_Expression $value, $line, $tag = null) {
267+
parent::__construct(array('value' => $value), array('name' => $name), $line, $tag);
268+
}
269+
270+
public function compile(Twig_Compiler $compiler) {
271+
$compiler
272+
->addDebugInfo($this)
273+
->write('$context[\''.$this->getAttribute('name').'\'] = ')
274+
->subcompile($this->getNode('value'))
275+
->raw(";\n");
276+
}
277+
278+
}
279+
280+
}
281+
282+
// these files are loaded three times and we can't re-set a class
283+
if (!class_exists("Project_setdupe_TokenParser")) {
284+
285+
class Project_setdupe_TokenParser extends Twig_TokenParser {
286+
287+
public function parse(Twig_Token $token) {
288+
289+
$parser = $this->parser;
290+
$stream = $parser->getStream();
291+
292+
$name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
293+
$stream->expect(Twig_Token::OPERATOR_TYPE, '=');
294+
$value = $parser->getExpressionParser()->parseExpression();
295+
$stream->expect(Twig_Token::BLOCK_END_TYPE);
296+
297+
return new Project_setdupe_Node($name, $value, $token->getLine(), $this->getTag());
298+
}
299+
300+
public function getTag() {
301+
return 'setdupe';
302+
}
303+
304+
}
305+
306+
}
307+
308+
?>
309+
```
310+
311+
Would be used like this in a pattern:
312+
313+
```
314+
{% setdupe name = "Ziggy" %}
315+
{{ name }}
316+
```
317+
318+
### Enable `dump()`
319+
320+
To use `dump()` set `twigDebug` in `config/config.yml` to `true`.
321+
322+
### Modify the Default Date and Interval Formats
323+
324+
You can modify the default date and interval formats for Twig by editing the `twigDefaultDateFormat` and `twigDefaultIntervalFormat` in `config/config.yml`. Set them to an empty string to use Twig's default formats. **Please note:** both must be set for this feature to work.
22325

23326
## Available Loaders
24327

@@ -59,3 +362,4 @@ $patternLoaderClass = $patternEngineBasePath."\Loaders\PatternLoader";
59362
$patternLoader = new $patternLoaderClass($options);
60363
$code = $patternLoader->render(array("pattern" => $patternContent, "data" => $data));
61364
print $output; // outputs the given pattern
365+
```

composer.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,11 @@
3535
"twigDebug": false,
3636
"twigDefaultDateFormat": "",
3737
"twigDefaultIntervalFormat": "",
38-
"twigMacroExt": "macro"
38+
"twigMacroExt": "macro.twig",
39+
"twigFilterExt": "filter.twig",
40+
"twigFunctionExt": "function.twig",
41+
"twigTagExt": "tag.twig",
42+
"twigTestExt": "test.twig"
3943
}
4044
]
4145
}

src/PatternLab/PatternEngine/Twig/Loaders/FilesystemLoader.php

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,38 @@ class FilesystemLoader extends Loader {
2323
*/
2424
public function __construct($options = array()) {
2525

26-
// set-up the loader
26+
// set-up default vars
2727
$twigDebug = Config::getOption("twigDebug");
28-
$macroPath = Config::getOption("sourceDir").DIRECTORY_SEPARATOR."_macros";
29-
$twigLoader = new \Twig_Loader_Filesystem(array($options["templatePath"],$options["partialsPath"],$macroPath));
28+
29+
// set-up the paths to be searched for templates
30+
$dirPaths = array();
31+
$dirPaths[] = $options["templatePath"];
32+
$dirPaths[] = $options["partialsPath"];
33+
34+
// see if source/_macros exists. if so add it to be searchable
35+
$macrosPath = Config::getOption("sourceDir").DIRECTORY_SEPARATOR."_macros";
36+
if (is_dir($macrosPath)) {
37+
$dirPaths[] = $macrosPath;
38+
}
39+
40+
// see if source/_layouts exists. if so add it to be searchable
41+
$layoutsPath = Config::getOption("sourceDir").DIRECTORY_SEPARATOR."_layouts";
42+
if (is_dir($layoutsPath)) {
43+
$dirPaths[] = $layoutsPath;
44+
}
45+
46+
// set-up Twig
47+
$twigLoader = new \Twig_Loader_Filesystem($dirPaths);
3048
$this->instance = new \Twig_Environment($twigLoader, array("debug" => $twigDebug));
3149

32-
// customize the loader
50+
// customize Twig
51+
$this->instance = TwigUtil::loadFilters($this->instance);
52+
$this->instance = TwigUtil::loadFunctions($this->instance);
53+
$this->instance = TwigUtil::loadTags($this->instance);
54+
$this->instance = TwigUtil::loadTests($this->instance);
3355
$this->instance = TwigUtil::loadDateFormats($this->instance);
3456
$this->instance = TwigUtil::loadDebug($this->instance);
35-
$this->instance = TwigUtil::loadMacros($this->instance, "filesystem");
57+
$this->instance = TwigUtil::loadMacros($this->instance);
3658

3759
}
3860

0 commit comments

Comments
 (0)