# Configuring routes

The HttpSoft\Router package is responsible for routing, and the application instance proxies the methods of the route collector.

By default, routes are configured in the file config/routes.php. Several methods are provided for adding routes for the most popular HTTP methods. Each of these methods creates, adds to the collection, and returns an instance of the HttpSoft\Router\Route.

/**
 * @var HttpSoft\Basis\Application $app
 * @var Psr\Container\ContainerInterface $container
 * @var mixed $handler
 */

$app = $container->get(HttpSoft\Basis\Application::class);

// Only for the "GET" method:
$app->get('post.view', '/posts/{id}', $handler)->tokens(['id' => '\d+']);

// Only for the "POST" method:
$app->post('post.create', '/posts', $handler);

// Only for the "PUT" method:
$app->post('post.update', '/posts/{id}', $handler)->tokens(['id' => '\d+']);

// Only for the "PATCH" method:
$app->patch('post.change', '/posts/{id}', $handler)->tokens(['id' => '\d+']);

// Only for the "DELETE" method:
$app->delete('post.delete', '/posts/{id}', $handler)->tokens(['id' => '\d+']);

// Only for the "HEAD" method:
$app->delete('post.headers', '/posts', $handler);

// Only for the "OPTIONS" method:
$app->options('post.options', '/posts', $handler);

// For any methods:
$app->any('posts', '/posts{[id]}', $handler)->tokens(['id' => '\d+']);

// For custom methods:
$app->add('posts.edit', '/posts/(create|update)/{id}', $handler, ['GET', 'POST'])
    ->tokens(['id' => '\d+'])
;

# Route groups

You can specify routes inside of a group. All routes defined inside a group will have a common path prefix.

use HttpSoft\Router\RouteCollector;

$app->group('/post', static function (RouteCollector $router) use ($handler): void {
    $router->get('post.view', '/{slug}', $handler)->tokens(['slug' => '[\w-]+']);
    $router->get('post.list', '{[page]}', $handler)->tokens(['page' => '\d+']);
});

The result will be equivalent to:

$app->get('post.view', '/post/{slug}', $handler)->tokens(['slug' => '[\w-]+']);
$app->get('post.list', '/post{[page]}', $handler)->tokens(['page' => '\d+']);

# Placeholders and tokens

When adding the {token} placeholder parameter to the path, the regular expression [^/]+ will be used by default, which matches everything except the slash that points to the next segment of the path. To specify custom regular expressions for placeholder parameter tokens, use the HttpSoft\Router\Route::tokens() method.

$app->delete('post.delete', '/posts/{id}', $handler)->tokens(['id' => '\d+']);

$app->get('post.view', '/posts/{slug}{format}', $handler)
    ->tokens(['slug' => '[\w\-]+', 'format' => '\.[a-zA-z]{3,}'])
;

You can set the token to the default value using the HttpSoft\Router\Route::defaults() method.

$app->get('post.view', '/posts/{slug}{format}', $handler)
    ->tokens(['slug' => '[\w\-]+', 'format' => '\.[a-zA-z]{3,}'])
    ->defaults(['format' => '.html'])
;

Placeholders enclosed in [...] are considered optional.

$app->get('post.list', '/posts{[page]}', $handler)
    ->tokens(['page' => '\d+'])
;

// `{[year/month/day]}` equivalently to `{[/year/month/day]}`
$app->get('post.archive', '/posts/archive{[year/month/day]}', $handler)
    ->tokens(['year' => '\d{4}', 'month' => '\d{2}', 'day' => '\d{2}'])
;

Optional parameters SHOULD only be at the very end of the route and SHOULD NOT be preceded by a leading slash.

Read more about adding routes, setting a host for each route, tokens, etc.

# Matching routes

