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 />