getting started  »  routing

Routing

The router object facilitates the configuration and matching of the routes to access your application. It supports both HTTP and CLI routing. With it, you can establish valid routes along with any parameters that may be required with them. When the router object is created, it auto-detects in which environment the application is running and creates the appropriate route match object (HTTP or CLI) within the router object. The routes are then added to that route match object to be used to evaluate incoming application requests.

Basic Routing

HTTP

In the example below, a web request of http://localhost/hello will execute the closure as the controller and echo Hello World out to the browser.

$router->addRoute('/hello', function() {
    echo 'Hello World';
});
CLI

In the example below, a CLI command of ./app hello will execute the closure as the controller and echo Hello World out to the console.

$router->addRoute('hello', function($name) {
    echo 'Hello World';
});

Dispatch Parameters

Route dispatch parameters that are needed for a particular route be can define as required or optional parameters.

Required

If a parameter is required and not provided with the requested route, the route is invalid.

// HTTP
$router->addRoute('/hello/:name', function($name) {
    echo 'Hello ' . ucfirst($name);
});

// CLI
$router->addRoute('hello <name>', function($name) {
    echo 'Hello ' . ucfirst($name);
});

In the two examples above, an HTTP request of http://localhost/hello/pop and a CLI command of ./app hello pop will each echo out Hello Pop to the browser and console, respectively.

Optional

If a parameter is optional and not provided with the requested route, the route is still valid. Consider the following controller class and method:

use Pop\Controller\AbstractController;

class MyApp\Controller\MainController extends AbstractController
{
    public function hello($name = null)
    {
        if (empty($name)) {
            echo 'Hello World!';
        } else {
            echo 'Hello ' . ucfirst($name);
        }
    }
}

Then add the following routes for HTTP and CLI:

// HTTP

$router->addRoute('/hello[/:name]', [
    'controller' => 'MyApp\Controller\MainController',
    'action'     => 'hello'
]);

// CLI
$router->addRoute('hello [<name>]', [
    'controller' => 'MyApp\Controller\MainController',
    'action'     => 'hello'
]);

In the above example, the parameter $name is an optional dispatch parameter and the hello() method performs differently depending on whether the parameter is present.

Routing Syntax

The tables below outline the accepted routing syntax for the route matching:

Web Route Expected
/foo/:bar/:baz The 2 params are required
/foo/:bar[/:baz] First param required, last one is optional
/foo/:bar/:baz* One required param, one required param that is a collection (array)
/foo/:bar[/:baz*] One required param, one optional param that is a collection (array)
CLI Route Expected
foo bar The 2 commands are required
foo bar|baz Two commands are required, the 2nd can accept 2 values
foo [bar|baz] The second command is optional and can accept 2 values
foo <name> [<email>] First parameter required, 2nd parameter optional
foo --name=|-n [-e|--email=] First option value required, 2nd option value is optional
foo [--option|-o] Option with both long and short formats

Using a Controller Class

Some of the above examples used simple closures as the controllers. While this is acceptable, using a controller class provides more control to design and build out all the routes needed for the application. The example below creates two valid routes for http://localhost/ and http://localhost/hello

use Pop\Controller\AbstractController;

class MyApp\Controller\IndexController extends AbstractController
{
    public function index()
    {
        echo 'Home Page!';
    }

    public function hello()
    {
        echo 'Hello World!';
    }
}

$router->addRoute('/', [
    'controller' => 'MyApp\Controller\IndexController',
    'action'     => 'index'
]);

$router->addRoute('/hello', [
    'controller' => 'MyApp\Controller\IndexController',
    'action'     => 'hello'
]);

And it works for CLI routes as well. The example below creates two valid routes for ./app help and ./app hello

use Pop\Controller\AbstractController;

class MyApp\Controller\ConsoleController extends AbstractController
{
    public function help()
    {
        echo 'Help Page!';
    }

    public function hello()
    {
        echo 'Hello World!';
    }
}

