Five New Features in Latte 3.1 That Will Make Your Life Easier
Latte 3.1 brings five new features – new filters
|column, |commas and |limit, improved
|slice for iterators, and two new engine features. No revolution,
just small things you'll appreciate.
No More Variable Leaking from
{foreach}
This has bugged me for years. You write
{foreach $items as $item}, the loop ends, and $item
happily lives on with the value of the last element. Worse, it can overwrite a
variable you were counting on:
{var $name = 'Yay'}
{foreach $users as $name}
...
{/foreach}
{$name} {* oops, no longer 'Yay' *}
Latte has long warned you when foreach overwrote a variable passed to the
template via parameters. But variables created directly in the template, e.g.
via {var}, were overwritten silently. And that's the
sneaky case.
Now you can fix it with a single setting:
$latte->setFeature(Latte\Feature::ScopedLoopVariables);
Variables from {foreach} now exist only inside the loop. After
the loop ends, the original value is restored. And if the variable didn't exist
before? It disappears completely.
{var $name = 'Yay'}
{foreach $users as $name}
...
{/foreach}
{$name} {* outputs 'Yay', yay! *}
And the old warning about overwritten parameters? It gets disabled with this feature, because it no longer makes sense – nothing gets overwritten anymore.
It works with destructuring {foreach $data as [$a, $b]} and of
course with keys {foreach $arr as $k => $v} – everything is
cleaned up after the loop. Nested loops have independent scopes.
The only exception: if you iterate by reference
{foreach $arr as &$v}, scope doesn't apply – references
directly modify the original array, so restoring values after the loop would
break them. Makes sense.
Indent Your Templates However You Like
You know the drill: you have a <ul> with a
{foreach} inside generating <li>. You naturally
indent it for readability. But that indentation bleeds into the output. Either
you have clean code and ugly HTML, or the other way around.
Unacceptable.
With Feature::Dedent, this dilemma disappears:
$latte->setFeature(Latte\Feature::Dedent);
Latte automatically removes the common indentation inside paired tags. So this:
<ul>
{foreach $items as $item}
<li>{$item}</li>
{/foreach}
</ul>
generates:
<ul>
<li>...</li>
<li>...</li>
</ul>
The indentation is gone as if it was never there.
And since dedent is performed at template compilation time, not during each
render, it has zero performance impact. It works for all paired tags –
{if}, {block}, {capture},
{foreach} and more. Nested tags are dedented independently, each at
its own level.
If the indentation is inconsistent – for instance, you mix tabs with
spaces, or a line doesn't have sufficient indentation – Latte throws a
CompileException with the exact line number. No silent swallowing
of errors.
Filter
|commas
Joining an array into a comma-separated string – easy. But what if you want “and” instead of a comma before the last element? That's exactly the kind of thing where you start writing conditions in the template and suddenly you have four lines of code instead of one. And all you wanted to say was “apple, pear and plum”.
{['apple', 'pear', 'plum']|commas} {* apple, pear, plum *}
{['apple', 'pear', 'plum']|commas:' and '} {* apple, pear and plum *}
{['apple', 'pear', 'plum']|commas:', or '} {* apple, pear, or plum *}
Without a parameter, it joins with a comma and space. With a parameter, it uses the given string as the separator between the last two elements – the rest stays comma-separated. Natural language, not foreach.
Filter
|column
You have an array of user records, say
[['id' => 1, 'name' => 'John'], ['id' => 2, 'name' => 'Jane'], ...],
and you need just the names? The |column filter extracts values
from a single column – whether it's an array key or an object property:
{$users|column:'name'|commas} {* John, Jane, Bob *}
It optionally accepts a second parameter for indexing the results:
{foreach ($users|column:'name':'id') as $id => $name}
{$id}: {$name}
{/foreach}
It works with iterators too, not just arrays – however, the iterator is internally converted to an array, so don't expect any lazy magic.
Filter
|slice for Iterators and New Filter |limit
The |slice filter extracts a portion of an array or string (with
full UTF-8 support for strings). Until now, it only worked with arrays and
strings. Now it also handles iterators and generators – it returns a
generator that reads elements from the source one by one and stops once the
limit is reached. The entire iterator is never loaded into memory:
{foreach ($generator|slice:0:10) as $item}
{$item}
{/foreach}
On top of that, there's a new filter
|limit – a more convenient variant for the typical case of
“take the first N elements”. It works with arrays, iterators, and strings
(with UTF-8 support):
{foreach ($items|limit:5) as $item}
{$item}
{/foreach}
{$description|limit:100}
The difference from |slice is that |limit preserves
the original keys by default.
How to Enable
The new features ScopedLoopVariables and Dedent are
enabled via setFeature():
$latte = new Latte\Engine;
$latte->setFeature(Latte\Feature::ScopedLoopVariables);
$latte->setFeature(Latte\Feature::Dedent);
And if you're using Nette, just enable them in the configuration:
latte:
scopedLoopVariables: true
dedent: true
Five small things for life.
Sign in to submit a comment