The route matching process occurs in two stages with the help of middleware (see the description).

  1. HttpSoft\Router\Middleware\RouteMatchMiddleware — matches the incoming request with the added routes. If a match occurs, registers an instance of HttpSoft\Router\Route as a request attribute, using the HttpSoft\Router\Route class name as the attribute name, and passes the request to the next middleware.
  2. HttpSoft\Router\Middleware\RouteDispatchMiddleware — checks for the existence of a matching route (instance of HttpSoft\Router\Route) as an attribute in the request. If it exists, the handler for this route is used, otherwise the request processing is delegated to the handler, which is passed as an argument to the process() method.

Middleware is registered in the pipeline config/pipeline.php. Any number of other middleware can be located between these two middleware. The HttpSoft\Router\Middleware\RouteDispatchMiddleware SHOULD be at the end of the pipeline.

Read more about the middleware pipeline.

# Route handling

For example, let's add two simple routes.

$app->get('home', '/', HomeAction::class);
$app->get('data', '/data', DataAction::class);

Fictitious class names (HomeAction, DataAction, etc.) are used as handlers/actions. This approach allows you to organize the processing of each route in a separate class. These classes implement the Psr\Http\Server\RequestHandlerInterface.

use HttpSoft\Response\JsonResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;

final class HomeAction implements RequestHandlerInterface
{
    public function handle(ServerRequestInterface $request): ResponseInterface
    {
        return new JsonResponse(['name' => 'HomeAction']);
    }
}

Handler objects will be created through the container, so they can accept dependencies in the constructor.

use HttpSoft\Basis\Response\PrepareJsonDataTrait;
use HttpSoft\Response\JsonResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;

final class DataAction implements RequestHandlerInterface
{
    use PrepareJsonDataTrait;

    private Repository $repository;

    public function __construct(Repository $repository)
    {
        $this->repository = $repository;
    }

    public function handle(ServerRequestInterface $request): ResponseInterface
    {
        return new JsonResponse($this->prepareJsonData($this->repository->getAll()));
    }
}

After running the application and matching the route with the request, the handler for the matched route will be executed and returns an instance of the response to send to the client.

The handler can also be:

Read more about handlers, see in HttpSoft\Runner\MiddlewareResolver.

# URI generation

URI generation is most often used in request actions or view templates. To access routes, you need to accept an instance of the route collector in the action constructor.

use HttpSoft\Response\RedirectResponse;
use HttpSoft\Router\RouteCollector;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;

final class PageAction implements RequestHandlerInterface
{
    private RouteCollector $router;
    
    public function __construct(RouteCollector $router)
    {
        $this->router = $router;
    }

    public function handle(ServerRequestInterface $request): ResponseInterface
    {
        return new RedirectResponse($this->router->routes()->path('home'));
    }
}

To generate paths, use the HttpSoft\Router\RouteCollection::path() method.

/**
 * @var HttpSoft\Router\RouteCollector $router
 * @var mixed $handler
 */

$router->get('home', '/', $handler);
$router->routes()->path('home'); // '/'

$router->get('page', '/{slug}', $handler)->tokens(['slug' => '[\w\-]+']);
$router->routes()->path('page', ['slug' => 'page-slug']); // '/page-slug'

To generate URL, use the HttpSoft\Router\RouteCollection::url() method.

/**
 * @var HttpSoft\Router\RouteCollector $router
 * @var mixed $handler
 */

$router->get('home', '/', $handler)->host('(shop|blog).example.com');
$router->routes()->url('home', []); // '/'
$router->routes()->url('home', [], 'blog.example.com'); // '//blog.example.com'
$router->routes()->url('home', [], 'blog.example.com', true); // 'https://blog.example.com'
$router->routes()->url('home', [], 'blog.example.com', false); // 'http://blog.example.com'

$router->get('page', '/{slug}', $handler)->tokens(['slug' => '[\w\-]+']);
$router->routes()->url('page', ['slug' => 'page-slug']); // '/page-slug'
$router->routes()->url('page', ['slug' => 'page-slug'], 'any-host.com'); // '//any-host.com/page-slug'

Read more about URI generation, see in HttpSoft\Router\RouteCollection.