$router->addRoute('help', [
    'controller' => 'MyApp\Controller\ConsoleController',
    'action'     => 'help'
]);

$router->addRoute('hello', [
    'controller' => 'MyApp\Controller\ConsoleController',
    'action'     => 'hello'
]);

Controller Parameters

Commonly, you may require access to various elements and values of your application while within an instance of your controller class. To provide this, the router object allows you to inject parameters into the controller upon instantiation. Let's assume the controller's constructor in the below example, and we will define the values to pass into it:

use Pop\Controller\AbstractController;

class MyApp\Controller\IndexController extends AbstractController
{
    protected $foo;
    protected $bar;

    public function __construct($foo, $bar)
    {
        $this->foo = $foo;
        $this->bar = $bar;
    }
}

$router->addControllerParams(
    'MyApp\Controller\IndexController', [
        'foo' => $foo,
        'bar' => $bar
    ]
);

If you require parameters to be injected globally to all of your controller classes, then you can replace the controller name MyApp\Controller\IndexController with * and they will be injected into all controllers.

$router->addControllerParams(
    '*', [
        'foo' => $foo,
        'bar' => $bar
    ]
);

You can also define controller parameters within the route configuration as well.

$config = [
    'routes' => [
        '/products' => [
            'controller'       => 'MyApp\Controller\ProductsController',
            'action'           => 'index',
            'controllerParams' => [
                'baz' => 789
            ]
        ]
    ]
];

$app = new Pop\Application($config);

Dynamic Routing

Dynamic routing is also supported. You can define routes as outlined in the examples below, and they will be dynamically mapped and routed to the correct controller and method. Let's assume your application has the following controller class:

use Pop\Controller\AbstractController;

class MyApp\Controller\UsersController extends AbstractController
{
    public function index()
    {
        // Show a list of users
    }

    public function edit($id = null)
    {
        // Edit the user with the ID# of $id
    }
}

You could define a dynamic route like this:

// HTTP
$router->addRoute('/:controller/:action[/:param]', [
    'prefix' => 'MyApp\Controller\\'
]);

// CLI
$router->addRoute('<controller> <action> [<param>]', [
    'prefix' => 'MyApp\Controller\\'
]);

and HTTP and CLI routes such as these examples below would be valid:

http://localhost/users
http://localhost/users/edit/1001
./app users
./app users edit 1001

Named Routes

Named routes are supported either through the API or through the routes configuration. The benefit of named routes is that it gives a simple name to call up and reference the route when needed.

Via the API
$router = new Pop\Router\Router();

$router->addRoute('/home', function() {
    echo 'Home!' . PHP_EOL;
})->name('home');

$router->addRoute('/hello/:name', function($name) {
    echo 'Hello, ' . $name . '!' . PHP_EOL;
})->name('hello');
Via the Config
$app = new Application([
    'routes' => [
        '/home' => [
            'controller' => function () {
                echo 'Home!' . PHP_EOL;
            },
            'name' => 'home'
        ],
        '/hello/:name' => [
            'controller' => function ($name) {
                echo 'Hello, ' . $name . '!' . PHP_EOL;
            },
            'name' => 'hello'
        ]
    ]
]);

URL Generation

Using the named routed feature described above, you can generate URLs as needed by calling on the router and passing an array or object down with any of the dispatch parameters. The simple way to do this is with the static Pop\Route\Route class, which stores the application's current router. Consider the following named route:

$router->addRoute('/hello/:name', function($name) {
    echo 'Hello, ' . $name . '!' . PHP_EOL;
})->name('hello');

Below is an example of how to generate the appropriate URLs for a data set that would utilize that route:

foreach ($names as $name):
    echo '<a href="' . Route::url('hello', $name) . '">'  . $name->name . '</a><br />' . PHP_EOL;
endforeach;
<a href="/hello/nick">nick</a><br />
<a href="/hello/jim">jim</a><br />
<a href="/hello/world">world</a><br />