Routing

Basics

Viewi has built in routing support. And if you are familiar with at least one the popular PHP frameworks, you will find it very familiar. So, let's dig into.

Your routes are defined in the file \viewi-app\routes.php. This file has access to the current application instance Viewi\App $app, which has its own router:

<?php

use Viewi\App;
use Components\Views\Home\HomePage;

/**
 * @var App $app
 */
$router = $app->router();
$router->get('/', HomePage::class);

Available route methods

You can register your component under any HTTP verb and it will be executed accordingly once url pattern has been matched.

$router->get(string $url, $action, ?array $defaults = null, array $wheres = []);
$router->post(string $url, $action, ?array $defaults = null, array $wheres = []);
$router->put(string $url, $action, ?array $defaults = null, array $wheres = []);
$router->delete(string $url, $action, ?array $defaults = null, array $wheres = []);
$router->patch(string $url, $action, ?array $defaults = null, array $wheres = []);
$router->options(string $url, $action, ?array $defaults = null, array $wheres = []);

// Matches any verb
$router->all(string $url, $action, ?array $defaults = null, array $wheres = []);

// Register method on your own
$router->register(string $method, string $url, $action, ?array $defaults = null, array $wheres = []);

Please note, Viewi router supports your custom callable for standalone use.

$router->get('/api/post/{id}', function (int $id) {
    $post = new PostModel();
    $post->id = $id;
    $post->name = 'View Post Demo';
    return $post;
});

Route parameters

Required parameter

{param} captures required segment from the URI and injects it into your component in place of parameter with the same name. Order doesn't matter. For example:

$router->get('/post/{id}', PostPage::class);
class PostPage extends BaseComponent
{
    public function __construct(private HttpClient $http, public int $id)
    {
        // id is available here
    }
}

Optional parameter

{param?} captures optional segment from the URI and injects it into your component in place of parameter with the same name. In case if segment is empty, default or null value will be injected. Order doesn't matter. For example:

$router->get('/post/{id?}', PostPage::class);
class PostPage extends BaseComponent
{
    public function __construct(private HttpClient $http, public ?int $id = null)
    {
        // id is available here if present, otherwise NULL
    }
}

Constraint rules

Method where and argument $wheres set constraint rules (regular expression constrains). Additionally you can use URL segment rules with {param<regex>}.

$router->get('/post/{id}', PostPage::class)->where('id', '\\d+');
// OR all at once
$router->get('/post/{id}', PostPage::class)->where(['id' => '\\d+']);
// OR as argument
$router->get('/post/{id}', PostPage::class, null, ['id' => '\\d+']);
// OR as URL segment rule
$router->get('/post/{id<\\d+>}', PostPage::class);

Segments allowed to contain all characters except /. But you can explicitly allow any character by using where.

// ignore '/'
$router->get('/search/{search}', SearchPage::class)->where('search', '.*');

Transform

To modify your response from Viewi you can use transform method. Use case: send proper status code for NotFoundPage component:

$router
    ->get('*', NotFoundPage::class)
    ->transform(function (Response $response) {
        return $response->withStatus(404, 'Not Found');
    });

Route sections

You can group routes with similar prefixes:

$router->get('/admin', Dashboard::class);
$router->get('/admin/content', PageList::class);

Same as:

$router->section('admin', function (Router $router) {
    $router->get('/', Dashboard::class);
    $router->get('/content', PageList::class);
}

Mark route as lazy

If you want to separate routes into multiple asset bundles, you can use lazy grouping method:

$router->lazy('admin', function (Router $router) {
    $router->get('/admin', Dashboard::class);
    $router->get('/admin/content', PageList::class);
});

Now these components will be loaded only if you navigate to any of these routes.

Same effect can get with LazyLoad attribute.

More examples

// optional 'query' with constraint
$router->get('/post/{type}/{query<[A-Za-z]+>?}', PostPage::class);
// wildcard for all URLs that start with '/docs'
$router->get('/docs/*', DocsPage::class);