Skip to content

Commit 78fb077

Browse files
committed
latte 3.1
1 parent f1acb46 commit 78fb077

File tree

5 files changed

+270
-8
lines changed

5 files changed

+270
-8
lines changed

latte/en/attributes.texy

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
Smart HTML Attributes
2+
*********************
3+
4+
.[perex]
5+
Latte 3.1 comes with a set of improvements that focuses on one of the most common activities in templates – printing HTML attributes. It brings more convenience, flexibility and security.
6+
7+
8+
Boolean Attributes
9+
==================
10+
11+
HTML has a specific category of attributes where the value doesn't matter, but their presence does. These are attributes like `checked`, `disabled`, `selected`, `hidden`, `readonly`, `required`, etc.
12+
13+
Previously, you had to use `n:attr` or conditions. In Latte 3.1, you can pass `true` or `false` directly to the attribute:
14+
15+
```latte
16+
<input hidden={true} readonly={false}>
17+
```
18+
19+
Outputs:
20+
21+
```latte
22+
<input hidden>
23+
```
24+
25+
If the value is `true`, the attribute is rendered. If `false`, it is omitted. This is intuitive and the code is much more readable.
26+
27+
If you are using a JavaScript library that requires the presence/absence of an attribute and you want to achieve this behavior for other attributes as well (e.g. `data-` attributes), use the [toggle |filters#toggle] filter.
28+
29+
30+
Null Values
31+
===========
32+
33+
This is one of the most pleasant changes. Previously, if a variable was `null`, it printed as an empty string `""`. This often led to empty attributes in HTML like `class=""` or `title=""`.
34+
35+
In Latte 3.1, a new universal rule applies: **A value of `null` means the attribute does not exist.**
36+
37+
```latte
38+
<div title="{$title}"></div>
39+
```
40+
41+
If `$title` is `null`, the output is `<div></div>`. If it contains a string, e.g. "Hello", the output is `<div title="Hello"></div>`. Thanks to this, you don't have to wrap attributes in conditions.
42+
43+
If you use filters, keep in mind that they usually convert `null` to a string (e.g. empty string). To prevent this, use the [nullsafe operator |syntax#Nullsafe Filters] `?|`:
44+
45+
```latte
46+
<div title="{$title?|upper}"></div>
47+
```
48+
49+
50+
Classes and Styles
51+
==================
52+
53+
The `n:class` attribute is loved for its ability to compose classes from arrays and conditions. Now the standard `class` attribute gains this superpower as well.
54+
55+
You can pass an array (even an associative one with conditions) to the `class` attribute, exactly as you are used to with `n:class`:
56+
57+
```latte
58+
<div class="header">
59+
<button class={[
60+
'btn',
61+
'btn-primary',
62+
'active' => $isActive,
63+
'disabled' => $isDisabled
64+
]}>Press me</button>
65+
</div>
66+
```
67+
68+
If `$isActive` is true and `$isDisabled` is false, it renders:
69+
70+
```latte
71+
<div class="header">
72+
<button class="btn btn-primary active">Press me</button>
73+
</div>
74+
```
75+
76+
The `style` attribute also gains similar flexibility. Instead of concatenating strings, you can pass an array:
77+
78+
```latte
79+
<div style={[
80+
background: 'lightblue',
81+
display: $isVisible ? 'block' : 'none'
82+
]}></div>
83+
```
84+
85+
86+
Data Attributes
87+
===============
88+
89+
Often we need to pass configuration for JavaScript into HTML. Previously this was done via `json_encode`. Now you can simply pass an array or object to a `data-` attribute and Latte will serialize it to JSON:
90+
91+
```latte
92+
<div data-config={[ theme: 'dark', version: 2 ]}></div>
93+
```
94+
95+
Outputs:
96+
97+
```latte
98+
<div data-config='{"theme":"dark","version":2}'></div>
99+
```
100+
101+
Also, `true` and `false` are rendered as strings `"true"` and `"false"` (i.e. valid JSON).
102+
103+
104+
Aria Attributes
105+
===============
106+
107+
The WAI-ARIA specification requires text values `"true"` and `"false"` for boolean values. Latte handles this automatically for `aria-` attributes:
108+
109+
```latte
110+
<button aria-expanded={true} aria-checked={false}></button>
111+
```
112+
113+
Outputs:
114+
115+
```latte
116+
<button aria-expanded="true" aria-checked="false"></button>
117+
```
118+
119+
120+
Type Checking
121+
=============
122+
123+
Latte 3.1 checks attribute types to prevent you from printing nonsense.
124+
125+
1. **Standard attributes (href, src, id...):** Expect a string or `null`. If they receive an array, object or boolean, Latte throws a warning and the attribute is omitted.
126+
2. **Boolean attributes (checked...):** Expect a boolean.
127+
3. **Special attributes (class, style, data-, aria-):** Have their own rules described above.
128+
129+
This check helps you discover bugs in your code early.
130+
131+
132+
Migration from Latte 3.0
133+
========================
134+
135+
Since the behavior of `null` changes (it used to print `""`, now it doesn't print anything) and `data-` attributes (booleans used to print `1`/`""`, now `"true"`/`"false"`), Latte offers a tool to help with migration.
136+
137+
You can enable **migration warnings** (see [Develop |develop#Migration Warnings]), which will warn you during rendering if the output differs from Latte 3.0.
138+
139+
If the new behavior is correct (e.g. you want the empty attribute to disappear), confirm it using the [accept |filters#accept] filter to suppress the warning:
140+
141+
```latte
142+
<div class="{$var|accept}"></div>
143+
```
144+
145+
If you want to keep the attribute as empty (e.g. `title=""`) instead of dropping it, use the null coalescing operator:
146+
147+
```latte
148+
<div title={$var ?? ''}></div>
149+
```
150+
151+
Or, if you strictly require the old behavior (e.g. `"1"` for `true`), explicitly cast the value to string:
152+
153+
```latte
154+
<div data-foo={(string) $bool}></div>
155+
```

latte/en/develop.texy

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ Supported PHP versions (applies to the latest patch Latte versions):
1515

1616
| version | compatible with PHP
1717
|-----------------|-------------------
18-
| Latte 3.0 | PHP 8.0 – 8.2
18+
| Latte 3.1 | PHP 8.2 – 8.5
19+
| Latte 3.0 | PHP 8.0 – 8.5
1920

2021

2122
How to Render a Template
@@ -193,6 +194,26 @@ $latte = new Latte\Engine;
193194
$latte->setStrictTypes();
194195
```
195196

197+
Strict mode is **enabled by default** in Latte 3.1. To disable it use `$latte->setStrictParsing(false)`.
198+
199+
200+
Migration Warnings .{data-version:3.1}
201+
======================================
202+
203+
Latte 3.1 changes the behavior of some HTML attributes (see [Smart Attributes|attributes]). For example, `null` values now drop the attribute instead of printing an empty string. To easily find places where this change affects your templates, you can enable migration warnings:
204+
205+
```php
206+
$latte->setMigrationWarnings();
207+
```
208+
209+
When enabled, Latte checks rendered attributes and triggers a user warning (`E_USER_WARNING`) if the output differs from what Latte 3.0 would have produced. When you encounter a warning, apply one of the solutions above (accept, fallback, or cast):
210+
211+
1. If the new output is correct for your use case (e.g., you prefer the attribute to disappear when `null`), suppress the warning by adding the [`|accept` |filters#accept] filter
212+
2. If you want the attribute to be rendered as empty (e.g. `class=""`) instead of being dropped when the variable is `null`, provide an empty string as a fallback: `class={$val ?? ''}`
213+
3. If you strictly require the old behavior (e.g., printing `"1"` for `true` instead of `"true"`), explicitly cast the value to a string: `data-foo={(string) $val}`
214+
215+
Once all warnings are resolved, disable migration warnings by removing `setMigrationWarnings()` and remove all `|accept` filters from your templates, as they are no longer needed.
216+
196217

197218
Translation in Templates .{toc: TranslatorExtension}
198219
====================================================

latte/en/filters.texy

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ In templates, we can use functions that help modify or reformat data into its fi
5555
| `floor` | [rounds a number down to a given precision |#floor]
5656
| `round` | [rounds a number to a given precision |#round]
5757

58+
.[table-latte-filters]
59+
|## HTML Attributes
60+
| `accept` | [accepts the new behavior of smart attributes |#accept]
61+
| `toggle` | [toggles the presence of an HTML attribute |#toggle]
62+
5863
.[table-latte-filters]
5964
|## Escaping
6065
| `escapeUrl` | [escapes a parameter in a URL |#escapeUrl]
@@ -117,10 +122,31 @@ It is then called in the template like this:
117122
```
118123

119124

125+
Nullsafe Filters .{data-version:3.1}
126+
------------------------------------
127+
128+
If the variable can be `null`, you can use the nullsafe operator `?|`. If the value is `null`, the filter is not called and the result is `null`.
129+
130+
```latte
131+
<h1>{$heading?|upper}</h1>
132+
```
133+
134+
This is useful in combination with [smart attributes |attributes], which are omitted if the value is `null`.
135+
136+
120137
Filters
121138
=======
122139

123140

141+
accept .[filter]{data-version:3.1}
142+
----------------------------------
143+
This filter is used exclusively within HTML attributes during the migration to Latte 3.1.
144+
145+
It suppresses [Migration Warnings |develop#Migration Warnings] when you are satisfied with the new behavior of smart attributes. Use it to acknowledge that the new rendering (e.g., dropping a `null` attribute) is correct for a specific case, silencing the warning so you can focus on others.
146+
147+
**Note:** This is a temporary tool. Once the migration is complete and migration warnings are disabled, you should remove this filter from your templates.
148+
149+
124150
batch(int $length, mixed $item): array .[filter]
125151
------------------------------------------------
126152
A filter that simplifies listing linear data in a table format. It returns an array of arrays with the specified number of items. If you provide a second parameter, it will be used to fill in missing items in the last row.
@@ -827,6 +853,19 @@ Extracts a portion of a string. This filter has been replaced by the [#slice] fi
827853
```
828854

829855

856+
toggle .[filter]{data-version:3.1}
857+
----------------------------------
858+
This filter is used exclusively within HTML attributes. It switches the rendering of the entire attribute based on a truthy/falsey value.
859+
860+
It is useful for attributes where Latte does not automatically turn boolean values into presence/absence (like it does for `checked`, `hidden` etc), for example `data-` attributes or arbitrary attributes required by JS libraries.
861+
862+
```latte
863+
<div data-expanded="{$isExpanded|toggle}"></div>
864+
```
865+
866+
If `$isExpanded` is truthy, it renders `<div data-expanded></div>`. If falsey, the attribute is omitted.
867+
868+
830869
translate(...$args) .[filter]
831870
-----------------------------
832871
Translates expressions into other languages. To make the filter available, you need to [set up the translator |develop#TranslatorExtension]. You can also use the [tags for translation |tags#Translation].

latte/en/syntax.texy

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,40 @@ Which outputs, depending on the variable `$url`:
111111

112112
However, n:attributes are not only a shortcut for pair tags, there are some pure n:attributes as well, for example the coder's best friend [n:class|tags#n:class] or the very handy [n:href |application:creating-links#In the Presenter Template].
113113

114+
Typically, values in n:attributes are written in quotes. However, Latte 3.1 allows use curly braces. This syntax is mainly useful when the value contains a more complex expression with quote marks:
115+
116+
```latte
117+
<div n:if={str_contains($val, "foo")}> ... </div>
118+
```
119+
120+
121+
Smart HTML Attributes .{data-version:3.1}
122+
=========================================
123+
124+
Latte makes working with standard HTML attributes incredibly easy. It handles boolean attributes like `checked` for you, removes attributes containing `null`, and allows you to compose `class` and `style` values using arrays. It even automatically serializes data for `data-` attributes into JSON.
125+
126+
```latte
127+
<div
128+
class={[header, active => $isActive]}
129+
style={[color: 'blue', display: $show ? 'block']}
130+
hidden={$isHidden}
131+
data-options={[id: 123, lang: 'en']}
132+
></div>
133+
```
134+
135+
For example, it will display:
136+
137+
```latte
138+
<div
139+
class="header"
140+
style="color: blue"
141+
hidden
142+
data-options='{"id":123,"lang":"en"}'
143+
></div>
144+
```
145+
146+
Read more in the separate chapter [Smart HTML Attributes|attributes].
147+
114148

115149
Filters
116150
=======
@@ -148,10 +182,17 @@ On a block:
148182
```
149183

150184
Or directly on a value (in combination with the [`{=expr}` |tags#Printing] tag):
185+
151186
```latte
152187
<h1>{=' Hello world '|trim}<h1>
153188
```
154189

190+
If the value can be `null` and you want to avoid applying the filter in that case, use the [nullsafe operator |filters#Nullsafe Filters] `?|`:
191+
192+
```latte
193+
<h1>{$heading?|upper}</h1>
194+
```
195+
155196

156197
Dynamic HTML Tags .{data-version:3.0.9}
157198
=======================================
@@ -204,7 +245,7 @@ Simple strings are those composed purely of letters, digits, underscores, hyphen
204245
Constants
205246
---------
206247

207-
Since quotes can be omitted for simple strings, we recommend writing global constants with a leading slash to distinguish them:
248+
Use the global namespace separator to distinguish global constants from simple strings:
208249

209250
```latte
210251
{if \PROJECT_ID === 1} ... {/if}
@@ -265,8 +306,6 @@ A Window into History
265306

266307
Over its history, Latte introduced several syntactic sugar features that appeared in PHP itself a few years later. For example, in Latte, it was possible to write arrays as `[1, 2, 3]` instead of `array(1, 2, 3)` or use the nullsafe operator `$obj?->foo` long before it was possible in PHP itself. Latte also introduced the array expansion operator `(expand) $arr`, which is equivalent to today's `...$arr` operator from PHP.
267308

268-
The undefined-safe operator `??->`, which is similar to the nullsafe operator `?->` but does not raise an error if the variable does not exist, was created for historical reasons, and today we recommend using the standard PHP operator `?->`.
269-
270309

271310
PHP Limitations in Latte
272311
========================

latte/en/tags.texy

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ An overview and description of all the tags available by default in the Latte te
1616
| `{ifset}` … `{elseifset}` … `{/ifset}` | [ifset condition |#ifset elseifset]
1717
| `{ifchanged}` … `{/ifchanged}` | [tests if a value has changed |#ifchanged]
1818
| `{switch}` `{case}` `{default}` `{/switch}` | [switch condition |#switch case default]
19-
| `n:else` | [alternative content for conditions |#n:else]
19+
| `n:else`, `n:elseif` | [alternative content for conditions |#n:else-elseif]
2020

2121
.[table-latte-tags language-latte]
2222
|## Loops
@@ -252,14 +252,16 @@ Did you know you can add the `tag-` prefix to n:attributes? Then the condition w
252252
Awesome.
253253

254254

255-
`n:else` .{data-version:3.0.11}
256-
-------------------------------
255+
`n:else`, `n:elseif` .{toc: n:else}{data-version:3.0.11}
256+
--------------------------------------------------------
257257

258-
If you write the `{if} ... {/if}` condition in the form of an [n:attribute |syntax#n:attributes], you have the option to specify an alternative branch using `n:else`:
258+
If you write the `{if} ... {/if}` condition in the form of an [n:attribute |syntax#n:attributes], you have the option to specify alternative branches using `n:elseif` (since Latte 3.1) and `n:else`:
259259

260260
```latte
261261
<strong n:if="$count > 0">In stock {$count} items</strong>
262262

263+
<em n:elseif="$count < 0">Invalid count</em>
264+
263265
<em n:else>not available</em>
264266
```
265267

@@ -997,6 +999,12 @@ Depending on the returned values, it prints, for example:
997999
<input type="checkbox" value="Hello" checked>
9981000
```
9991001

1002+
Smart attribute features in Latte 3.1, such as dropping `null` values or passing arrays into `class` or `style`, also work within `n:attr`:
1003+
1004+
```latte
1005+
<div n:attr="class: [a, b], title: $title"></div>
1006+
```
1007+
10001008

10011009
`n:tag`
10021010
-------

0 commit comments

Comments
 (0)