Nette Documentation Preview

syntax
Acheminement
************

<div class=perex>

Le routeur s'occupe de tout ce qui concerne les URL afin que vous n'ayez plus à y penser. Nous allons le montrer :

- comment configurer le routeur pour que les URLs ressemblent à ce que vous voulez
- quelques remarques sur la redirection SEO
- et nous vous montrerons comment écrire votre propre routeur.

</div>


Les URL plus humaines (ou cool ou jolies URL) sont plus utilisables, plus mémorables et contribuent positivement au référencement. Nette a cette idée en tête et répond parfaitement aux souhaits des développeurs. Vous pouvez concevoir la structure de l'URL de votre application exactement comme vous le souhaitez.
Vous pouvez même la concevoir après que l'application soit prête, car cela peut se faire sans aucune modification du code ou du modèle. Elle est définie de manière élégante en [un seul endroit |#Integration], dans le routeur, et n'est pas dispersée sous forme d'annotations dans tous les présentateurs.

Le routeur de Nette est spécial car il est **bidirectionnel**, il peut à la fois décoder les URL des requêtes HTTP et créer des liens. Il joue donc un rôle essentiel dans l'[application Nette |how-it-works#Nette Application], car il décide du présentateur et de l'action qui exécuteront la requête en cours, et est également utilisé pour la [génération d'URL |creating-links] dans le modèle, etc.

Cependant, le routeur n'est pas limité à cet usage, vous pouvez l'utiliser dans des applications où les présentateurs ne sont pas du tout utilisés, pour les API REST, etc. Pour en savoir plus, consultez la section [Utilisation séparée |#separated usage].


Collection de routes .[#toc-route-collection]
=============================================

La manière la plus agréable de définir les adresses URL dans l'application est via la classe [api:Nette\Application\Routers\RouteList]. La définition consiste en une liste de ce que l'on appelle des routes, c'est-à-dire des masques d'adresses URL et leurs présentateurs et actions associés utilisant une API simple. Il n'est pas nécessaire de nommer les routes.

```php
$router = new Nette\Application\Routers\RouteList;
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('article/<id>', 'Article:view');
// ...
```

L'exemple dit que si nous ouvrons `https://any-domain.com/rss.xml` avec l'action `rss` sera affiché, si `https://domain.com/article/12` avec l'action `view` est affiché, etc. Si aucune route appropriée n'est trouvée, Nette Application répond en lançant une exception [BadRequestException |api:Nette\Application\BadRequestException], qui apparaît à l'utilisateur comme une page d'erreur 404 Not Found.


Ordre des routes .[#toc-order-of-routes]
----------------------------------------

L'ordre dans lequel les routes sont listées est **très important** car elles sont évaluées séquentiellement de haut en bas. La règle est que l'on déclare les routes **du spécifique au général** :

```php
// FAUX: 'rss.xml' correspond à la première route et comprend qu'il s'agit de <slug>.
$router->addRoute('<slug>', 'Article:view');
$router->addRoute('rss.xml', 'Feed:rss');

// BON
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('<slug>', 'Article:view');
```

Les routes sont également évaluées de haut en bas lorsque les liens sont générés :

```php
// FAUX: génère un lien vers 'Feed:rss' comme 'admin/feed/rss'.
$router->addRoute('admin/<presenter>/<action>', 'Admin:default');
$router->addRoute('rss.xml', 'Feed:rss');

// BON
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('admin/<presenter>/<action>', 'Admin:default');
```

Nous ne vous cacherons pas qu'il faut un certain savoir-faire pour construire correctement une liste. En attendant que vous vous y mettiez, le [panneau de routage |#Debugging Router] sera un outil utile.


Masque et paramètres .[#toc-mask-and-parameters]
------------------------------------------------

Le masque décrit le chemin relatif basé sur la racine du site. Le masque le plus simple est une URL statique :

```php
$router->addRoute('products', 'Products:default');
```

Les masques contiennent souvent ce que l'on appelle des **paramètres**. Ils sont placés entre des crochets (par ex. `<year>`) et sont transmis au diffuseur cible, par exemple à la méthode `renderShow(int $year)` ou au paramètre persistant `$year`:

```php
$router->addRoute('chronicle/<year>', 'History:show');
```

L'exemple indique que si nous ouvrons `https://any-domain.com/chronicle/2020` et l'action `show` avec le paramètre `year: 2020` s'afficheront.

Nous pouvons spécifier une valeur par défaut pour les paramètres directement dans le masque et ainsi il devient facultatif :

```php
$router->addRoute('chronicle/<year=2020>', 'History:show');
```

La route acceptera maintenant l'URL `https://any-domain.com/chronicle/` avec le paramètre `year: 2020`.

Bien entendu, le nom du présentateur et de l'action peut également être un paramètre. Par exemple :

```php
$router->addRoute('<presenter>/<action>', 'Home:default');
```

Cette route accepte, par exemple, une URL sous la forme `/article/edit` resp. `/catalog/list` et les traduit en présentateurs et actions `Article:edit` resp. `Catalog:list`.

Elle donne également aux paramètres `presenter` et `action` des valeurs par défaut`Home` et `default` et ils sont donc facultatifs. Ainsi, la route accepte également une URL `/article` et la traduit en `Article:default`. Ou vice versa, un lien vers `Product:default` génère un chemin `/product`, un lien vers le défaut `Home:default` génère un chemin `/`.

Le masque peut décrire non seulement le chemin relatif basé sur la racine du site, mais aussi le chemin absolu lorsqu'il commence par une barre oblique, ou même l'URL absolue entière lorsqu'elle commence par deux barres obliques :

```php
// chemin relatif vers la racine du document de l'application
$router->addRoute('<presenter>/<action>', /* ... */);

// chemin absolu, relatif au nom d'hôte du serveur
$router->addRoute('/<presenter>/<action>', /* ... */);

// URL absolue incluant le nom d'hôte (mais relative au schéma)
$router->addRoute('//<lang>.example.com/<presenter>/<action>', /* ... */);

// URL absolue incluant le schéma
$router->addRoute('https://<lang>.example.com/<presenter>/<action>', /* ... */);
```


Expressions de validation .[#toc-validation-expressions]
--------------------------------------------------------

Une condition de validation peut être spécifiée pour chaque paramètre à l'aide d'une [expression régulière |https://www.php.net/manual/en/reference.pcre.pattern.syntax.php]. Par exemple, définissons `id` pour qu'il soit uniquement numérique, en utilisant l'expression régulière `\d+`:

```php
$router->addRoute('<presenter>/<action>[/<id \d+>]', /* ... */);
```

L'expression régulière par défaut pour tous les paramètres est `[^/]+`c'est-à-dire tout sauf la barre oblique. Si un paramètre est censé correspondre à un slash également, nous définissons l'expression régulière à `.+`.

```php
// accepte https://example.com/a/b/c, le chemin est 'a/b/c'.
$router->addRoute('<path .+>', /* ... */);
```


Séquences facultatives .[#toc-optional-sequences]
-------------------------------------------------

Les crochets indiquent les parties facultatives du masque. Toute partie du masque peut être définie comme facultative, y compris celles contenant des paramètres :

```php
$router->addRoute('[<lang [a-z]{2}>/]<name>', /* ... */);

// URLs acceptées:      Paramètres:
//   /en/download        lang => en, name => download
//   /download           lang => null, name => download
```

Bien entendu, lorsqu'un paramètre fait partie d'une séquence optionnelle, il devient également optionnel. S'il n'a pas de valeur par défaut, il sera nul.

Les sections optionnelles peuvent également se trouver dans le domaine :

```php
$router->addRoute('//[<lang=en>.]example.com/<presenter>/<action>', /* ... */);
```

Les séquences peuvent être librement imbriquées et combinées :

```php
$router->addRoute(
	'[<lang [a-z]{2}>[-<sublang>]/]<name>[/page-<page=0>]',
	'Home:default',
);

// URLs acceptées:
//   /en/hello
//   /en-us/hello
//   /hello
//   /hello/page-12
```

Le générateur d'URL essaie de garder l'URL aussi courte que possible, donc ce qui peut être omis est omis. Ainsi, par exemple, un chemin `index[.html]` génère un chemin `/index`. Vous pouvez inverser ce comportement en écrivant un point d'exclamation après le crochet gauche :

```php
// accepte à la fois /hello et /hello.html, génère /hello
$router->addRoute('<name>[.html]', /* ... */);

// accepte à la fois /hello et /hello.html, génère /hello.html
$router->addRoute('<name>[!.html]', /* ... */);
```

Les paramètres optionnels (c'est-à-dire les paramètres ayant une valeur par défaut) sans crochets se comportent comme s'ils étaient enveloppés de cette façon :

```php
$router->addRoute('<presenter=Home>/<action=default>/<id=>', /* ... */);

// équivaut à:
$router->addRoute('[<presenter=Home>/[<action=default>/[<id>]]]', /* ... */);
```

Pour modifier la façon dont la barre oblique la plus à droite est générée, c'est-à-dire qu'au lieu de `/home/`, vous obtenez `/home`, ajustez la route de cette façon :

```php
$router->addRoute('[<presenter=Home>[/<action=default>[/<id>]]]', /* ... */);
```


Caractères génériques .[#toc-wildcards]
---------------------------------------

Dans le masque de chemin absolu, nous pouvons utiliser les caractères génériques suivants pour éviter, par exemple, de devoir écrire un domaine dans le masque, qui peut être différent dans l'environnement de développement et de production :

- `%tld%` = domaine de premier niveau, par exemple `com` ou `org`
- `%sld%` = domaine de deuxième niveau, par ex. `example`
- `%domain%` = domaine sans sous-domaines, par ex. `example.com`
- `%host%` = hôte complet, par ex. `www.example.com`
- `%basePath%` = chemin d'accès au répertoire racine

```php
$router->addRoute('//www.%domain%/%basePath%/<presenter>/<action>', /* ... */);
$router->addRoute('//www.%sld%.%tld%/%basePath%/<presenter>/<action', /* ... */);
```


Notation avancée .[#toc-advanced-notation]
------------------------------------------

La cible d'un itinéraire, généralement écrite sous la forme `Presenter:action`, peut également être exprimée à l'aide d'un tableau qui définit les paramètres individuels et leurs valeurs par défaut :

```php
$router->addRoute('<presenter>/<action>[/<id \d+>]', [
	'presenter' => 'Home',
	'action' => 'default',
]);
```

Pour une spécification plus détaillée, une forme encore plus étendue peut être utilisée, où en plus des valeurs par défaut, d'autres propriétés de paramètres peuvent être définies, telles qu'une expression régulière de validation (voir le paramètre `id` ) :

```php
use Nette\Routing\Route;

$router->addRoute('<presenter>/<action>[/<id>]', [
	'presenter' => [
		Route::Value => 'Home',
	],
	'action' => [
		Route::Value => 'default',
	],
	'id' => [
		Route::Pattern => '\d+',
	],
]);
```

Il est important de noter que si les paramètres définis dans le tableau ne sont pas inclus dans le masque de chemin, leurs valeurs ne peuvent pas être modifiées, même en utilisant des paramètres de requête spécifiés après un point d'interrogation dans l'URL.


Filtres et traductions .[#toc-filters-and-translations]
-------------------------------------------------------

C'est une bonne pratique d'écrire le code source en anglais, mais que faire si vous avez besoin que l'URL de votre site Web soit traduite dans une autre langue ? Des routes simples telles que :

```php
$router->addRoute('<presenter>/<action>', 'Home:default');
```

généreront des URL en anglais, comme `/product/123` ou `/cart`. Si nous voulons que les présentateurs et les actions dans l'URL soient traduits en allemand (par exemple `/produkt/123` ou `/einkaufswagen`), nous pouvons utiliser un dictionnaire de traduction. Pour l'ajouter, nous avons déjà besoin d'une variante "plus bavarde" du deuxième paramètre :

```php
use Nette\Routing\Route;

$router->addRoute('<presenter>/<action>', [
	'presenter' => [
		Route::Value => 'Home',
		Route::FilterTable => [
			// string dans l'URL => presenter
			'produkt' => 'Product',
			'einkaufswagen' => 'Cart',
			'katalog' => 'Catalog',
		],
	],
	'action' => [
		Route::Value => 'default',
		Route::FilterTable => [
			'liste' => 'list',
		],
	],
]);
```

Plusieurs clés de dictionnaire peuvent être utilisées pour le même présentateur. Elles permettront de créer différents alias pour celui-ci. La dernière clé est considérée comme la variante canonique (c'est-à-dire celle qui figurera dans l'URL générée).

La table de traduction peut être appliquée à n'importe quel paramètre de cette manière. Cependant, si la traduction n'existe pas, la valeur originale est prise. Nous pouvons modifier ce comportement en ajoutant `Route::FilterStrict => true` et la route rejettera alors l'URL si la valeur ne figure pas dans le dictionnaire.

En plus du dictionnaire de traduction sous forme de tableau, il est possible de définir des fonctions de traduction propres :

```php
use Nette\Routing\Route;

$router->addRoute('<presenter>/<action>/<id>', [
	'presenter' => [
		Route::Value => 'Home',
		Route::FilterIn => function (string $s): string { /* ... */ },
		Route::FilterOut => function (string $s): string { /* ... */ },
	],
	'action' => 'default',
	'id' => null,
]);
```

La fonction `Route::FilterIn` effectue la conversion entre le paramètre dans l'URL et la chaîne de caractères, qui est ensuite transmise au présentateur, la fonction `FilterOut` assure la conversion dans le sens inverse.

Les paramètres `presenter`, `action` et `module` ont déjà des filtres prédéfinis qui convertissent le style PascalCase resp. camelCase en kebab-case utilisé dans l'URL. La valeur par défaut des paramètres est déjà écrite dans la forme transformée, ainsi, par exemple, dans le cas d'un présentateur, on écrit `<presenter=ProductEdit>` au lieu de `<presenter=product-edit>`.


Filtres généraux .[#toc-general-filters]
----------------------------------------

Outre les filtres pour des paramètres spécifiques, vous pouvez également définir des filtres généraux qui reçoivent un tableau associatif de tous les paramètres qu'ils peuvent modifier de quelque façon que ce soit, puis retourner. Les filtres généraux sont définis sous la clé `null`.

```php
use Nette\Routing\Route;

$router->addRoute('<presenter>/<action>', [
	'presenter' => 'Home',
	'action' => 'default',
	null => [
		Route::FilterIn => function (array $params): array { /* ... */ },
		Route::FilterOut => function (array $params): array { /* ... */ },
	],
]);
```

Les filtres généraux vous donnent la possibilité d'ajuster le comportement de l'itinéraire de n'importe quelle manière. Nous pouvons les utiliser, par exemple, pour modifier des paramètres en fonction d'autres paramètres. Par exemple, la traduction `<presenter>` et `<action>` en fonction de la valeur actuelle du paramètre `<lang>`.

Si un paramètre a un filtre personnalisé défini et qu'un filtre général existe en même temps, le filtre personnalisé `FilterIn` est exécuté avant le filtre général et vice versa le filtre général `FilterOut` est exécuté avant le filtre personnalisé. Ainsi, à l'intérieur du filtre général se trouvent les valeurs des paramètres `presenter` resp. `action` écrites en style PascalCase resp. camelCase.


Drapeau unidirectionnel .[#toc-oneway-flag]
-------------------------------------------

Les routes à sens unique sont utilisées pour préserver la fonctionnalité des anciennes URL que l'application ne génère plus mais accepte toujours. Nous les signalons avec `OneWay`:

```php
// ancienne URL /product-info?id=123
$router->addRoute('product-info', 'Product:detail', $router::ONE_WAY);
// nouvelle URL /produit/123
$router->addRoute('product/<id>', 'Product:detail');
```

Lors de l'accès à l'ancienne URL, le diffuseur redirige automatiquement vers la nouvelle URL afin que les moteurs de recherche n'indexent pas ces pages deux fois (voir [SEO et canonisation |#SEO and canonization]).


Routage dynamique avec des callbacks .[#toc-dynamic-routing-with-callbacks]
---------------------------------------------------------------------------

Le routage dynamique avec rappels vous permet d'affecter directement des fonctions (rappels) aux itinéraires, qui seront exécutées lorsque le chemin spécifié est visité. Cette fonctionnalité flexible vous permet de créer rapidement et efficacement différents points d'arrivée pour votre application :

```php
$router->addRoute('test', function () {
	echo 'You are at the /test address';
});
```

Vous pouvez également définir des paramètres dans le masque, qui seront automatiquement transmis à votre callback :

```php
$router->addRoute('<lang cs|en>', function (string $lang) {
	echo match ($lang) {
		'cs' => 'Welcome to the Czech version of our website!',
		'en' => 'Welcome to the English version of our website!',
	};
});
```


Modules .[#toc-modules]
-----------------------

Si nous avons plusieurs routes qui appartiennent à un seul [module |modules], nous pouvons utiliser `withModule()` pour les regrouper :

```php
$router = new RouteList;
$router->withModule('Forum') // les routeurs suivants font partie du module Forum
	->addRoute('rss', 'Feed:rss') // le présentateur est Forum:Feed
	->addRoute('<presenter>/<action>')

	->withModule('Admin') // les routeurs suivants font partie du module Forum:Admin
		->addRoute('sign:in', 'Sign:in');
```

Une alternative est d'utiliser le paramètre `module`:

```php
// L'URL manage/dashboard/default correspond au présentateur Admin:Dashboard
$router->addRoute('manage/<presenter>/<action>', [
	'module' => 'Admin',
]);
```


Sous-domaines .[#toc-subdomains]
--------------------------------

Les collections d'itinéraires peuvent être regroupées par sous-domaines :

```php
$router = new RouteList;
$router->withDomain('example.com')
	->addRoute('rss', 'Feed:rss')
	->addRoute('<presenter>/<action>');
```

Vous pouvez également utiliser des [caractères génériques |#wildcards] dans votre nom de domaine :

```php
$router = new RouteList;
$router->withDomain('example.%tld%')
	// ...
```


Préfixe de chemin .[#toc-path-prefix]
-------------------------------------

Les collections de routes peuvent être regroupées par chemin d'accès dans l'URL :

```php
$router = new RouteList;
$router->withPath('eshop')
	->addRoute('rss', 'Feed:rss') // correspond à l'URL /eshop/rss
	->addRoute('<presenter>/<action>'); // correspond à l'URL /shop/<presenter>/<action>.
```


Combinaisons .[#toc-combinations]
---------------------------------

Les utilisations ci-dessus peuvent être combinées :

```php
$router = (new RouteList)
	->withDomain('admin.example.com')
		->withModule('Admin')
			->addRoute(/* ... */)
			->addRoute(/* ... */)
		->end()
		->withModule('Images')
			->addRoute(/* ... */)
		->end()
	->end()
	->withDomain('example.com')
		->withPath('export')
			->addRoute(/* ... */)
			// ...
```


Paramètres de la requête .[#toc-query-parameters]
-------------------------------------------------

Les masques peuvent également contenir des paramètres de requête (paramètres situés après le point d'interrogation dans l'URL). Ils ne peuvent pas définir une expression de validation, mais ils peuvent modifier le nom sous lequel ils sont transmis au diffuseur :

```php
// utiliser le paramètre de requête 'cat' comme 'categoryId' dans l'application
$router->addRoute('product ? id=<productId> & cat=<categoryId>', /* ... */);
```


Paramètres Foo .[#toc-foo-parameters]
-------------------------------------

Nous allons maintenant plus loin. Les paramètres Foo sont essentiellement des paramètres sans nom qui permettent de faire correspondre une expression régulière. La route suivante correspond à `/index`, `/index.html`, `/index.htm` et `/index.php`:

```php
$router->addRoute('index<? \.html?|\.php|>', /* ... */);
```

Il est également possible de définir explicitement une chaîne qui sera utilisée pour la génération d'URL. La chaîne doit être placée directement après le point d'interrogation. La route suivante est similaire à la précédente, mais génère `/index.html` au lieu de `/index` car la chaîne `.html` est définie comme une "valeur générée".

```php
$router->addRoute('index<?.html \.html?|\.php|>', /* ... */);
```


Intégration .[#toc-integration]
===============================

Afin de connecter notre routeur à l'application, nous devons en informer le conteneur DI. Le moyen le plus simple est de préparer la fabrique qui construira l'objet routeur et de dire à la configuration du conteneur de l'utiliser. Disons que nous écrivons une méthode dans ce but `App\Core\RouterFactory::createRouter()`:

```php
namespace App\Core;

use Nette\Application\Routers\RouteList;

class RouterFactory
{
	public static function createRouter(): RouteList
	{
		$router = new RouteList;
		$router->addRoute(/* ... */);
		return $router;
	}
}
```

Puis nous écrivons dans la [configuration |dependency-injection:services]:

```neon
services:
	- App\Core\RouterFactory::createRouter
```

Toutes les dépendances, telles qu'une connexion à une base de données, etc., sont transmises à la méthode factory en tant que paramètres en utilisant le [câblage automatique |dependency-injection:autowiring]:

```php
public static function createRouter(Nette\Database\Connection $db): RouteList
{
	// ...
}
```


SimpleRouter .[#toc-simplerouter]
=================================

Un routeur beaucoup plus simple que la collection de routes est [SimpleRouter |api:Nette\Application\Routers\SimpleRouter]. Il peut être utilisé lorsqu'il n'y a pas besoin d'un format d'URL spécifique, lorsque `mod_rewrite` (ou des alternatives) n'est pas disponible ou lorsque nous ne voulons tout simplement pas encore nous embêter avec des URL conviviales.

Il génère des adresses à peu près sous cette forme :

```
http://example.com/?presenter=Product&action=detail&id=123
```

Le paramètre du constructeur `SimpleRouter` est un présentateur et une action par défaut, c'est-à-dire une action à exécuter si nous ouvrons par exemple `http://example.com/` sans paramètres supplémentaires.

```php
// par défaut, le présentateur est 'Home' et l'action 'default'.
$router = new Nette\Application\Routers\SimpleRouter('Home:default');
```

Nous recommandons de définir SimpleRouter directement dans la [configuration |dependency-injection:services]:

```neon
services:
	- Nette\Application\Routers\SimpleRouter('Home:default')
```


Référencement et Canonisation .[#toc-seo-and-canonization]
==========================================================

Le cadre améliore le référencement (optimisation pour les moteurs de recherche) en empêchant la duplication du contenu à différentes URL. Si plusieurs adresses renvoient à une même destination, par exemple `/index` et `/index.html`, le framework détermine la première comme primaire (canonique) et redirige les autres vers elle en utilisant le code HTTP 301. Grâce à cela, les moteurs de recherche n'indexent pas les pages deux fois et ne cassent pas leur page rank. .

Ce processus est appelé canonisation. L'URL canonique est celle générée par le routeur, c'est-à-dire par la première route correspondante dans la [collection |#route-collection] sans le drapeau OneWay. Par conséquent, dans la collection, nous listons d'abord les routes primaires.

La canonisation est effectuée par le présentateur, plus dans le chapitre [canonisation |presenters#Canonization].


HTTPS .[#toc-https]
===================

Afin d'utiliser le protocole HTTPS, il est nécessaire de l'activer sur l'hébergement et de configurer le serveur.

La redirection de tout le site vers HTTPS doit être effectuée au niveau du serveur, par exemple à l'aide du fichier .htaccess dans le répertoire racine de notre application, avec le code HTTP 301. Les paramètres peuvent différer selon l'hébergement et ressemblent à quelque chose comme ceci :

```
<IfModule mod_rewrite.c>
	RewriteEngine On
	...
	RewriteCond %{HTTPS} off
	RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
	...
</IfModule>
```

Le routeur génère une URL avec le même protocole que la page a été chargée, il n'est donc pas nécessaire de définir autre chose.

Cependant, si nous avons exceptionnellement besoin que différentes routes fonctionnent sous différents protocoles, nous le mettrons dans le masque de route :

```php
// Générera une adresse HTTP
$router->addRoute('http://%host%/<presenter>/<action>', /* ... */);

// Générera une adresse HTTPS
$router->addRoute('https://%host%/<presenter>/<action>', /* ... */);
```


Routeur de débogage .[#toc-debugging-router]
============================================

La barre de routage affichée dans [Tracy Bar |tracy:] est un outil utile qui affiche une liste de routes et également les paramètres que le routeur a obtenus de l'URL.

La barre verte avec le symbole ✓ représente la route qui correspond à l'URL actuelle, les barres bleues avec les symboles ≈ indiquent les routes qui correspondraient également à l'URL si le vert ne les dépassait pas. Nous voyons le présentateur actuel & l'action plus loin.

[* routing-debugger.webp *]

En même temps, s'il y a une redirection inattendue due à la [canonicalisation |#SEO and Canonization], il est utile de regarder dans la barre *redirect* pour voir comment le routeur a compris l'URL à l'origine et pourquoi il a redirigé.

.[note]
Lors du débogage du routeur, il est recommandé d'ouvrir les Outils du développeur dans le navigateur (Ctrl+Shift+I ou Cmd+Option+I) et de désactiver le cache dans le panneau Réseau afin que les redirections n'y soient pas stockées.


Performances .[#toc-performance]
================================

Le nombre de routes affecte la vitesse du routeur. Leur nombre ne devrait certainement pas dépasser quelques dizaines. Si votre site a une structure d'URL trop compliquée, vous pouvez écrire un [routeur personnalisé |#custom router].

Si le routeur n'a pas de dépendances, par exemple sur une base de données, et que sa fabrique n'a pas d'arguments, nous pouvons sérialiser sa forme compilée directement dans un conteneur DI et ainsi rendre l'application légèrement plus rapide.

```neon
routing:
	cache: true
```


Routeur personnalisé .[#toc-custom-router]
==========================================

Les lignes suivantes sont destinées aux utilisateurs très avancés. Vous pouvez créer votre propre routeur et l'ajouter naturellement à votre collection de routes. Le routeur est une implémentation de l'interface [api:Nette\Routing\Router] avec deux méthodes :

```php
use Nette\Http\IRequest as HttpRequest;
use Nette\Http\UrlScript;

class MyRouter implements Nette\Routing\Router
{
	public function match(HttpRequest $httpRequest): ?array
	{
		// ...
	}

	public function constructUrl(array $params, UrlScript $refUrl): ?string
	{
		// ...
	}
}
```

La méthode `match` traite la [requête |http:request] courante [$httpRequest |http:request], à partir de laquelle non seulement l'URL, mais aussi les en-têtes etc. peuvent être récupérés, dans un tableau contenant le nom du diffuseur et ses paramètres. Si elle ne peut pas traiter la requête, elle renvoie null.
Lors du traitement de la demande, nous devons retourner au moins le diffuseur et l'action. Le nom du diffuseur est complet et inclut les modules éventuels :

```php
[
	'presenter' => 'Front:Home',
	'action' => 'default',
]
```

La méthode `constructUrl`, quant à elle, génère une URL absolue à partir du tableau de paramètres. Elle peut utiliser les informations du paramètre `$refUrl`, qui est l'URL actuelle.

Pour ajouter un routeur personnalisé à la collection de routes, utilisez `add()`:

```php
$router = new Nette\Application\Routers\RouteList;
$router->add($myRouter);
$router->addRoute(/* ... */);
// ...
```


Utilisation séparée .[#toc-separated-usage]
===========================================

Par utilisation séparée, nous entendons l'utilisation des capacités du routeur dans une application qui n'utilise pas Nette Application et les présentateurs. Presque tout ce que nous avons montré dans ce chapitre s'y applique, avec les différences suivantes :

- pour les collections de routes, nous utilisons la classe [api:Nette\Routing\RouteList]
- comme une simple classe de routeur [api:Nette\Routing\SimpleRouter]
- parce qu'il n'y a pas de paire `Presenter:action`, nous utilisons la [notation Advanced |#Advanced notation]

Donc encore une fois nous allons créer une méthode qui va construire un routeur, par exemple :

```php
namespace App\Core;

use Nette\Routing\RouteList;

class RouterFactory
{
	public static function createRouter(): RouteList
	{
		$router = new RouteList;
		$router->addRoute('rss.xml', [
			'controller' => 'RssFeedController',
		]);
		$router->addRoute('article/<id \d+>', [
			'controller' => 'ArticleController',
		]);
		// ...
		return $router;
	}
}
```

Si vous utilisez un conteneur DI, ce que nous recommandons, ajoutez à nouveau la méthode à la configuration et récupérez le routeur avec la requête HTTP du conteneur :

```php
$router = $container->getByType(Nette\Routing\Router::class);
$httpRequest = $container->getByType(Nette\Http\IRequest::class);
```

Ou bien nous créerons directement des objets :

```php
$router = App\Core\RouterFactory::createRouter();
$httpRequest = (new Nette\Http\RequestFactory)->fromGlobals();
```

Il faut maintenant laisser le routeur fonctionner :

```php
$params = $router->match($httpRequest);
if ($params === null) {
	// aucune route correspondante trouvée, nous enverrons une erreur 404
	exit;
}

// nous traitons les paramètres reçus
$controller = $params['controller'];
// ...
```

Et vice versa, nous allons utiliser le routeur pour créer le lien :

```php
$params = ['controller' => 'ArticleController', 'id' => 123];
$url = $router->constructUrl($params, $httpRequest->getUrl());
```


{{composer: nette/router}}

Acheminement

Le routeur s'occupe de tout ce qui concerne les URL afin que vous n'ayez plus à y penser. Nous allons le montrer :

  • comment configurer le routeur pour que les URLs ressemblent à ce que vous voulez
  • quelques remarques sur la redirection SEO
  • et nous vous montrerons comment écrire votre propre routeur.

Les URL plus humaines (ou cool ou jolies URL) sont plus utilisables, plus mémorables et contribuent positivement au référencement. Nette a cette idée en tête et répond parfaitement aux souhaits des développeurs. Vous pouvez concevoir la structure de l'URL de votre application exactement comme vous le souhaitez. Vous pouvez même la concevoir après que l'application soit prête, car cela peut se faire sans aucune modification du code ou du modèle. Elle est définie de manière élégante en un seul endroit, dans le routeur, et n'est pas dispersée sous forme d'annotations dans tous les présentateurs.

Le routeur de Nette est spécial car il est bidirectionnel, il peut à la fois décoder les URL des requêtes HTTP et créer des liens. Il joue donc un rôle essentiel dans l'application Nette, car il décide du présentateur et de l'action qui exécuteront la requête en cours, et est également utilisé pour la génération d'URL dans le modèle, etc.

Cependant, le routeur n'est pas limité à cet usage, vous pouvez l'utiliser dans des applications où les présentateurs ne sont pas du tout utilisés, pour les API REST, etc. Pour en savoir plus, consultez la section Utilisation séparée.

Collection de routes

La manière la plus agréable de définir les adresses URL dans l'application est via la classe Nette\Application\Routers\RouteList. La définition consiste en une liste de ce que l'on appelle des routes, c'est-à-dire des masques d'adresses URL et leurs présentateurs et actions associés utilisant une API simple. Il n'est pas nécessaire de nommer les routes.

$router = new Nette\Application\Routers\RouteList;
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('article/<id>', 'Article:view');
// ...

L'exemple dit que si nous ouvrons https://any-domain.com/rss.xml avec l'action rss sera affiché, si https://domain.com/article/12 avec l'action view est affiché, etc. Si aucune route appropriée n'est trouvée, Nette Application répond en lançant une exception BadRequestException, qui apparaît à l'utilisateur comme une page d'erreur 404 Not Found.

Ordre des routes

L'ordre dans lequel les routes sont listées est très important car elles sont évaluées séquentiellement de haut en bas. La règle est que l'on déclare les routes du spécifique au général :

// FAUX: 'rss.xml' correspond à la première route et comprend qu'il s'agit de <slug>.
$router->addRoute('<slug>', 'Article:view');
$router->addRoute('rss.xml', 'Feed:rss');

// BON
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('<slug>', 'Article:view');

Les routes sont également évaluées de haut en bas lorsque les liens sont générés :

// FAUX: génère un lien vers 'Feed:rss' comme 'admin/feed/rss'.
$router->addRoute('admin/<presenter>/<action>', 'Admin:default');
$router->addRoute('rss.xml', 'Feed:rss');

// BON
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('admin/<presenter>/<action>', 'Admin:default');

Nous ne vous cacherons pas qu'il faut un certain savoir-faire pour construire correctement une liste. En attendant que vous vous y mettiez, le panneau de routage sera un outil utile.

Masque et paramètres

Le masque décrit le chemin relatif basé sur la racine du site. Le masque le plus simple est une URL statique :

$router->addRoute('products', 'Products:default');

Les masques contiennent souvent ce que l'on appelle des paramètres. Ils sont placés entre des crochets (par ex. <year>) et sont transmis au diffuseur cible, par exemple à la méthode renderShow(int $year) ou au paramètre persistant $year:

$router->addRoute('chronicle/<year>', 'History:show');

L'exemple indique que si nous ouvrons https://any-domain.com/chronicle/2020 et l'action show avec le paramètre year: 2020 s'afficheront.

Nous pouvons spécifier une valeur par défaut pour les paramètres directement dans le masque et ainsi il devient facultatif :

$router->addRoute('chronicle/<year=2020>', 'History:show');

La route acceptera maintenant l'URL https://any-domain.com/chronicle/ avec le paramètre year: 2020.

Bien entendu, le nom du présentateur et de l'action peut également être un paramètre. Par exemple :

$router->addRoute('<presenter>/<action>', 'Home:default');

Cette route accepte, par exemple, une URL sous la forme /article/edit resp. /catalog/list et les traduit en présentateurs et actions Article:edit resp. Catalog:list.

Elle donne également aux paramètres presenter et action des valeurs par défautHome et default et ils sont donc facultatifs. Ainsi, la route accepte également une URL /article et la traduit en Article:default. Ou vice versa, un lien vers Product:default génère un chemin /product, un lien vers le défaut Home:default génère un chemin /.

Le masque peut décrire non seulement le chemin relatif basé sur la racine du site, mais aussi le chemin absolu lorsqu'il commence par une barre oblique, ou même l'URL absolue entière lorsqu'elle commence par deux barres obliques :

// chemin relatif vers la racine du document de l'application
$router->addRoute('<presenter>/<action>', /* ... */);

// chemin absolu, relatif au nom d'hôte du serveur
$router->addRoute('/<presenter>/<action>', /* ... */);

// URL absolue incluant le nom d'hôte (mais relative au schéma)
$router->addRoute('//<lang>.example.com/<presenter>/<action>', /* ... */);

// URL absolue incluant le schéma
$router->addRoute('https://<lang>.example.com/<presenter>/<action>', /* ... */);

Expressions de validation

Une condition de validation peut être spécifiée pour chaque paramètre à l'aide d'une expression régulière. Par exemple, définissons id pour qu'il soit uniquement numérique, en utilisant l'expression régulière \d+:

$router->addRoute('<presenter>/<action>[/<id \d+>]', /* ... */);

L'expression régulière par défaut pour tous les paramètres est [^/]+c'est-à-dire tout sauf la barre oblique. Si un paramètre est censé correspondre à un slash également, nous définissons l'expression régulière à .+.

// accepte https://example.com/a/b/c, le chemin est 'a/b/c'.
$router->addRoute('<path .+>', /* ... */);

Séquences facultatives

Les crochets indiquent les parties facultatives du masque. Toute partie du masque peut être définie comme facultative, y compris celles contenant des paramètres :

$router->addRoute('[<lang [a-z]{2}>/]<name>', /* ... */);

// URLs acceptées:      Paramètres:
//   /en/download        lang => en, name => download
//   /download           lang => null, name => download

Bien entendu, lorsqu'un paramètre fait partie d'une séquence optionnelle, il devient également optionnel. S'il n'a pas de valeur par défaut, il sera nul.

Les sections optionnelles peuvent également se trouver dans le domaine :

$router->addRoute('//[<lang=en>.]example.com/<presenter>/<action>', /* ... */);

Les séquences peuvent être librement imbriquées et combinées :

$router->addRoute(
	'[<lang [a-z]{2}>[-<sublang>]/]<name>[/page-<page=0>]',
	'Home:default',
);

// URLs acceptées:
//   /en/hello
//   /en-us/hello
//   /hello
//   /hello/page-12

Le générateur d'URL essaie de garder l'URL aussi courte que possible, donc ce qui peut être omis est omis. Ainsi, par exemple, un chemin index[.html] génère un chemin /index. Vous pouvez inverser ce comportement en écrivant un point d'exclamation après le crochet gauche :

// accepte à la fois /hello et /hello.html, génère /hello
$router->addRoute('<name>[.html]', /* ... */);

// accepte à la fois /hello et /hello.html, génère /hello.html
$router->addRoute('<name>[!.html]', /* ... */);

Les paramètres optionnels (c'est-à-dire les paramètres ayant une valeur par défaut) sans crochets se comportent comme s'ils étaient enveloppés de cette façon :

$router->addRoute('<presenter=Home>/<action=default>/<id=>', /* ... */);

// équivaut à:
$router->addRoute('[<presenter=Home>/[<action=default>/[<id>]]]', /* ... */);

Pour modifier la façon dont la barre oblique la plus à droite est générée, c'est-à-dire qu'au lieu de /home/, vous obtenez /home, ajustez la route de cette façon :

$router->addRoute('[<presenter=Home>[/<action=default>[/<id>]]]', /* ... */);

Caractères génériques

Dans le masque de chemin absolu, nous pouvons utiliser les caractères génériques suivants pour éviter, par exemple, de devoir écrire un domaine dans le masque, qui peut être différent dans l'environnement de développement et de production :

  • %tld% = domaine de premier niveau, par exemple com ou org
  • %sld% = domaine de deuxième niveau, par ex. example
  • %domain% = domaine sans sous-domaines, par ex. example.com
  • %host% = hôte complet, par ex. www.example.com
  • %basePath% = chemin d'accès au répertoire racine
$router->addRoute('//www.%domain%/%basePath%/<presenter>/<action>', /* ... */);
$router->addRoute('//www.%sld%.%tld%/%basePath%/<presenter>/<action', /* ... */);

Notation avancée

La cible d'un itinéraire, généralement écrite sous la forme Presenter:action, peut également être exprimée à l'aide d'un tableau qui définit les paramètres individuels et leurs valeurs par défaut :

$router->addRoute('<presenter>/<action>[/<id \d+>]', [
	'presenter' => 'Home',
	'action' => 'default',
]);

Pour une spécification plus détaillée, une forme encore plus étendue peut être utilisée, où en plus des valeurs par défaut, d'autres propriétés de paramètres peuvent être définies, telles qu'une expression régulière de validation (voir le paramètre id ) :

use Nette\Routing\Route;

$router->addRoute('<presenter>/<action>[/<id>]', [
	'presenter' => [
		Route::Value => 'Home',
	],
	'action' => [
		Route::Value => 'default',
	],
	'id' => [
		Route::Pattern => '\d+',
	],
]);

Il est important de noter que si les paramètres définis dans le tableau ne sont pas inclus dans le masque de chemin, leurs valeurs ne peuvent pas être modifiées, même en utilisant des paramètres de requête spécifiés après un point d'interrogation dans l'URL.

Filtres et traductions

C'est une bonne pratique d'écrire le code source en anglais, mais que faire si vous avez besoin que l'URL de votre site Web soit traduite dans une autre langue ? Des routes simples telles que :

$router->addRoute('<presenter>/<action>', 'Home:default');

généreront des URL en anglais, comme /product/123 ou /cart. Si nous voulons que les présentateurs et les actions dans l'URL soient traduits en allemand (par exemple /produkt/123 ou /einkaufswagen), nous pouvons utiliser un dictionnaire de traduction. Pour l'ajouter, nous avons déjà besoin d'une variante „plus bavarde“ du deuxième paramètre :

use Nette\Routing\Route;

$router->addRoute('<presenter>/<action>', [
	'presenter' => [
		Route::Value => 'Home',
		Route::FilterTable => [
			// string dans l'URL => presenter
			'produkt' => 'Product',
			'einkaufswagen' => 'Cart',
			'katalog' => 'Catalog',
		],
	],
	'action' => [
		Route::Value => 'default',
		Route::FilterTable => [
			'liste' => 'list',
		],
	],
]);

Plusieurs clés de dictionnaire peuvent être utilisées pour le même présentateur. Elles permettront de créer différents alias pour celui-ci. La dernière clé est considérée comme la variante canonique (c'est-à-dire celle qui figurera dans l'URL générée).

La table de traduction peut être appliquée à n'importe quel paramètre de cette manière. Cependant, si la traduction n'existe pas, la valeur originale est prise. Nous pouvons modifier ce comportement en ajoutant Route::FilterStrict => true et la route rejettera alors l'URL si la valeur ne figure pas dans le dictionnaire.

En plus du dictionnaire de traduction sous forme de tableau, il est possible de définir des fonctions de traduction propres :

use Nette\Routing\Route;

$router->addRoute('<presenter>/<action>/<id>', [
	'presenter' => [
		Route::Value => 'Home',
		Route::FilterIn => function (string $s): string { /* ... */ },
		Route::FilterOut => function (string $s): string { /* ... */ },
	],
	'action' => 'default',
	'id' => null,
]);

La fonction Route::FilterIn effectue la conversion entre le paramètre dans l'URL et la chaîne de caractères, qui est ensuite transmise au présentateur, la fonction FilterOut assure la conversion dans le sens inverse.

Les paramètres presenter, action et module ont déjà des filtres prédéfinis qui convertissent le style PascalCase resp. camelCase en kebab-case utilisé dans l'URL. La valeur par défaut des paramètres est déjà écrite dans la forme transformée, ainsi, par exemple, dans le cas d'un présentateur, on écrit <presenter=ProductEdit> au lieu de <presenter=product-edit>.

Filtres généraux

Outre les filtres pour des paramètres spécifiques, vous pouvez également définir des filtres généraux qui reçoivent un tableau associatif de tous les paramètres qu'ils peuvent modifier de quelque façon que ce soit, puis retourner. Les filtres généraux sont définis sous la clé null.

use Nette\Routing\Route;

$router->addRoute('<presenter>/<action>', [
	'presenter' => 'Home',
	'action' => 'default',
	null => [
		Route::FilterIn => function (array $params): array { /* ... */ },
		Route::FilterOut => function (array $params): array { /* ... */ },
	],
]);

Les filtres généraux vous donnent la possibilité d'ajuster le comportement de l'itinéraire de n'importe quelle manière. Nous pouvons les utiliser, par exemple, pour modifier des paramètres en fonction d'autres paramètres. Par exemple, la traduction <presenter> et <action> en fonction de la valeur actuelle du paramètre <lang>.

Si un paramètre a un filtre personnalisé défini et qu'un filtre général existe en même temps, le filtre personnalisé FilterIn est exécuté avant le filtre général et vice versa le filtre général FilterOut est exécuté avant le filtre personnalisé. Ainsi, à l'intérieur du filtre général se trouvent les valeurs des paramètres presenter resp. action écrites en style PascalCase resp. camelCase.

Drapeau unidirectionnel

Les routes à sens unique sont utilisées pour préserver la fonctionnalité des anciennes URL que l'application ne génère plus mais accepte toujours. Nous les signalons avec OneWay:

// ancienne URL /product-info?id=123
$router->addRoute('product-info', 'Product:detail', $router::ONE_WAY);
// nouvelle URL /produit/123
$router->addRoute('product/<id>', 'Product:detail');

Lors de l'accès à l'ancienne URL, le diffuseur redirige automatiquement vers la nouvelle URL afin que les moteurs de recherche n'indexent pas ces pages deux fois (voir SEO et canonisation).

Routage dynamique avec des callbacks

Le routage dynamique avec rappels vous permet d'affecter directement des fonctions (rappels) aux itinéraires, qui seront exécutées lorsque le chemin spécifié est visité. Cette fonctionnalité flexible vous permet de créer rapidement et efficacement différents points d'arrivée pour votre application :

$router->addRoute('test', function () {
	echo 'You are at the /test address';
});

Vous pouvez également définir des paramètres dans le masque, qui seront automatiquement transmis à votre callback :

$router->addRoute('<lang cs|en>', function (string $lang) {
	echo match ($lang) {
		'cs' => 'Welcome to the Czech version of our website!',
		'en' => 'Welcome to the English version of our website!',
	};
});

Modules

Si nous avons plusieurs routes qui appartiennent à un seul module, nous pouvons utiliser withModule() pour les regrouper :

$router = new RouteList;
$router->withModule('Forum') // les routeurs suivants font partie du module Forum
	->addRoute('rss', 'Feed:rss') // le présentateur est Forum:Feed
	->addRoute('<presenter>/<action>')

	->withModule('Admin') // les routeurs suivants font partie du module Forum:Admin
		->addRoute('sign:in', 'Sign:in');

Une alternative est d'utiliser le paramètre module:

// L'URL manage/dashboard/default correspond au présentateur Admin:Dashboard
$router->addRoute('manage/<presenter>/<action>', [
	'module' => 'Admin',
]);

Sous-domaines

Les collections d'itinéraires peuvent être regroupées par sous-domaines :

$router = new RouteList;
$router->withDomain('example.com')
	->addRoute('rss', 'Feed:rss')
	->addRoute('<presenter>/<action>');

Vous pouvez également utiliser des caractères génériques dans votre nom de domaine :

$router = new RouteList;
$router->withDomain('example.%tld%')
	// ...

Préfixe de chemin

Les collections de routes peuvent être regroupées par chemin d'accès dans l'URL :

$router = new RouteList;
$router->withPath('eshop')
	->addRoute('rss', 'Feed:rss') // correspond à l'URL /eshop/rss
	->addRoute('<presenter>/<action>'); // correspond à l'URL /shop/<presenter>/<action>.

Combinaisons

Les utilisations ci-dessus peuvent être combinées :

$router = (new RouteList)
	->withDomain('admin.example.com')
		->withModule('Admin')
			->addRoute(/* ... */)
			->addRoute(/* ... */)
		->end()
		->withModule('Images')
			->addRoute(/* ... */)
		->end()
	->end()
	->withDomain('example.com')
		->withPath('export')
			->addRoute(/* ... */)
			// ...

Paramètres de la requête

Les masques peuvent également contenir des paramètres de requête (paramètres situés après le point d'interrogation dans l'URL). Ils ne peuvent pas définir une expression de validation, mais ils peuvent modifier le nom sous lequel ils sont transmis au diffuseur :

// utiliser le paramètre de requête 'cat' comme 'categoryId' dans l'application
$router->addRoute('product ? id=<productId> & cat=<categoryId>', /* ... */);

Paramètres Foo

Nous allons maintenant plus loin. Les paramètres Foo sont essentiellement des paramètres sans nom qui permettent de faire correspondre une expression régulière. La route suivante correspond à /index, /index.html, /index.htm et /index.php:

$router->addRoute('index<? \.html?|\.php|>', /* ... */);

Il est également possible de définir explicitement une chaîne qui sera utilisée pour la génération d'URL. La chaîne doit être placée directement après le point d'interrogation. La route suivante est similaire à la précédente, mais génère /index.html au lieu de /index car la chaîne .html est définie comme une „valeur générée“.

$router->addRoute('index<?.html \.html?|\.php|>', /* ... */);

Intégration

Afin de connecter notre routeur à l'application, nous devons en informer le conteneur DI. Le moyen le plus simple est de préparer la fabrique qui construira l'objet routeur et de dire à la configuration du conteneur de l'utiliser. Disons que nous écrivons une méthode dans ce but App\Core\RouterFactory::createRouter():

namespace App\Core;

use Nette\Application\Routers\RouteList;

class RouterFactory
{
	public static function createRouter(): RouteList
	{
		$router = new RouteList;
		$router->addRoute(/* ... */);
		return $router;
	}
}

Puis nous écrivons dans la configuration:

services:
	- App\Core\RouterFactory::createRouter

Toutes les dépendances, telles qu'une connexion à une base de données, etc., sont transmises à la méthode factory en tant que paramètres en utilisant le câblage automatique:

public static function createRouter(Nette\Database\Connection $db): RouteList
{
	// ...
}

SimpleRouter

Un routeur beaucoup plus simple que la collection de routes est SimpleRouter. Il peut être utilisé lorsqu'il n'y a pas besoin d'un format d'URL spécifique, lorsque mod_rewrite (ou des alternatives) n'est pas disponible ou lorsque nous ne voulons tout simplement pas encore nous embêter avec des URL conviviales.

Il génère des adresses à peu près sous cette forme :

http://example.com/?presenter=Product&action=detail&id=123

Le paramètre du constructeur SimpleRouter est un présentateur et une action par défaut, c'est-à-dire une action à exécuter si nous ouvrons par exemple http://example.com/ sans paramètres supplémentaires.

// par défaut, le présentateur est 'Home' et l'action 'default'.
$router = new Nette\Application\Routers\SimpleRouter('Home:default');

Nous recommandons de définir SimpleRouter directement dans la configuration:

services:
	- Nette\Application\Routers\SimpleRouter('Home:default')

Référencement et Canonisation

Le cadre améliore le référencement (optimisation pour les moteurs de recherche) en empêchant la duplication du contenu à différentes URL. Si plusieurs adresses renvoient à une même destination, par exemple /index et /index.html, le framework détermine la première comme primaire (canonique) et redirige les autres vers elle en utilisant le code HTTP 301. Grâce à cela, les moteurs de recherche n'indexent pas les pages deux fois et ne cassent pas leur page rank. .

Ce processus est appelé canonisation. L'URL canonique est celle générée par le routeur, c'est-à-dire par la première route correspondante dans la collection sans le drapeau OneWay. Par conséquent, dans la collection, nous listons d'abord les routes primaires.

La canonisation est effectuée par le présentateur, plus dans le chapitre canonisation.

HTTPS

Afin d'utiliser le protocole HTTPS, il est nécessaire de l'activer sur l'hébergement et de configurer le serveur.

La redirection de tout le site vers HTTPS doit être effectuée au niveau du serveur, par exemple à l'aide du fichier .htaccess dans le répertoire racine de notre application, avec le code HTTP 301. Les paramètres peuvent différer selon l'hébergement et ressemblent à quelque chose comme ceci :

<IfModule mod_rewrite.c>
	RewriteEngine On
	...
	RewriteCond %{HTTPS} off
	RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
	...
</IfModule>

Le routeur génère une URL avec le même protocole que la page a été chargée, il n'est donc pas nécessaire de définir autre chose.

Cependant, si nous avons exceptionnellement besoin que différentes routes fonctionnent sous différents protocoles, nous le mettrons dans le masque de route :

// Générera une adresse HTTP
$router->addRoute('http://%host%/<presenter>/<action>', /* ... */);

// Générera une adresse HTTPS
$router->addRoute('https://%host%/<presenter>/<action>', /* ... */);

Routeur de débogage

La barre de routage affichée dans Tracy Bar est un outil utile qui affiche une liste de routes et également les paramètres que le routeur a obtenus de l'URL.

La barre verte avec le symbole ✓ représente la route qui correspond à l'URL actuelle, les barres bleues avec les symboles ≈ indiquent les routes qui correspondraient également à l'URL si le vert ne les dépassait pas. Nous voyons le présentateur actuel & l'action plus loin.

En même temps, s'il y a une redirection inattendue due à la canonicalisation, il est utile de regarder dans la barre redirect pour voir comment le routeur a compris l'URL à l'origine et pourquoi il a redirigé.

Lors du débogage du routeur, il est recommandé d'ouvrir les Outils du développeur dans le navigateur (Ctrl+Shift+I ou Cmd+Option+I) et de désactiver le cache dans le panneau Réseau afin que les redirections n'y soient pas stockées.

Performances

Le nombre de routes affecte la vitesse du routeur. Leur nombre ne devrait certainement pas dépasser quelques dizaines. Si votre site a une structure d'URL trop compliquée, vous pouvez écrire un routeur personnalisé.

Si le routeur n'a pas de dépendances, par exemple sur une base de données, et que sa fabrique n'a pas d'arguments, nous pouvons sérialiser sa forme compilée directement dans un conteneur DI et ainsi rendre l'application légèrement plus rapide.

routing:
	cache: true

Routeur personnalisé

Les lignes suivantes sont destinées aux utilisateurs très avancés. Vous pouvez créer votre propre routeur et l'ajouter naturellement à votre collection de routes. Le routeur est une implémentation de l'interface Nette\Routing\Router avec deux méthodes :

use Nette\Http\IRequest as HttpRequest;
use Nette\Http\UrlScript;

class MyRouter implements Nette\Routing\Router
{
	public function match(HttpRequest $httpRequest): ?array
	{
		// ...
	}

	public function constructUrl(array $params, UrlScript $refUrl): ?string
	{
		// ...
	}
}

La méthode match traite la requête courante $httpRequest, à partir de laquelle non seulement l'URL, mais aussi les en-têtes etc. peuvent être récupérés, dans un tableau contenant le nom du diffuseur et ses paramètres. Si elle ne peut pas traiter la requête, elle renvoie null. Lors du traitement de la demande, nous devons retourner au moins le diffuseur et l'action. Le nom du diffuseur est complet et inclut les modules éventuels :

[
	'presenter' => 'Front:Home',
	'action' => 'default',
]

La méthode constructUrl, quant à elle, génère une URL absolue à partir du tableau de paramètres. Elle peut utiliser les informations du paramètre $refUrl, qui est l'URL actuelle.

Pour ajouter un routeur personnalisé à la collection de routes, utilisez add():

$router = new Nette\Application\Routers\RouteList;
$router->add($myRouter);
$router->addRoute(/* ... */);
// ...

Utilisation séparée

Par utilisation séparée, nous entendons l'utilisation des capacités du routeur dans une application qui n'utilise pas Nette Application et les présentateurs. Presque tout ce que nous avons montré dans ce chapitre s'y applique, avec les différences suivantes :

Donc encore une fois nous allons créer une méthode qui va construire un routeur, par exemple :

namespace App\Core;

use Nette\Routing\RouteList;

class RouterFactory
{
	public static function createRouter(): RouteList
	{
		$router = new RouteList;
		$router->addRoute('rss.xml', [
			'controller' => 'RssFeedController',
		]);
		$router->addRoute('article/<id \d+>', [
			'controller' => 'ArticleController',
		]);
		// ...
		return $router;
	}
}

Si vous utilisez un conteneur DI, ce que nous recommandons, ajoutez à nouveau la méthode à la configuration et récupérez le routeur avec la requête HTTP du conteneur :

$router = $container->getByType(Nette\Routing\Router::class);
$httpRequest = $container->getByType(Nette\Http\IRequest::class);

Ou bien nous créerons directement des objets :

$router = App\Core\RouterFactory::createRouter();
$httpRequest = (new Nette\Http\RequestFactory)->fromGlobals();

Il faut maintenant laisser le routeur fonctionner :

$params = $router->match($httpRequest);
if ($params === null) {
	// aucune route correspondante trouvée, nous enverrons une erreur 404
	exit;
}

// nous traitons les paramètres reçus
$controller = $params['controller'];
// ...

Et vice versa, nous allons utiliser le routeur pour créer le lien :

$params = ['controller' => 'ArticleController', 'id' => 123];
$url = $router->constructUrl($params, $httpRequest->getUrl());