PHPackages                             nette/routing - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [HTTP &amp; Networking](/categories/http)
4. /
5. nette/routing

ActiveLibrary[HTTP &amp; Networking](/categories/http)

nette/routing
=============

Nette Routing: two-ways URL conversion

v3.1.3(2mo ago)25712.0M↓27.2%4[2 PRs](https://github.com/nette/routing/pulls)20BSD-3-ClausePHPPHP 8.1 - 8.5CI passing

Since Feb 13Pushed 2mo ago16 watchersCompare

[ Source](https://github.com/nette/routing)[ Packagist](https://packagist.org/packages/nette/routing)[ Docs](https://nette.org)[ RSS](/packages/nette-routing/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (10)Dependencies (7)Versions (14)Used By (20)

Nette Routing: two-ways URL conversion
======================================

[](#nette-routing-two-ways-url-conversion)

[![Downloads this Month](https://camo.githubusercontent.com/ab182d90f621c96a450a0f6837da8fff60fb58ea9d4a7f882287e1ad4ffca0b8/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f646d2f6e657474652f726f7574696e672e737667)](https://packagist.org/packages/nette/routing)[![Tests](https://github.com/nette/routing/workflows/Tests/badge.svg?branch=master)](https://github.com/nette/routing/actions)[![Coverage Status](https://camo.githubusercontent.com/155bc2350c0ed0db9af9018a5473562e00aa1d0dd0023416e72b399a4b0f090b/68747470733a2f2f636f766572616c6c732e696f2f7265706f732f6769746875622f6e657474652f726f7574696e672f62616467652e7376673f6272616e63683d6d6173746572)](https://coveralls.io/github/nette/routing?branch=master)

Introduction
============

[](#introduction)

The router is responsible for everything about URLs so that you no longer have to think about them. We will show:

- how to set up the router so that the URLs look like you want
- a few notes about SEO redirection
- and we'll show you how to write your own router

It requires PHP version 8.2 and supports PHP up to 8.5.

Documentation can be found on the [website](https://doc.nette.org/routing).

[Support Me](https://github.com/sponsors/dg)
--------------------------------------------

[](#support-me)

Do you like Nette Routing? Are you looking forward to the new features?

[![Buy me a coffee](https://camo.githubusercontent.com/afa7c20ccaac10ac4f1f51669bafb212856b932e0c8b276cb290336cf08624b8/68747470733a2f2f66696c65732e6e657474652e6f72672f69636f6e732f646f6e6174696f6e2d332e737667)](https://github.com/sponsors/dg)

Thank you!

Basics
======

[](#basics)

More human URLs (or cool or pretty URLs) are more usable, more memorable, and contribute positively to SEO. Nette Framework keeps this in mind and fully meets developers' desires.

Let's start a little technically. A router is an object that implements the [Nette\\Routing\\Router](https://api.nette.org/routing/Nette/Routing/Router.html) interface, which can decompose a URL into an array of parameters (method `match`) and, conversely, build a URL from an array of parameters (method `constructUrl`). Therefore, it is also said that the router is bidirectional. Nette brings a very elegant way to define how the URLs of your application look like.

You can use it in the same way in completely different cases, for the REST API, for applications where controllers are not used at all, etc.

Thus, routing is a separate and sophisticated layer of the application, thanks to which the look of URL addresses can be easily designed or changed when the entire application is ready, because it can be done without modification of the code or templates. Which gives developers huge freedom.

Route Collection
================

[](#route-collection)

The most pleasant way to define the URL addresses in the application is via the class [Nette\\Routing\\RouteList](https://api.nette.org/routing/Nette/Routing/RouteList.html). The big advantage is that the whole router is defined in one place and is not so scattered in the form of annotations in all controllers.

The definition consists of a list of so-called routes, ie masks of URL addresses and their associated controllers and actions using a simple API. We do not have to name the routes.

```
$router = new Nette\Routing\RouteList;
$router->addRoute('rss.xml', [
	'controller' => 'RssFeedController',
]);
$router->addRoute('article/', [
	'controller' => 'ArticleController',
]);
...
```

Order of routes is important because they are tried sequentially from the first one to the last one. Basic rule is to **declare routes from the most specific to the most general**.

Now we have to let the router to work:

```
$params = $router->match($httpRequest);
if ($params === null) {
	// no matching route found, we will send a 404 error
	exit;
}

// we process the received parameters
$controller = $params['controller'];
...
```

And vice versa, we will use the router to create the link:

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

Mask and Parameters
-------------------

[](#mask-and-parameters)

The mask describes the relative path based on the site root. The simplest mask is a static URL:

```
$router->addRoute('products', ...);
```

Often masks contain so-called **parameters**. They are enclosed in angle brackets (e.g. ``).

```
$router->addRoute('chronicle/', ...);
```

We can specify a default value for the parameters directly in the mask and thus it becomes optional:

```
$router->addRoute('chronicle/', ...);
```

The route will now accept the URL `https://any-domain.com/chronicle/`, which will again display with parameter `year: 2020`.

The mask can describe not only the relative path based on the site root, but also the absolute path when it begins with a slash, or even the entire absolute URL when it begins with two slashes:

```
// relative path to application document root
$router->addRoute('/', ...);

// absolute path, relative to server hostname
$router->addRoute('//', ...);

// absolute URL including hostname (but scheme-relative)
$router->addRoute('//.example.com//', ...);

// absolute URL including schema
$router->addRoute('https://.example.com//', ...);
```

Validation Expressions
----------------------

[](#validation-expressions)

A validation condition can be specified for each parameter using [regular expression ](https://www.php.net/manual/en/reference.pcre.pattern.syntax.php). For example, let's set `id` to be only numerical, using `\d+` regexp:

```
$router->addRoute('/[/]', ...);
```

The default regular expression for all parameters is `[^/]+`, ie everything except the slash. If a parameter is supposed to match a slash as well, we set the regular expression to `.+`.

```
// accepts https://example.com/a/b/c, path is 'a/b/c'
$router->addRoute('', ...);
```

Optional Sequences
------------------

[](#optional-sequences)

Square brackets denote optional parts of mask. Any part of mask may be set as optional, including those containing parameters:

```
$router->addRoute('[/]', ...);

// Accepted URLs:      Parameters:
//   /en/download        lang => en, name => download
//   /download           lang => null, name => download
```

Of course, when a parameter is part of an optional sequence, it also becomes optional. If it does not have a default value, it will be null.

Optional sections can also be in the domain:

```
$router->addRoute('//[.]example.com//', ...);
```

Sequences may be freely nested and combined:

```
$router->addRoute(
	'[[-]/][/page-]',
	...
);

// Accepted URLs:
//   /cs/hello
//   /en-us/hello
//   /hello
//   /hello/page-12
```

URL generator tries to keep the URL as short as possible, so what can be omitted is omitted. Therefore, for example, a route `index[.html]` generates a path `/index`. You can reverse this behavior by writing an exclamation mark after the left square bracket:

```
// accepts both /hello and /hello.html, generates /hello
$router->addRoute('[.html]', ...);

// accepts both /hello and /hello.html, generates /hello.html
$router->addRoute('[!.html]', ...);
```

Optional parameters (ie. parameters having default value) without square brackets do behave as if wrapped like this:

```
$router->addRoute('//', ...);

// equals to:
$router->addRoute('[/[/[]]]', ...);
```

To change how the rightmost slash is generated, i.e. instead of `/homepage/` get a `/homepage`, adjust the route this way:

```
$router->addRoute('[[/[/]]]', ...);
```

Wildcards
---------

[](#wildcards)

In the absolute path mask, we can use the following wildcards to avoid, for example, the need to write a domain to the mask, which may differ in the development and production environment:

- `%tld%` = top level domain, e.g. `com` or `org`
- `%sld%` = second level domain, e.g. `example`
- `%domain%` = domain without subdomains, e.g. `example.com`
- `%host%` = whole host, e.g. `www.example.com`
- `%basePath%` = path to the root directory

```
$router->addRoute('//www.%domain%/%basePath%//', ...);
$router->addRoute('//www.%sld%.%tld%/%basePath%//addRoute('/', [...]);
```

will generate English URLs, such as `/product/123` or `/cart`. If we want to have controllers and actions in the URL translated to Deutsch (e.g. `/produkt/123` or `/einkaufswagen`), we can use a translation dictionary. To add it, we already need a "more talkative" variant of the second parameter:

```
use Nette\Routing\Route;

$router->addRoute('/', [
	'controller' => [
		Route::Value => 'Homepage',
		Route::FilterTable => [
			// string in URL => controller
			'produkt' => 'Product',
			'einkaufswagen' => 'Cart',
			'katalog' => 'Catalog',
		],
	],
	'action' => [
		Route::Value => 'default',
		Route::FilterTable => [
			'liste' => 'list',
		],
	],
]);
```

Multiple dictionary keys can by used for the same controller. They will create various aliases for it. The last key is considered to be the canonical variant (i.e. the one that will be in the generated URL).

The translation table can be applied to any parameter in this way. However, if the translation does not exist, the original value is taken. We can change this behavior by adding `Router::FILTER_STRICT => true` and the route will then reject the URL if the value is not in the dictionary.

In addition to the translation dictionary in the form of an array, it is possible to set own translation functions:

```
use Nette\Routing\Route;

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

The function `Route::FILTER_IN` converts between the parameter in the URL and the string, which is then passed to the controller, the function `FILTER_OUT` ensures the conversion in the opposite direction.

Global Filters
--------------

[](#global-filters)

Besides filters for specific parameters, you can also define global filters that receive an associative array of all parameters that they can modify in any way and then return. Global filters are defined under `null` key.

```
use Nette\Routing\Route;

$router->addRoute('/', [
	'controller' => 'Homepage',
	'action' => 'default',
	'' => [
		Route::FilterIn => function (array $params): array { ... },
		Route::FilterOut => function (array $params): array { ... },
	],
]);
```

Global filters give you the ability to adjust the behavior of the route in absolutely any way. We can use them, for example, to modify parameters based on other parameters. For example, translation `` and `` based on the current value of parameter ``.

If a parameter has a custom filter defined and a global filter exists at the same time, custom `FILTER_IN` is executed before the global and vice versa global `FILTER_OUT` is executed before the custom. Thus, inside the global filter are the values of the parameters `controller` resp. `action` written in PascalCase resp. camelCase style.

OneWay flag
-----------

[](#oneway-flag)

One-way routes are used to preserve the functionality of old URLs that the application no longer generates but still accepts. We flag them with `oneWay`:

```
// old URL /product-info?id=123
$router->addRoute('product-info', [...], oneWay: true);
// new URL /product/123
$router->addRoute('product/', [...]);
```

When accessing the old URL, the controller automatically redirects to the new URL so that search engines do not index these pages twice (see \[#SEO and canonization\]).

Subdomains
----------

[](#subdomains)

Route collections can be grouped by subdomains:

```
$router = new RouteList;
$router->withDomain('example.com')
	->addRoute('rss', [...])
	->addRoute('/');
```

You can also use \[#wildcards\] in your domain name:

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

Path Prefix
-----------

[](#path-prefix)

Route collections can be grouped by path in URL:

```
$router = new RouteList;
$router->withPath('/eshop')
	->addRoute('rss', [...]) // matches URL /eshop/rss
	->addRoute('/'); // matches URL /eshop//
```

Combinations
------------

[](#combinations)

The above usage can be combined:

```
$router = (new RouteList)
	->withDomain('admin.example.com')
		->addRoute(...)
		->addRoute(...)
	->end()
	->withDomain('example.com')
		->withPath('export')
			->addRoute(...)
			...
```

Query Parameters
----------------

[](#query-parameters)

Masks can also contain query parameters (parameters after the question mark in the URL). They cannot define a validation expression, but they can change the name under which they are passed to the controller:

```
// use query parameter 'cat' as a 'categoryId' in application
$router->addRoute('product ? id= & cat=', ...);
```

Foo Parameters
--------------

[](#foo-parameters)

We're going deeper now. Foo parameters are basically unnamed parameters which allow to match a regular expression. The following route matches `/index`, `/index.html`, `/index.htm` and `/index.php`:

```
$router->addRoute('index
