Share
Embed
Transcript
39 slides
-
</> htmx <button hx-post="/clicked" hx-swap="outerHTML"> Click Me </button> Supported in
Symfony since 2.0 preview release 5 - Dec 8, 2010 https://github.com/symfony/symfony/commit/e8672740 https://jolicode.com/blog/making-a-single-page-application-with-htmx-and-symfony -
</> htmx #[Route('/messages', name: 'messages')] public function messages(MessageRepository $messageRepo): Response
{ return $this->render('messages.html.twig', [ 'messages' => $messageRepo->findAll(), // many more variables ]); } {% extends 'base.html.twig' %} {% block body %} <ul id="messages"> {% for message in messages %} <li>{{ message.content }}</li> {% endfor %} </ul> <a href="{{ path('messages') }}">Refresh</a> {% endblock %} -
#[Route('/messages/refresh', name: 'refresh_messages')] #[Template('messages/messages.html.twig', block: 'messages')] public function messages(MessageRepository $messageRepository):
array { return ['messages' => $messageRepository->findAll()]; } {% extends 'base.html.twig' %} {% block body %} <ul id="messages"> {% block messages %} ... {% endblock %} </ul> <button hx-post="{{ path('refresh_messages') }}" hx-target="#messages"> Refresh </button> {% endblock %} "block" attribute added in Symfony 7.2 https://github.com/symfony/symfony/pull/58028 Twig ❤ </> htmx -
Twig 3.9+ Twig evolved a lot in the last 6
months Template cache hot reload Operator precedence fixes Arrows everywhere Named arguments (macros, ...) Twig callable arguments as snake-case or camelCase Colon as argument separator Yielding instead of echo-ing Inline comments ChainCache Argument unpacking Static Analyzer support{% types %} Compile time checks {% guard %} Functions: enum, html_cva, enum_cases Filters: find, singular, plural, shuffle Tests: sequence, mapping Tons of internal changes/refactoring to make the code more robust (no more global functions!) -
Template cache hot reload https://github.com/twigphp/Twig/pull/4338 Use cases: * Long-running processes
- Messenger worker * Modern PHP app servers - FrankenPHP * User templates - stored in a DB * ... -
Template cache hot reload https://github.com/twigphp/Twig/pull/4338 $twig = new Twig\Environment($loader, [
'auto_reload' => false, 'cache' => __DIR__.'/cache', ]); // prime the cache echo $twig->load('index.twig')->render([]); // update the template file_put_contents(__DIR__.'/index.twig', file_get_contents(__DIR__.'/index.twig').'foo'); // still use the cached version echo $twig->load('index.twig')->render([]); // remove the cache for this template $twig->removeCache('index.twig'); // refresh the cache with the updated template echo $twig->load('index.twig')->render([]); -
Operators Precedence {{ '42' ~ 1 + 41 }} ?
{{ '42' ~ (1 + 41) }} {{ ('42' ~ 1) + 41 }} Probably not what you would expect Probably what you would expect -
4242 462 How to NOT break BC? Twig 3 Twig
4 {{ '42' ~ (1 + 41) }} {{ ('42' ~ 1) + 41 }} {{ '42' ~ 1 + 41 }} https://github.com/twigphp/Twig/pull/4363 -
A framework to automate these changes Since twig/twig 3.15: Add
explicit parentheses around the "??" binary operator to avoid behavior change in the next major version as its precedence will change in "index.twig" at line 4. -
Other operator precedence changes 'not' => ['precedence' => 50, 'precedence_change'
=> new OperatorPrecedenceChange(70)] '??' => ['precedence' => 300, 'precedence_change' => new OperatorPrecedenceChange(5)] '~' => ['precedence' => 40, 'precedence_change' => new OperatorPrecedenceChange(27)] -
Inline comments {{ # this is an inline comment "Hello
World"|upper # this is an inline comment }} {{ { # this is an inline comment fruit: 'apple', # this is an inline comment color: 'red', # this is an inline comment }|join(', ') }} {{ "Hello World"|upper # this is an inline comment }} {{ "Hello World"|upper # this is an inline comment }} Nope Yep Yep Yep -
Twig callables A Twig Filter is a Twig callable A
Twig Function is a Twig callable A Twig Test is a Twig callable A Twig Tag is NOT a Twig callable 'needs_environment' => false, 'needs_context' => false, 'needs_charset' => false, 'is_variadic' => false, 'deprecation_info' => null, 'deprecated' => false, 'deprecating_package' => '', 'alternative' => null, -
Named arguments everywhere https://github.com/twigphp/Twig/pull/4300 {# dot operator#} {{ html.generate_input(name: 'pwd',
type: 'password') }} {# macro #} {{ html.generate(name: 'pwd', type: 'password') }} {# block function #} {{ block(name: "title", template: "common_blocks.twig") }} -
Static analysis with {% types %} https://github.com/twigphp/Twig/pull/4235 https://twig.symfony.com/types {% types
{ is_correct: 'boolean', score?: 'number', } %} https://github.com/alisqi/TwigQI https://github.com/twigstan/twigstan -
Compile-time checks with {% guard %} https://github.com/twigphp/Twig/pull/4304 https://twig.symfony.com/guard {% guard
function importmap %} {{ importmap('app') }} {% else %} {# the importmap function doesn't exist, generate fallback code #} {% endguard %} -
Arguments unpacking https://github.com/twigphp/Twig/pull/4300 {% set numbers = [1, 2, ...moreNumbers]
%} {% set ratings = {'foo': 10, 'bar': 5, ...moreRatings} %} {{ 'Hello %s %s!'|format(...['Fabien', 'Potencier']) }} New -
Twig callable args as camel or snake https://github.com/twigphp/Twig/pull/4318 public static
function htmlCva(... $compoundVariants = [], ...): Cva {% set alert = html_cva( // ... compound_variants=[{ color: ['red'], size: ['md', 'lg'], class: 'font-bold' }] ) %} PHP likes camelCase Twig likes snake_case -
Arrows everywhere https://github.com/twigphp/Twig/pull/4377 https://github.com/twigphp/Twig/pull/4378 {% set first_name_fn = (p) =>
p.first %} {{ _self.display_people(people, first_name_fn) }} {% macro display_people(people, fn) %} {{ people|map(fn)|join(', ') }} {% endmacro %} -
for improvements (4.x only) loop.previous / loop.next {% for value
in values %} {% if not loop.first and value > loop.previous %} The value just increased! {% endif %} {{ value }} {% if not loop.last and loop.next > value %} The value will increase even more! {% endif %} {% endfor %} https://github.com/twigphp/Twig/pull/4135 New -
for improvements (4.x only) loop.changed {% for entry in entries
%} {% if loop.changed(entry.category) %} <h2>{{ entry.category }}</h2> {% endif %} <p>{{ entry.message }}</p> {% endfor %} https://github.com/twigphp/Twig/pull/4135 New -
for improvements (4.x only) loop.cycle {% for row in rows
%} <li class="{{ loop.cycle('odd', 'even') }}">{{ row }}</li> {% endfor %} https://github.com/twigphp/Twig/pull/4135 New {{ cycle(['odd', 'even'], loop.index0) }} -
for improvements (4.x only) recursive loops via loop() <ul class="sitemap">
{%- for item in sitemap %} <li>{{ item.title }} {%- if item.children -%} <ul class="submenu depth-{{ loop.depth }}"> {{ loop(item.children) }} </ul> {%- endif %} </li> {%- endfor %} </ul> New https://github.com/twigphp/Twig/pull/4153 New -
for improvements (4.x only) if support is back! {% set
stopOnFabien = false %} {% for user in users if not stopOnFabien %} - {{ user }} {% set stopOnFabien = user == 'Fabien' %} {% endfor %} https://github.com/twigphp/Twig/pull/4251 {% for user in users if loop.length != 5 %} - {{ user }} {% endfor %}