├── images
└── logos
│ └── social.png
├── LICENSE
└── README.md
/images/logos/social.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexander-schranz/twig-for-react-devs/HEAD/images/logos/social.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Alexander Schranz
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Twig for react devs
2 |
3 | An easy guide for react developers getting into PHP Twig template engine from Symfony.
4 |
5 | 
6 |
7 | ## Introduction
8 |
9 | Hello 👋,
10 |
11 | My name is Alexander Schranz ([alex_s_](https://twitter.com/alex_s_)) and I'm fulltime Webdeveloper
12 | working on the [SULU CMS](https://sulu.io/?utm_source=github&utm_medium=repository&utm_campaign=alex-twig-for-react-devs)
13 | and did based on that created a lot of websites.
14 |
15 | With this article I wanted to make it easier for React Developers
16 | getting into the Twig Syntax.
17 |
18 | Every Section will first Provide the `React JS` ⚛️ code and then the `Twig` 🌱
19 | followed by explanation about the difference between them.
20 |
21 | At the start I want to mention here that `Twig` was inspired a lot by
22 | [Jinja2 Template Engine](https://github.com/pallets/jinja)
23 | developed for the Django Python Framework, but was improved
24 | over the years with other new features by the Symfony Team.
25 |
26 | Before continue with the overview the best comparison is
27 | what `JSX` is for `React` (JS), `Twig` is for `Symfony` (PHP).
28 | So all Business Logic should live outside of it.
29 |
30 | **Table of Contents**
31 |
32 | - [Outputting a variable](#outputting-a-variable)
33 | - [Outputting raw HTML](#outputting-raw-html)
34 | - [If statements](#if-statements)
35 | - [Loops](#loops)
36 | - [Base Template](#base-template)
37 | - [Providing props to templates](#providing-props-to-templates)
38 | - [Include and override parts](#include-and-override-parts)
39 | - [Dynamic Include](#dynamic-include)
40 | - [Conclusion](#conclusion)
41 |
42 | ## Outputting a variable
43 |
44 | In the first example we will to the most simpliest thing just
45 | outputting a variable we did create in our component / template.
46 |
47 | ### Outputting a variable (React JS ⚛️)
48 |
49 | ```js
50 | // src/views/My.js
51 |
52 | import React from 'react';
53 |
54 | class My extends React.Component {
55 | render() {
56 | const title = 'My Title';
57 |
58 | return
{title}
;
59 | }
60 | }
61 | ```
62 |
63 | ### Outputting a variable (Twig 🌱)
64 |
65 | ```twig
66 | {# templates/pages/my.html.twig #}
67 |
68 | {% set title = 'My Title' %}
69 |
70 | {{ title }}
71 | ```
72 |
73 | ### Outputting a variable (Explanation)
74 |
75 | Outputting a variable in Twig and JSX is very similar instead
76 | of `{variable}` you use `{{ variable }}`. The spaces are optional
77 | but recommended by the [Twig coding standards](https://twig.symfony.com/doc/3.x/coding_standards.html).
78 |
79 | ## Outputting raw HTML
80 |
81 | There are cases where you use CKEditor or other rich text editors
82 | to create content which will provide you raw HTML code you would
83 | like to render in this case you want not that your variable you
84 | want to outputted does get escaped.
85 |
86 | ### Outputting raw HTML (React JS ⚛️)
87 |
88 | ```js
89 | // src/views/My.js
90 |
91 | import React from 'react';
92 |
93 | class My extends React.Component {
94 | render() {
95 | const someHtml = 'My Text editor content
';
96 |
97 | return ;
98 | }
99 | }
100 | ```
101 |
102 | ### Outputting raw HTML (Twig 🌱)
103 |
104 | ```twig
105 | {# templates/pages/my.html.twig #}
106 |
107 | {% set someHtml = 'My Text editor content
' %}
108 |
109 |
110 | {{ someHtml|raw }}
111 |
112 | ```
113 |
114 | ### Outputting raw HTML (Explanation)
115 |
116 | Both (`React JS` and `Twig`) escape variables by default so no HTML
117 | injected without a call of an additional prop or filter.
118 |
119 | There are in Twig other ways to disable `autoescape` over a whole
120 | content. See for this the [official Twig Docs](https://twig.symfony.com/doc/3.x/api.html#escaper-extension).
121 |
122 | I personally recommend only using the `|raw` filter todo this kind
123 | of things.
124 |
125 | ## If statements
126 |
127 | ### If statements (React JS ⚛️)
128 |
129 | ```js
130 | // src/views/My.js
131 |
132 | import React from 'react';
133 |
134 | class My extends React.Component {
135 | render() {
136 | var title = 'Test' ;
137 | const list = ['Test', 'Test 2'];
138 |
139 | // Basic equal if statement
140 | if (title == 'test') {
141 | title = 'Basic equal if statement';
142 | // Not equal if statement
143 | } else if (title != 'test') {
144 | title = 'Not equal if statement';
145 | // Typesafe if statemenet
146 | } else if (title === false) {
147 | title = 'Typesafe if statemenet';
148 | // Typesafe not if statemenet
149 | } else if (title !== false) {
150 | title = 'Typesafe not if statemenet';
151 | // In array if statemenet
152 | } else if (list.includes(title)) {
153 | title = 'In array if statemenet';
154 | // Not in array if statemenet
155 | } else if (!list.includes(title)) {
156 | title = 'Not in array if statemenet';
157 | // Greater if statement
158 | } else if (title > 10) {
159 | title = 'Greater if statement';
160 | // And if statement
161 | } else if (title > 10 && title < 10) {
162 | title = 'And if statement';
163 | // Or if statement
164 | } else if (title > 10 || title != 0) {
165 | title = 'Or if statement';
166 | // Else if statement
167 | } else {
168 | title = 'Else if statement';
169 | }
170 |
171 | return
172 | {title}
173 |
174 | {title ? title : 'Other'}
175 |
;
176 | }
177 | }
178 | ```
179 |
180 | ### If statements (Twig 🌱)
181 |
182 | ```twig
183 | {# templates/pages/my.html.twig #}
184 |
185 | {% set title = 'Test' %}
186 | {% set list = ['Test', 'Test 2'] %}
187 |
188 | {# Basic equal if statement #}
189 | {% if title == 'test' %}
190 | {% set title = 'Basic equal if statement' %}
191 | {# Basic not equal if statement #}
192 | {% elseif title != 'test' %}
193 | {% set title = 'Basic not equal if statement' %}
194 | {# Typesafe if statement #}
195 | {% elseif title same as(false) %} {# Very uncommon in Twig todo typesafe checks #}
196 | {% set title = 'Typesafe if statement' %}
197 | {# Typesafe not if statement #}
198 | {% elseif title not same as(false) %} {# Very uncommon in Twig todo typesafe checks #}
199 | {% set title = 'Typesafe not if statement' %}
200 | {# In array if statement #}
201 | {% elseif title in list %}
202 | {% set title = 'In array if statement' %}
203 | {# Not in array if statement #}
204 | {% elseif title not in list %}
205 | {% set title = 'Not in array if statement' %}
206 | {# Greater if statement #}
207 | {% elseif title > 10 %}
208 | {% set title = 'Greater if statement' %}
209 | {# And if statement #}
210 | {% elseif title > 10 and title < 10 %}
211 | {% set title = 'And if statement' %}
212 | {# Or if statement #}
213 | {% elseif title > 10 or title != 0 %}
214 | {% set title = 'Or if statement' %}
215 | {# Else if statement #}
216 | {% else %}
217 | {% set title = 'Else if statement' %}
218 | {% endif %}
219 |
220 |
221 | {{ title }}
222 |
223 | {{ title ?: 'Other' }}
224 |
225 | ```
226 |
227 | ### If statements (Explanation)
228 |
229 | For if statement the [if tag](https://twig.symfony.com/doc/3.x/tags/if.html)
230 | is used. Where in JavaScript it is very common to have
231 | typesafe check with `===`. This is very uncommon in twig
232 | because Twig is really focused being a template engine and
233 | not a language where you should build business logic.
234 |
235 | It is still possible in Twig doing a typesafe check using
236 | the [same as test](https://twig.symfony.com/doc/3.x/tests/sameas.html).
237 |
238 | Twig like PHP supports also the ternary operator (`?:`) and the
239 | null-coalescing operator (`??`). Read more about them in the
240 | [Twig operators documentation](https://twig.symfony.com/doc/3.x/templates.html#other-operators).
241 |
242 | ## Loops
243 |
244 | ### Loops (React JS ⚛️)
245 |
246 | ```js
247 | // src/views/My.js
248 |
249 | import React from 'react';
250 |
251 | class My extends React.Component {
252 | render() {
253 | const list = [{
254 | title: 'List Item 1',
255 | }, {
256 | title: 'List Item 2',
257 | }];
258 |
259 | return
260 | {list.map((item) => {
261 | return -
262 | {item.title}
263 |
;
264 | })}
265 |
;
266 | }
267 | }
268 | ```
269 |
270 | ### Loops (Twig 🌱)
271 |
272 | ```twig
273 | {# templates/pages/my.html.twig #}
274 |
275 | {% set list = [
276 | {
277 | title: 'List Item 1',
278 | },
279 | {
280 | title: 'List Item 2',
281 | }
282 | ] %}
283 |
284 |
285 | {% for item in list %}
286 | - {{ item.title }}
287 | {% endfor %}
288 |
289 | ```
290 |
291 | ### Loops (Explanation)
292 |
293 | Where in `JavaScript` different way of looping exist in twig
294 | you will work with the [for](https://twig.symfony.com/doc/3.x/tags/for.html)
295 | to loop over variables.
296 |
297 | Twig here supports some range function to make developing of
298 | templates easier for example:
299 |
300 | ```twig
301 | {# Output numbers from 0 - 10 #}
302 | {% for i in 0..10 %}
303 | * {{ i }}
304 | {% endfor %}
305 |
306 | {# Output letters from a-z #}
307 | {% for i in 'a'..'z' %}
308 | * {{ i }}
309 | {% endfor %}
310 |
311 | {# Output letters from A-Z #}
312 | {% for letter in 'a'|upper..'z'|upper %}
313 | * {{ letter }}
314 | {% endfor %}
315 | ```
316 |
317 | By default inside the `for` loop Twig provide some magic variable
318 | called `loop` which will provide you the following data:
319 |
320 | ```twig
321 | {% for i in 0..10 %}
322 | {{ loop.index }} {# index beginning with 1 #}
323 | {{ loop.index0 }} {# index beginning with 0 #}
324 | {{ loop.revindex }} {# reverted index #}
325 | {{ loop.revindex0 }} {# reverted index0 #}
326 | {{ loop.first }} {# boolean flag if its the first item of this loop #}
327 | {{ loop.last }} {# boolean flag if its the last item of this loop #}
328 | {{ loop.length }} {# the number of items of this loop #}
329 | {{ loop.parent }} {# access the parent loop variable if using nested loops #}
330 | {% endfor %}
331 | ```
332 |
333 | Where in `React JS` you maybe need a `counter` or accessing the length
334 | in `Twig` the `loop` variable is available inside every loop and make
335 | it easy to add CSS classes to first or last items of a loop.
336 |
337 | See here also the Twig documentation about the [for](https://twig.symfony.com/doc/3.x/tags/for.html)
338 | tag.
339 |
340 | As `React JS` has access to all `JavaScript` array available function
341 | Twig also has it tricks to make outputting of html very simple.
342 |
343 | So for example let see that we have variable which content is the
344 | following:
345 |
346 | ```js
347 | ["1", "2", "3", "4", "5"]
348 | ```
349 |
350 | and we want to render the following HTML:
351 |
352 | ```html
353 |
354 |
355 |
356 | 1
357 |
358 |
359 |
360 | 2
361 |
362 |
363 |
364 | 3
365 |
366 |
367 |
368 |
369 |
370 | 4
371 |
372 |
373 |
374 | 5
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 | ```
383 |
384 | This can in Twig easily be rendered by the `batch` function
385 | the following way:
386 |
387 | ```twig
388 |
389 | {% for row in list|batch(3, '') %}
390 |
391 | {% for item in row %}
392 |
393 | {{ item }}
394 |
395 | {% endfor %}
396 |
397 | {% endfor %}
398 |
399 | ```
400 |
401 | Read here also the offical documentation about the
402 | [Twig batch function](https://twig.symfony.com/doc/3.x/filters/batch.html).
403 |
404 | ## Base Template
405 |
406 | The first you mostly create when building a website is a base
407 | template or base component which contains all things which your
408 | templates have in common like Navigation, Footer, Header.
409 |
410 | ### Base Template (React JS ⚛️)
411 |
412 | In react for rendering anything we first need to create a
413 | `index.html` where the react application is started which also
414 | contains the minimal required html for a page.
415 |
416 | ```html
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 | ```
428 |
429 | In react a base template could look like the following.
430 |
431 | ```js
432 | // src/views/Base.js
433 |
434 | import React from 'react';
435 | import Head from 'next/head'
436 | import Navigation from './components/Navigation';
437 | import Header from './components/Header';
438 | import Footer from './components/Footer';
439 |
440 | class Base extends React.Component {
441 | render() {
442 | const {
443 | header
444 | } = this.props;
445 |
446 | const headerComponent = header ? header : ;
447 |
448 | return
449 |
450 |
{this.props.title}
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 | {this.props.children}
460 |
461 |
462 |
463 |
;
464 | }
465 | }
466 | ```
467 |
468 | A default template build on top of the base template will look like the following way:
469 |
470 | ```js
471 | // src/views/Default.js
472 |
473 | import React from 'react';
474 | import Base from './components/Base';
475 |
476 | class Default extends React.Component {
477 | render() {
478 | const title = 'Some Title';
479 |
480 | return
481 | {title}
482 | ;
483 | }
484 | }
485 | ```
486 |
487 | A homepage template build on top of the base template could look this way
488 | which will override the Header component:
489 |
490 | ```js
491 | // src/views/Homepage.js
492 |
493 | import React from 'react';
494 | import Base from './components/Base';
495 | import HeaderBig from './components/HeaderBig';
496 |
497 | class Homepage extends React.Component {
498 | render() {
499 | const title = 'Some Title';
500 |
501 | return }>
503 | {title}
504 | ;
505 | }
506 | }
507 | ```
508 |
509 | This way we created a reusable base which can be used in all components again.
510 |
511 | ### Base Template (Twig 🌱)
512 |
513 | To implement the same as we did above we need to create the following
514 | in our Twig structure. As Twig is server rendered by PHP in Symfony
515 | we will create the `base.html.twig` which will contain the html which
516 | was in react rendered by `index.html` and `Base.js`.
517 |
518 | ```twig
519 | {# templates/base.html.twig #}
520 |
521 |
522 | {title}
523 |
524 |
525 |
526 | {% include 'include/navigation.html.twig' %}
527 |
528 | {% block header %}
529 | {% include 'include/header.html.twig' %}
530 | {% endblock %}
531 |
532 | {% block content %}{% endblock %}
533 |
534 | {% include 'include/footer.html.twig' %}
535 |
536 |