Closed
Description
Request
It would be nice if Phalcon offered a native way to initialize the router component via an array in a similar manner that the config and providers are configured. The current implementation either requires passing around the DI and Router component or defining a file with broken references.
Reasons for Need
- Practicality, easier to maintain and similar to other Phalcon patterns.
- Separate files for different components, enabled.
- Environment based route loading (feature flags).
- Smooth integration with build, test, and sniffing tools.
- Smooth integration with IDES. No broken references.
- Reduce the need for file based loading if defined in a custom namespace. (require_once vs autoload)
- Ability to collapse structure in IDE.
Work Around
Below is an extremely raw version of what has to be done to work around the feature request. (Done in 10 minutes for this NFR). By parsing over a data structure and dynamically building the Router Provider, we are able to dynamically build the $router->add()
methods.
This method uses constants but in a real working version we should be able to set the config via an env file via a setter.
RouteConfig.php
<?php
namespace App\Config;
class RouteConfig
{
const DYNAMIC = 'dynamic';
const GET = 'get';
const POST = 'post';
const DELETE = 'delete';
const PUT = 'put';
const NOT_FOUND = 'notFound';
const CONFIG = [
'controllerName' => [
[
'route' => '/1',
'type' => self::POST,
'action' => 'actionName',
],
[
'route' => '/2/([0-9a-f-]+)',
'type' => 'SELF::GET',
'action' => 'actionName2',
'params' => [
'param1' => 1,
],
],
],
];
}
<?php
namespace App\Provider;
use App\Config\RouteConfig;
class RouterServiceProvider implements ServiceProviderInterface
{
const ROUTE_METHOD =[
RouteConfig::DYNAMIC => 'add',
RouteConfig::GET => 'addGet',
RouteConfig::POST => 'addPost',
RouteConfig::DELETE => 'addDelete',
RouteConfig::PUT => 'addPut',
RouteConfig::NOT_FOUND => 'notFound',
];
private Router $router;
public function __construct()
{
$this->router = new Router(false);
}
public function register(DiInterface $di): void
{
foreach (RouteConfig::CONFIG as $controller => $routes) {
$this->registerRoutes($controller, $routes);
}
$this->router->setDI($di);
$this->router->removeExtraSlashes(true);
$this->router->handle($_SERVER['REQUEST_URI']);
$di->set('router', $this->router);
}
private function registerRoutes(string $controller, array $routes): void
{
foreach ($routes as $route) {
$params = $route['params'] ?? [];
$routeMethod = self::ROUTE_METHOD[$route['type']] ?? null;
if ((!isset($route['action']) || !isset($route['route']) || is_null($routeMethod))) {
throw new RuntimeException('Invalid Router Config');
}
$routeData = $this->mapRouteData($controller, $route['action'], $params);
($routeMethod !== RouteConfig::NOT_FOUND)
? $this->router->$routeMethod($route['route'], $routeData)
: $this->router->notFound($routeData);
}
}
private function mapRouteData(string $controller, string $action, array $params): array
{
$routeData = [
'controller' => $controller,
'action' => $action,
];
return array_merge_recursive($routeData, $params);
}
}