Getting started
Routing and controllers
Command line
Databases (SQL)
Databases (NoSQL)
Security
Packages
Learn more
- Array helper
- Caching
- Command bus
- Date and time
- Events
- File system
- HTML helper
- Humanizer
- Image manipulation
- Internationalization
- Logging
- Number helper
- Pagination
- Sessions
- String helper
- URL builder
- UUID helper
- Validation
- Views
Official packages
Routing
The Mako router lets you map URL patterns to class methods and closures. It also allows you to perform reverse routing so that you don't have to hardcode URLs in your application.
Routes are registered in the phpapp/routing/routes.php file and there are three variables available in the scope, php$routes (the route collection) and php$app (the application instance) and php$container (the IoC container instance).
Basics
The following route will forward all phpGET requests to the php/ route to the phpwelcome method of the phpapp\controllers\Homecontroller class.
$routes->get('/', 'app\controllers\Home::welcome');
If you want the route to respond to phpPOST requests instead then you'll have to use the phppost method.
$routes->post('/', 'app\controllers\Home::welcome');
The available methods are phpget, phppost, phpput, phppatch, and phpdelete.
You can also make a route respond to all request methods using the phpall method.
$routes->all('/', 'app\controllers\Home::welcome');
All routes will by default respond to requests made using the
phpOPTIONSmethod.phpGETroutes will also respond tophpHEADrequests.
If you only want to allow a specific set of methods then you can use the phpregister method.
$routes->register(['GET', 'POST'], '/', 'app\controllers\Home::welcome');
Routes can also execute closures instead of class methods.
$routes->get('/hello-world', function()
{
return 'Hello, world!';
});
Route parameters
You'll often want to send parameters to your route actions. This is easy and can be done like this.
$routes->get('/articles/{id}', function($id)
{
return $id;
});
If you need to make a parameter optional then you can do so by adding the php? suffix.
$routes->get('/articles/{id}/{slug}?', function($id, $slug = null)
{
return $id . ' ' . $slug;
});
You can also impose constraints on your parameters using the phpwhen method. The route will not be matched unless all constraints are satisfied.
$routes->get('/articles/{id}', function($id)
{
return 'article ' . $id;
})
->when(['id' => '[0-9]+']);
Closure actions get executed by the phpContainer::call() method so all dependencies are automatically injected.
$routes->get('/article/{id}', function(ViewFactory $view, $id)
{
return $view->render('article', ['id' => $id]);
})
->when(['id' => '[0-9]+']);
Route middleware
Route middleware allows you to alter the request and response both before and after a route action gets executed.

Defining middleware
Middleware should (but is not required to) implement the phpMiddlewareInterface. The example below is the most basic middleware implementation (it doesn't actually do anything).
<?php
namespace app\routing\middleware;
use Closure;
use mako\http\Request;
use mako\http\Response;
use mako\http\routing\middleware\MiddlewareInterface;
class PassthroughMiddleware implements MiddlewareInterface
{
public function execute(Request $request, Response $response, Closure $next): Response
{
return $next($request, $response);
}
}
Middleware has to be registered in the phpapp/routing/middleware.php file before you can use them. There are three variables available in the scope, php$middleware (the middleware collection), php$app (the application instance) and php$container (the IoC container instance).
$middleware->register('passthrough', PassthroughMiddleware::class);
In the next example we'll create a middleware that returns a cached response if possible.
Note that all middleware is instantiated through the dependency injection container so you can easily inject your dependencies through the constructor.
<?php
namespace app\routing\middleware;
use Closure;
use mako\cache\CacheManager;
use mako\http\Request;
use mako\http\Response;
use mako\http\routing\middleware\MiddlewareInterface;
class CacheMiddleware implements MiddlewareInterface
{
protected $cache;
protected $minutes;
public function __construct(CacheManager $cache, $minutes = null)
{
$this->cache = $cache;
$this->minutes = $minutes ?? 10;
}
public function execute(Request $request, Response $response, Closure $next): Response
{
if(this->cache->has('route.' . $request->path()))
{
return $response->body($cache->get('route.' . $request->path()));
}
$response = $next($request, $response);
$this->cache->put('route.' . $request->path(), $response->getBody(), 60 * $this->minutes);
return $response;
}
}
The cache example above is very basic and should probably not be used in a production environment.
Assigning middleware
Assigning middleware to a route is done using the phpmiddleware method. You can also pass an array of middleware if your route requires multiple middleware. Middleware will get executed in the order that they are assigned.
$routes->get('/articles/{id}', 'app\controllers\Articles::view')
->when(['id' => '[0-9]+'])
->middleware('cache');
You can also pass parameters to your middleware. In the example below we're telling the middleware to cache the response for 60 minutes instead of the default 10.
$routes->get('/articles/{id}', 'app\controllers\Articles::view')
->when(['id' => '[0-9]+'])
->middleware('cache("minutes":60)');
Middleware parameters are parsed as JSON. So booleans, strings, arrays, objects (associative arrays), null and numeric values are valid.
Middleware priority
As mentioned above, by default middleware get executed in the order that they are assigned to the route. You can however ensure the execution order by configuring middleware priority.
$middleware->setPriority(['cache' => 1, 'passthrough' => 2]);
In the example above we're making sure that the phpcache middleware gets executed first, followed by the phppassthrough middleware.
You can use middleware priority without having to configure all your middleware. Non-configued middleware will be assigned a default priority of php100. This means that if you have a middleware that you want executed last then you can set its priority to a value of php101 or above.
Route groups
Route groups are useful when you have a set of routes with the same constraints and middleware.
$options =
[
'middleware' => 'cache',
'when' => ['id' => '[0-9]+'],
'namespace' => 'app\controllers',
];
$routes->group($options, function($routes)
{
$routes->get('/articles/{id}', 'Articles::view');
$routes->get('/photos/{id}', 'Photos::view');
});
All routes within the group will now have the same middleware and constraints. You can also nest groups if needed.
The following options are available when creating a route group. They are also available as chainable methods on individual routes.
| Option | Method | Description |
|---|---|---|
| middleware | middleware | A middleware or an array of middleware |
| namespace | namespace | The controller namespace (closures will not be affected) |
| prefix | prefix | Route prefix |
| when | when | An array of parameter constraints |
Reverse routing
You can assign names to your routes when you define them. This will allow you to perform reverse routing, thus removing the need of hardcoding URLs in your views.
$routes->get('/', 'Home::Welcome', 'home');
The route in the example above has been named phphome and we can now create a URL to the route using the phptoRoute method of the phpURLBuilder class.
<a href="{{$urlBuilder->toRoute('home')}}">Home</a>
You can also pass parameters to your route URLs.
<a href="{{$urlBuilder->toRoute('articles.view', ['id' => 1])">Article</a>
Faking request methods
Most browsers only support sending phpGET and phpPOST requests. You can get around this limitation by performing a phpPOST request including a phpREQUEST_METHOD_OVERRIDE field where you specify the request method you want to use.
<input type="hidden" name="REQUEST_METHOD_OVERRIDE" value="DELETE">
Another solution is to send a phpX_HTTP_METHOD_OVERRIDE header.