Nette Documentation Preview

syntax
Migrazione da Latte 2 a 3
*************************

.[perex]
Latte 3 ha un compilatore completamente riscritto e una grammatica formalmente ben definita. L'obiettivo è coincidere il più possibile con Latte 2, ma alcuni costrutti richiedono piccoli ritocchi.

Nella pratica risulta che la stragrande maggioranza dei template non ha bisogno di alcuna modifica e funziona in Latte 3 esattamente come in Latte 2. Ma come individuare le incompatibilità?

**Per prima cosa installate la versione di transizione Latte 2.11.**

Questa versione non porta nuove funzionalità: si limita ad avvisare con E_USER_DEPRECATED nei casi in cui sa che il nuovo Latte non li supporterà e, cosa più importante, vi suggerisce come sistemarli. Per passare in rassegna tutti i template e verificare se sono compatibili potete usare lo strumento [Linter |/develop#Linter], che si esegue dalla console:

```shell
vendor/bin/latte-lint <path>
```

Una volta risolte le eventuali incompatibilità, aggiornate a Latte 3.0. **E lanciate di nuovo il Linter** per assicurarvi che il nuovo parser rigoroso comprenda davvero tutti i template.


Modifiche all'API
=================

Le modifiche all'API riguardano solo l'aggiunta di tag personalizzati. Il resto dell'API resta uguale alla versione 2, cioè lo stesso modo di fare il rendering dei template, di passare i parametri e di registrare i filtri.

Fa eccezione il cosiddetto filtro dinamico `Engine::addFilter(null, ...)`, che ora è gestito dai [filtri registrati tramite una classe |/custom-filters#Filtri tramite una classe] con il metodo `addFilter()`. Il metodo originale `Engine::addFilterLoader()` esiste ancora come soluzione transitoria, ma è deprecato.

L'API per aggiungere tag personalizzati è completamente diversa, quindi le estensioni pensate per Latte 2 non funzioneranno. Vedi anche [#Aggiornamento delle estensioni].


Modifiche alla sintassi
=======================

Le modifiche sono le seguenti:

- i filtri usano la virgola come separatore dei parametri: dove prima si scriveva `|filter: arg : arg` ora si scrive `|filter: arg, arg`
- il tag `{label foo}...{/label}` è sempre di tipo pari; la forma dispari va scritta `{label /}`
- al contrario, il tag `{_'text'}` è sempre dispari; la forma pari `{_}...{/}` è sostituita dal nuovo `{translate}...{/translate}`
- le pseudo-stringhe come `{block foo-$var}` vanno scritte tra apici, `{block "foo-$var"}`, oppure completate con le parentesi graffe, `{block foo-{$var}}`
- lo stesso vale per gli attributi: al posto di `n:block="foo-$var"` usate `n:block="foo-{$var}"`
- in Latte 3 i filtri fanno distinzione tra maiuscole e minuscole
- il tag `{do ...}` o `{php ...}` può contenere solo espressioni; per usare PHP qualsiasi registrate [RawPhpExtension |/develop#RawPhpExtension].

E altri casi limite:

- gli attributi `n:inner-xxx`, `n:tag-xxx` e `n:ifcontent` non si possono usare sugli elementi HTML void
- l'attributo `n:inner-snippet` va scritto senza inner-
- i tag `</script>` e `</style>` devono essere chiusi
- la variabile magica `$iterations` è stata rimossa (da non confondere con `$iterator`!)
- sostituite il tag `{includeblock file.latte}` con [`{include file.latte with blocks}` |/tags#include] oppure con [`{import}` |/template-inheritance#Riuso orizzontale]
- `{include "abc"}` va scritto come `{include file "abc"}`, a meno che `"abc"` non contenga un punto e sia quindi evidente che si tratta di un file


Aggiornamento delle estensioni
==============================

Con la riscrittura completa del parser è cambiato del tutto il modo di scrivere i tag personalizzati. Se avete tag personalizzati creati per Latte, dovrete riscriverli per la versione 3: vedi la [documentazione|/custom-tags].

Se usate un'estensione di terzi che aggiunge tag, dovrete aspettare che l'autore rilasci una versione per Latte 3. Le librerie `nette/application`, `nette/caching` e `nette/forms` nella versione 3.1, così come Texy, sono già state aggiornate e funzionano sia con Latte 2 sia con Latte 3.


nette/application
-----------------

.[note]
Nell'uso normale di Nette questa estensione viene impostata automaticamente e non serve cambiare nulla.

Vecchio codice per Latte 2:

```php
$latte->onCompile[] = function ($latte) {
	Nette\Bridges\ApplicationLatte\UIMacros::install($latte->getCompiler());
};

$latte->addProvider('uiControl', $control);
$latte->addProvider('uiPresenter', $control->getPresenter());
```

Nuovo codice per Latte 3:

```php
$latte->addExtension(new Nette\Bridges\ApplicationLatte\UIExtension($control));
```

UIExtension aggiunge `n:href`, `{link}`, `{control}`, `{snippet}` e simili. I tag per gli snippet passano quindi da Latte stesso alla libreria `nette/application`. In Latte 3 il metodo del presenter `templatePrepareFilters()` non viene più chiamato.


nette/forms
-----------

.[note]
Nell'uso normale di Nette questa estensione viene impostata automaticamente e non serve cambiare nulla.

Vecchio codice per Latte 2:

```php
$latte->onCompile[] = function ($latte) {
	Nette\Bridges\FormsLatte\FormMacros::install($latte->getCompiler());
};
```

Nuovo codice per Latte 3:

```php
$latte->addExtension(new Nette\Bridges\FormsLatte\FormsExtension);
```


nette/caching
-------------

.[note]
Nell'uso normale di Nette questa estensione viene impostata automaticamente e non serve cambiare nulla.

Vecchio codice per Latte 2:

```php
$latte->onCompile[] = function ($latte) {
	$latte->getCompiler()->addMacro('cache', new Nette\Bridges\CacheLatte\CacheMacro);
};

$latte->addProvider('cacheStorage', $cacheStorage);
```

Nuovo codice per Latte 3:

```php
$latte->addExtension(new Nette\Bridges\CacheLatte\CacheExtension($cacheStorage));
```


Tracy
-----

Anche il pannello per Tracy si attiva ora come estensione.

Vecchio codice per Latte 2:

```php
$latte = new Latte\Engine;
Latte\Bridges\Tracy\LattePanel::initialize($latte);
```

Nuovo codice per Latte 3:

```php
$latte = new Latte\Engine;
$latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);
```


Traduzioni
----------

TranslatorExtension aggiunge i tag di traduzione `{_'text'}`, il nuovo tag di tipo pari `{translate}...{/translate}` e il filtro `|translate`.

Vecchio codice per Latte 2:

```php
$latte->addFilter('translate', [$translator, 'translate']);
```

Nuovo codice per Latte 3:

```php
$latte->addExtension(new Latte\Essential\TranslatorExtension($translator));
```

Nei presenter si attiva automaticamente impostando il traduttore nel template con il metodo `$template->setTranslator($translator)`. Senza questo i tag di traduzione non saranno disponibili e dovrete registrare l'estensione manualmente o tramite un file di configurazione.


File di configurazione
======================

In Latte 2 era possibile registrare nuovi tag tramite il [file di configurazione |application:configuration#Template Latte], nella sezione `latte › macros`. Nella versione 3 si aggiungono in questo modo intere estensioni:

```neon
latte:
	extensions:
		- App\Templating\LatteExtension
		- Latte\Essential\TranslatorExtension(@Nette\Localization\Translator)
```


Sviluppate un'estensione per Latte?
===================================

Nella vostra libreria potete supportare entrambe le versioni di Latte contemporaneamente. Per rilevare la versione conviene usare la costante `Latte\Engine::VERSION`, così da separare l'uso di `onCompile[]` e `addMacro()` dal nuovo `addExtension()`:

```php
if (version_compare(Latte\Engine::VERSION, '3', '<')) {
	// inizializzazione per Latte 2
	$this->latte->onCompile[] = function ($latte) {
		$latte->addMacro(/* ... */);
	};
} else {
	// inizializzazione per Latte 3
	$this->latte->addExtension(/* ... */);
}
```

Come esempio, proviamo a riscrivere per Latte 3 il codice seguente, pensato per Latte 2:

```php
// vecchio codice per Latte 2
$this->latte->onCompile[] = function (Latte\Engine $latte) {
	$set = new Latte\Macros\MacroSet($latte->getCompiler());
	$set->addMacro('foo', 'echo %escape(MyClass:myFunc(%node.word, %node.array))');
};
```

Latte 3 si estende tramite le [estensioni|/extending-latte]. Un'estensione minimale che aggiunge il tag `foo` avrebbe questo aspetto:

```php
// nuovo codice per Latte 3
class FooExtension extends Latte\Extension
{
	public function getTags(): array
	{
		return [
			'foo' => [FooNode::class, 'create'], // la classe FooNode la aggiungiamo tra un attimo
		];
	}
}

// registrazione
$this->latte->addExtension(new FooExtension);
```

Il nuovo compilatore è più solido e non contiene le scorciatoie di prima, quindi scrivere una macro richiede qualche riga di codice in più. Per esempio non possiamo passare direttamente una stringa di codice PHP come in Latte 2: creiamo invece una funzione. Ricordiamo che in Latte 2 la funzione avrebbe avuto più o meno questo aspetto:

```php
// Latte 2
$set->addMacro('foo', function (Latte\MacroNode $node, Latte\PhpWriter $writer) {
	return $writer->write('echo %escape(MyClass:myFunc(%node.word, %node.array))');
});
```

Latte 3 procede comunque in modo molto simile, solo che `MacroNode` si chiama `Latte\Compiler\Tag` e `PhpWriter` si chiama `Latte\Compiler\PrintContext`. Soprattutto, però, c'è un passaggio intermedio in più: la funzione non restituisce direttamente codice PHP, ma un nodo, cioè un discendente di `StatementNode`, che entra poi a far parte dell'albero AST. E questo nodo ha un metodo `print(Latte\Compiler\PrintContext $context): string` che restituisce il codice PHP:

```php
// Latte 3
class FooNode extends Latte\Compiler\Nodes\StatementNode
{
	public static function create(Latte\Compiler\Tag $tag): self
	{
		$node = new self;
		return $node;
	}

	public function print(Latte\Compiler\PrintContext $context): string
	{
		return $context->format('echo ...'); // restituisce codice PHP
	}
}
```

Inoltre la maschera in `$context->format()` non ha più le abbreviazioni `%node.***`: si presuppone che prima abbiate [analizzato il contenuto del tag |/custom-tags#Funzione di analisi del tag]. Usiamo quindi il parser per analizzare il contenuto in variabili (sottonodi) e poi lo stampiamo:

```php
use Latte\Compiler\Nodes\Php\Expression\ArrayNode;
use Latte\Compiler\Nodes\Php\ExpressionNode;

class FooNode extends Latte\Compiler\Nodes\StatementNode
{
	public ExpressionNode $subject;
	public ArrayNode $args;

	public static function create(Latte\Compiler\Tag $tag): self
	{
		$node = new self;
		// analisi del contenuto del tag
		$node->subject = $tag->parser->parseUnquotedStringOrExpression();
		$tag->parser->stream->tryConsume(',');
		$node->args = $tag->parser->parseArguments();
		return $node;
	}

	public function print(Latte\Compiler\PrintContext $context): string
	{
		return $context->format(
			'echo %escape(MyClass:myFunc(%node, %node));',
			$this->subject,
			$this->args,
		);
	}
}
```

Infine aggiungeremo il metodo `getIterator()` per permettere di attraversare i sottonodi durante la [visita dell'albero |/custom-tags#Implementare getIterator() per i sottonodi]:

```php
class FooNode extends Latte\Compiler\Nodes\StatementNode
{
	...

	public function &getIterator(): \Generator
	{
		yield $this->subject;
		yield $this->args;
	}
}
```

{{priority: -1}}

Migrazione da Latte 2 a 3

Latte 3 ha un compilatore completamente riscritto e una grammatica formalmente ben definita. L'obiettivo è coincidere il più possibile con Latte 2, ma alcuni costrutti richiedono piccoli ritocchi.

Nella pratica risulta che la stragrande maggioranza dei template non ha bisogno di alcuna modifica e funziona in Latte 3 esattamente come in Latte 2. Ma come individuare le incompatibilità?

Per prima cosa installate la versione di transizione Latte 2.11.

Questa versione non porta nuove funzionalità: si limita ad avvisare con E_USER_DEPRECATED nei casi in cui sa che il nuovo Latte non li supporterà e, cosa più importante, vi suggerisce come sistemarli. Per passare in rassegna tutti i template e verificare se sono compatibili potete usare lo strumento Linter, che si esegue dalla console:

vendor/bin/latte-lint <path>

Una volta risolte le eventuali incompatibilità, aggiornate a Latte 3.0. E lanciate di nuovo il Linter per assicurarvi che il nuovo parser rigoroso comprenda davvero tutti i template.

Modifiche all'API

Le modifiche all'API riguardano solo l'aggiunta di tag personalizzati. Il resto dell'API resta uguale alla versione 2, cioè lo stesso modo di fare il rendering dei template, di passare i parametri e di registrare i filtri.

Fa eccezione il cosiddetto filtro dinamico Engine::addFilter(null, ...), che ora è gestito dai filtri registrati tramite una classe con il metodo addFilter(). Il metodo originale Engine::addFilterLoader() esiste ancora come soluzione transitoria, ma è deprecato.

L'API per aggiungere tag personalizzati è completamente diversa, quindi le estensioni pensate per Latte 2 non funzioneranno. Vedi anche Aggiornamento delle estensioni.

Modifiche alla sintassi

Le modifiche sono le seguenti:

  • i filtri usano la virgola come separatore dei parametri: dove prima si scriveva |filter: arg : arg ora si scrive |filter: arg, arg
  • il tag {label foo}...{/label} è sempre di tipo pari; la forma dispari va scritta {label /}
  • al contrario, il tag {_'text'} è sempre dispari; la forma pari {_}...{/} è sostituita dal nuovo {translate}...{/translate}
  • le pseudo-stringhe come {block foo-$var} vanno scritte tra apici, {block "foo-$var"}, oppure completate con le parentesi graffe, {block foo-{$var}}
  • lo stesso vale per gli attributi: al posto di n:block="foo-$var" usate n:block="foo-{$var}"
  • in Latte 3 i filtri fanno distinzione tra maiuscole e minuscole
  • il tag {do ...} o {php ...} può contenere solo espressioni; per usare PHP qualsiasi registrate RawPhpExtension.

E altri casi limite:

  • gli attributi n:inner-xxx, n:tag-xxx e n:ifcontent non si possono usare sugli elementi HTML void
  • l'attributo n:inner-snippet va scritto senza inner-
  • i tag </script> e </style> devono essere chiusi
  • la variabile magica $iterations è stata rimossa (da non confondere con $iterator!)
  • sostituite il tag {includeblock file.latte} con {include file.latte with blocks} oppure con {import}
  • {include "abc"} va scritto come {include file "abc"}, a meno che "abc" non contenga un punto e sia quindi evidente che si tratta di un file

Aggiornamento delle estensioni

Con la riscrittura completa del parser è cambiato del tutto il modo di scrivere i tag personalizzati. Se avete tag personalizzati creati per Latte, dovrete riscriverli per la versione 3: vedi la documentazione.

Se usate un'estensione di terzi che aggiunge tag, dovrete aspettare che l'autore rilasci una versione per Latte 3. Le librerie nette/application, nette/caching e nette/forms nella versione 3.1, così come Texy, sono già state aggiornate e funzionano sia con Latte 2 sia con Latte 3.

nette/application

Nell'uso normale di Nette questa estensione viene impostata automaticamente e non serve cambiare nulla.

Vecchio codice per Latte 2:

$latte->onCompile[] = function ($latte) {
	Nette\Bridges\ApplicationLatte\UIMacros::install($latte->getCompiler());
};

$latte->addProvider('uiControl', $control);
$latte->addProvider('uiPresenter', $control->getPresenter());

Nuovo codice per Latte 3:

$latte->addExtension(new Nette\Bridges\ApplicationLatte\UIExtension($control));

UIExtension aggiunge n:href, {link}, {control}, {snippet} e simili. I tag per gli snippet passano quindi da Latte stesso alla libreria nette/application. In Latte 3 il metodo del presenter templatePrepareFilters() non viene più chiamato.

nette/forms

Nell'uso normale di Nette questa estensione viene impostata automaticamente e non serve cambiare nulla.

Vecchio codice per Latte 2:

$latte->onCompile[] = function ($latte) {
	Nette\Bridges\FormsLatte\FormMacros::install($latte->getCompiler());
};

Nuovo codice per Latte 3:

$latte->addExtension(new Nette\Bridges\FormsLatte\FormsExtension);

nette/caching

Nell'uso normale di Nette questa estensione viene impostata automaticamente e non serve cambiare nulla.

Vecchio codice per Latte 2:

$latte->onCompile[] = function ($latte) {
	$latte->getCompiler()->addMacro('cache', new Nette\Bridges\CacheLatte\CacheMacro);
};

$latte->addProvider('cacheStorage', $cacheStorage);

Nuovo codice per Latte 3:

$latte->addExtension(new Nette\Bridges\CacheLatte\CacheExtension($cacheStorage));

Tracy

Anche il pannello per Tracy si attiva ora come estensione.

Vecchio codice per Latte 2:

$latte = new Latte\Engine;
Latte\Bridges\Tracy\LattePanel::initialize($latte);

Nuovo codice per Latte 3:

$latte = new Latte\Engine;
$latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);

Traduzioni

TranslatorExtension aggiunge i tag di traduzione {_'text'}, il nuovo tag di tipo pari {translate}...{/translate} e il filtro |translate.

Vecchio codice per Latte 2:

$latte->addFilter('translate', [$translator, 'translate']);

Nuovo codice per Latte 3:

$latte->addExtension(new Latte\Essential\TranslatorExtension($translator));

Nei presenter si attiva automaticamente impostando il traduttore nel template con il metodo $template->setTranslator($translator). Senza questo i tag di traduzione non saranno disponibili e dovrete registrare l'estensione manualmente o tramite un file di configurazione.

File di configurazione

In Latte 2 era possibile registrare nuovi tag tramite il file di configurazione, nella sezione latte › macros. Nella versione 3 si aggiungono in questo modo intere estensioni:

latte:
	extensions:
		- App\Templating\LatteExtension
		- Latte\Essential\TranslatorExtension(@Nette\Localization\Translator)

Sviluppate un'estensione per Latte?

Nella vostra libreria potete supportare entrambe le versioni di Latte contemporaneamente. Per rilevare la versione conviene usare la costante Latte\Engine::VERSION, così da separare l'uso di onCompile[] e addMacro() dal nuovo addExtension():

if (version_compare(Latte\Engine::VERSION, '3', '<')) {
	// inizializzazione per Latte 2
	$this->latte->onCompile[] = function ($latte) {
		$latte->addMacro(/* ... */);
	};
} else {
	// inizializzazione per Latte 3
	$this->latte->addExtension(/* ... */);
}

Come esempio, proviamo a riscrivere per Latte 3 il codice seguente, pensato per Latte 2:

// vecchio codice per Latte 2
$this->latte->onCompile[] = function (Latte\Engine $latte) {
	$set = new Latte\Macros\MacroSet($latte->getCompiler());
	$set->addMacro('foo', 'echo %escape(MyClass:myFunc(%node.word, %node.array))');
};

Latte 3 si estende tramite le estensioni. Un'estensione minimale che aggiunge il tag foo avrebbe questo aspetto:

// nuovo codice per Latte 3
class FooExtension extends Latte\Extension
{
	public function getTags(): array
	{
		return [
			'foo' => [FooNode::class, 'create'], // la classe FooNode la aggiungiamo tra un attimo
		];
	}
}

// registrazione
$this->latte->addExtension(new FooExtension);

Il nuovo compilatore è più solido e non contiene le scorciatoie di prima, quindi scrivere una macro richiede qualche riga di codice in più. Per esempio non possiamo passare direttamente una stringa di codice PHP come in Latte 2: creiamo invece una funzione. Ricordiamo che in Latte 2 la funzione avrebbe avuto più o meno questo aspetto:

// Latte 2
$set->addMacro('foo', function (Latte\MacroNode $node, Latte\PhpWriter $writer) {
	return $writer->write('echo %escape(MyClass:myFunc(%node.word, %node.array))');
});

Latte 3 procede comunque in modo molto simile, solo che MacroNode si chiama Latte\Compiler\Tag e PhpWriter si chiama Latte\Compiler\PrintContext. Soprattutto, però, c'è un passaggio intermedio in più: la funzione non restituisce direttamente codice PHP, ma un nodo, cioè un discendente di StatementNode, che entra poi a far parte dell'albero AST. E questo nodo ha un metodo print(Latte\Compiler\PrintContext $context): string che restituisce il codice PHP:

// Latte 3
class FooNode extends Latte\Compiler\Nodes\StatementNode
{
	public static function create(Latte\Compiler\Tag $tag): self
	{
		$node = new self;
		return $node;
	}

	public function print(Latte\Compiler\PrintContext $context): string
	{
		return $context->format('echo ...'); // restituisce codice PHP
	}
}

Inoltre la maschera in $context->format() non ha più le abbreviazioni %node.***: si presuppone che prima abbiate analizzato il contenuto del tag. Usiamo quindi il parser per analizzare il contenuto in variabili (sottonodi) e poi lo stampiamo:

use Latte\Compiler\Nodes\Php\Expression\ArrayNode;
use Latte\Compiler\Nodes\Php\ExpressionNode;

class FooNode extends Latte\Compiler\Nodes\StatementNode
{
	public ExpressionNode $subject;
	public ArrayNode $args;

	public static function create(Latte\Compiler\Tag $tag): self
	{
		$node = new self;
		// analisi del contenuto del tag
		$node->subject = $tag->parser->parseUnquotedStringOrExpression();
		$tag->parser->stream->tryConsume(',');
		$node->args = $tag->parser->parseArguments();
		return $node;
	}

	public function print(Latte\Compiler\PrintContext $context): string
	{
		return $context->format(
			'echo %escape(MyClass:myFunc(%node, %node));',
			$this->subject,
			$this->args,
		);
	}
}

Infine aggiungeremo il metodo getIterator() per permettere di attraversare i sottonodi durante la visita dell'albero:

class FooNode extends Latte\Compiler\Nodes\StatementNode
{
	...

	public function &getIterator(): \Generator
	{
		yield $this->subject;
		yield $this->args;
	}
}