Add support for params in route with new route dispatcher
This commit is contained in:
parent
0111ef9525
commit
e8d8e0e95b
12 changed files with 393 additions and 115 deletions
162
src/Routing/RouteDispatcher.php
Normal file
162
src/Routing/RouteDispatcher.php
Normal file
|
@ -0,0 +1,162 @@
|
|||
<?php
|
||||
|
||||
namespace Core\Routing;
|
||||
|
||||
use Core\Env\Env;
|
||||
use Core\Exceptions\Exceptions;
|
||||
use Core\Exceptions\Exceptions\NotFoundHttpException;
|
||||
use Core\Http\Request;
|
||||
use Core\View\Render;
|
||||
use Exception;
|
||||
|
||||
class RouteDispatcher
|
||||
{
|
||||
/**
|
||||
* Current request instance
|
||||
*
|
||||
* @var \Core\Http\Request
|
||||
*/
|
||||
private Request $request;
|
||||
|
||||
/**
|
||||
* Collection of all routes
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private array $routeCollection;
|
||||
|
||||
/**
|
||||
* @param \Core\Http\Request $request
|
||||
* @param array $routeCollection
|
||||
*/
|
||||
public function __construct(Request $request, array $routeCollection)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->routeCollection = $routeCollection;
|
||||
|
||||
try {
|
||||
$route = $this->findMatchingRoute();
|
||||
$controller = $this->instantiateController($route['controller']);
|
||||
$action = $this->validateActionExists($controller, $route['action']);
|
||||
$params = $this->resolveParameters($controller, $action, $route['params'] ?? []);
|
||||
|
||||
$this->executeAction($controller, $action, $params);
|
||||
} catch (NotFoundHttpException $e) {
|
||||
$this->handleException($e, 404, 'page not found');
|
||||
} catch (Exception $e) {
|
||||
$this->handleException($e, 500, 'something went wrong');
|
||||
}
|
||||
}
|
||||
|
||||
public static function dispatch(Request $request, array $routeCollection): void
|
||||
{
|
||||
new self($request, $routeCollection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate a matching route for the incoming request.
|
||||
*
|
||||
* @return array
|
||||
* @throws \Core\Exceptions\Exceptions\NotFoundHttpException
|
||||
*/
|
||||
private function findMatchingRoute(): array
|
||||
{
|
||||
$url = $this->request->url();
|
||||
$method = $this->request->method();
|
||||
|
||||
foreach ($this->routeCollection[$method] ?? [] as $routeRegex => $route) {
|
||||
if (preg_match($routeRegex, $url, $matches)) {
|
||||
$params = array_filter(
|
||||
$matches,
|
||||
fn($key) => !is_numeric($key),
|
||||
ARRAY_FILTER_USE_KEY
|
||||
);
|
||||
return array_merge($route, ['params' => $params]);
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException(sprintf("No route found for: %s", $url));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the requested controller.
|
||||
*
|
||||
* @param string $controllerName
|
||||
* @return object
|
||||
* @throws \Core\Exceptions\Exceptions\NotFoundHttpException
|
||||
*/
|
||||
private function instantiateController(string $controllerName): object
|
||||
{
|
||||
if (!class_exists($controllerName)) {
|
||||
throw new NotFoundHttpException(sprintf("Controller '%s' missing", $controllerName));
|
||||
}
|
||||
|
||||
return new $controllerName($this->request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the action exists in the controller.
|
||||
*
|
||||
* @param object $controller
|
||||
* @param string $actionName
|
||||
* @return string
|
||||
* @throws \Core\Exceptions\Exceptions\NotFoundHttpException
|
||||
*/
|
||||
private function validateActionExists(object $controller, string $actionName): string
|
||||
{
|
||||
if (!method_exists($controller, $actionName)) {
|
||||
throw new NotFoundHttpException(sprintf("Method '%s' not found on '%s'", $actionName, get_class($controller)));
|
||||
}
|
||||
|
||||
return $actionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and resolve parameters for the controller action.
|
||||
*
|
||||
* @param object $controller
|
||||
* @param string $action
|
||||
* @param array $params
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function resolveParameters(object $controller, string $action, array $params): array
|
||||
{
|
||||
return RouteValidator::resolve($controller, $action, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the resolved action on the controller with validated parameters.
|
||||
*
|
||||
* @param object $controller
|
||||
* @param string $action
|
||||
* @param array $params
|
||||
* @return void
|
||||
*/
|
||||
private function executeAction(object $controller, string $action, array $params): void
|
||||
{
|
||||
$response = $controller->$action(...$params);
|
||||
|
||||
if ($response instanceof Render) {
|
||||
$response->render();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle exceptions gracefully.
|
||||
*
|
||||
* @param Exception $e
|
||||
* @param int $statusCode
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
private function handleException(Exception $e, int $statusCode, string $message): void
|
||||
{
|
||||
if (Env::get('debug')) {
|
||||
Exceptions::catchOne($e);
|
||||
}
|
||||
|
||||
http_response_code($statusCode);
|
||||
echo $message;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue