Routing
A route connects a page address to the code that builds the response. Laminas has no single file with all addresses: each module describes its routes in the router section of module/<Module>/config/module.config.php.
The first route
Open module/Blog/config/module.config.php and replace the empty array:
// module/Blog/config/module.config.php
namespace Blog;
use Laminas\Router\Http\Literal;
return [
'router' => [
'routes' => [
'blog' => [
'type' => Literal::class,
'options' => [
'route' => '/blog',
'defaults' => [
'controller' => Controller\PostController::class,
'action' => 'index',
],
],
],
],
],
];What each key means:
| Key | Meaning |
|---|---|
blog | The route name. Links are built from it later |
type | The route type. Literal is an exact address match |
route | The address itself |
defaults.controller | The controller class that answers the request |
defaults.action | The action — the controller method indexAction() |
Open http://localhost:8080/blog. You get an error page explaining that the controller was not found. That is expected: the route already matched, and the PostController class is created in the controllers lesson. Before the route was added, the same address answered that the page was not found.
A route with a parameter
The Segment type lets you put a variable part — a parameter — into the address. A parameter starts with a colon. Add a second route next to the first one, inside routes:
// at the top of the file: use Laminas\Router\Http\Segment;
'post' => [
'type' => Segment::class,
'options' => [
'route' => '/blog/:id',
'constraints' => [
'id' => '[0-9]+',
],
'defaults' => [
'controller' => Controller\PostController::class,
'action' => 'view',
],
],
],The address /blog/5 goes to the view action, and the number 5 becomes the id parameter. The constraints section sets a regular expression for the parameter: here id is digits only, so /blog/anna does not match this route.
An optional part
The part of the address in square brackets may be left out. The default value comes from defaults:
'archive' => [
'type' => Segment::class,
'options' => [
'route' => '/archive[/:year]',
'constraints' => ['year' => '[0-9]{4}'],
'defaults' => [
'controller' => Controller\PostController::class,
'action' => 'archive',
'year' => 2025,
],
],
],The route answers both /archive and /archive/2024. In the first case year equals 2025.
Route types
| Type | Purpose |
|---|---|
Literal | An exact address without parameters — the fastest |
Segment | An address with parameters: /:id, /:year/:month |
Regex | An address matched by a regular expression |
Method | A restriction by HTTP method |
Linking to a route
Addresses are not typed by hand in the code — they are built from the route name. In a view script the url() helper does it: $this->url('post', ['id' => 5]) returns the string /blog/5. If the address in the configuration changes, every link changes with it.
Common mistake
A parameter without a constraint accepts any text. The route /blog/:id without constraints also matches /blog/add, and the action receives an id equal to add. Always set a constraint for numeric parameters: 'id' => '[0-9]+'.