Custom HTTP routes in Zend Framework 1 MVC
Scenario: you're stuck with Zend 1 (which will support PHP7 - great news for those of us with legacy apps!) and you're building an API.
You've got routes like /api/categories
, and /api/products
but you want to provide some nice, nested resource routes, such as /api/categories/42/products
to get all products in a given category. This would be an alternative to /api/products?categoryId=42
.
I poked around at ZF1's router and came up with the following. It allows you to have both routes functional, giving your end users flexibility, whilst only requiring a single Controller.
Zend's Bootstrap:
protected function _initApiRoute()
{
$front = $this->_frontController;
$router = $front->getRouter();
if ($router instanceof Zend_Controller_Router_Rewrite) {
(new \Api\Routes($front, $router))->initRoutes();
}
}
Routes:
namespace Api;
use Zend_Controller_Front as FrontController;
use Zend_Rest_Route as RestRoute;
use Zend_Controller_Router_Rewrite as Router;
use Zend_Controller_Router_Route_Regex as RouteRegex;
class Routes
{
/** @var FrontController */
private $front;
/** @var Router */
private $router;
public function __construct(FrontController $front, Router $router)
{
$this->front = $front;
$this->router = $router;
}
public function initRoutes()
{
// zf1 matches routes in reverse order, so we start with defaults and then add more specific overrides
// most normal rest resources are covered by this route
$this->router->addRoute('api', new RestRoute($this->front, array(), array('api')));
// match specific overrides
// this example would find all products in the given category ID
// equivalent to doing /api/products?categoryId=42
$this->addRoute('api/categories/(.+)/products', 'products', ['categoryId']);
}
/**
* @param string $regex
* @param string $controller
* @param array $params
*/
private function addRoute($regex, $controller, $params = [])
{
// remap params to have keys starting with 1
$keyedParams = [];
$i = 1;
foreach ($params as $param) {
$keyedParams[$i] = $param;
$i++;
}
$this->router->addRoute($regex, new RouteRegex(
$regex,
[
'module' => 'api',
'controller' => $controller,
],
$keyedParams
));
}
}