1.4.5. Building Custom Maps and Routes

1.4.5.1. Extending the Map Class

You might want to extend the Map class to provide convenience methods specific to your application. As with writing a custom matching rule, this is a bit involved but not difficult:

  1. Write your Map extended class.

  2. Add your extended class factory to the RouterContainer.

  3. Use the RouterContainer as normal, and it will return your custom Map class.

For example, you may wish to have a resource() method that automatically attaches a series of routes all at once for a given route name and route path. First, write your extension of the Map class:

<?php
use Aura\Router\Map;

class MyResourceMap extends Map
{
    public function resource($namePrefix, $pathPrefix)
    {
        return $this->attach($namePrefix, $pathPrefix, function ($map) {
            $map->get('browse', '');
            $map->get('read', '/{id}');
            $map->patch('edit', '/{id}');
            $map->post('add', '');
            $map->delete('delete', '/{id}');
        });
    }
}
?>

Then tell the RouterContainer how to create your extended class using a factory callable:

<?php
use Aura\Router\Route;

$routerContainer->setMapFactory(function () {
    return new MyResourceMap(new Route());
});
?>

Now you can get the Map from the RouterContainer, and it will be your custom extended class:

<?php
$map = $routerContainer->getMap();
echo get_class($map); // "ResourceMap"
?>

1.4.5.2. Extending The Route Class

Likewise, you may only need to add special parameters to the routes themselves, without needing to change the Map logic. This too is a bit involved but not difficult:

  1. Write your Route extended class.

  2. Add your extended class factory to the RouterContainer.

  3. Use the RouterContainer as normal, and it will use your custom Route for the Map.

For example, you may need to add a model() method that specifes the model to use with the handler. First, write your extended Route class:

<?php
use Aura\Router\Route;

class ModelRoute extends Route
{
    protected $model;

    public function model($model)
    {
        $this->model = $model;
        return $this;
    }
}
?>

Then tell the RouterContainer how to create your extended class using a factory callable:

<?php
use ModelRoute;

$routerContainer->setRouteFactory(function () {
    return new ModelRoute();
});
?>

Now you can get the Map from the RouterContainer, and it will use your custom extended route object when adding routes:

<?php
$map = $routerContainer->getMap();
$route = $map->get('foo', '/path/to/foo')->model('MyModelClass');
echo get_class($route); // "ModelRoute"
echo $route->model; // "MyModelClass"
?>

Because the Map object proxies unknown method calls to the underlying route object, your new methods will also be honored by the Map object to set route defaults:

<?php
$map->model('DefaultModelClass');
$route = $map->get('foo', '/path/to/foo');
echo get_class($route); // "ModelRoute"
echo $route->model; // "DefaultModelClass"
?>

1.4.5.3. Automated Route Caching and Building

You may wish to build your route map from some external source. Alternatively, you might want to cache your route map for production deployments so that you do not have to add the routes from scratch on each page load.

To effect this or other automated map-building functionality, call the RouterContainer::setMapBuilder() method and pass a builder callable to set up the Map before the container returns it. The builder callable should take a Map instance as its only argument.

The following is a naive example for file-based caching and restoring of Map routes. It uses the Map::setRoutes() and Map::getRoutes() methods to work with the array of mapped route objects.

<?php
$routerContainer->setMapBuilder(function ($map) {

    // the cache file location
    $cache = '/path/to/routes.cache';

    // does the cache exist?
    if (file_exists($cache)) {

        // restore from the cache
        $routes = unserialize(file_get_contents($cache));
        $map->setRoutes($routes);

    } else {

        // build the routes on the map ...
        $map->get(...);
        $map->post(...);

        // ... then save them to the cache for the next page load
        $routes = $map->getRoutes();
        file_put_contents($cache, serialize($routes));
    }
});
?>

N.b.: If there are closures in the Route objects (e.g. in the handlers), you will not be able to serialize the routes for caching. This is because closures cannot be serialized by PHP. Consider using non-closure callables instead.

Now when you call $routerContainer->getMap(), the container will automatically call the map-builder logic and apply it to the $map before returning it.