I usually do this:
Collection - The collection that will save all routes.
Route - The class that represents a route. You should provide information like the uri you want to capture and http verbs accepted.
Router - The class that serves to bridge the Route and Collection. It is a facilitator for creating routes within the collection.
DispatcherInterface - The interface that provides a method to be run as the route dispatcher. I preferred to make an interface to be able to attend to several implementations, for example, someone who wants to use a more complex library or who simply wanted to use less resources.
So, in this context, we would have a structure something like this:
$router = new Router(new Collection);
// Cria a instância de Route, coloca dentro da Collection
// E retorna Rota recém-criada, para possíveis outra definições
$router->get('/', 'HomeController::getIndex');
$router->get('/login', function () {})->setName('home.login');
// Classe que implementa DispatcherInterface
$router->dispatch(new Dispatcher());
Based on the context presented in the question, I think an interesting one would be:
$request = new Request();
// retorna new Response se tudo der certo
$response = $router->dispatch(new RequestDispatcher($request));
$response->send();
Within your RequestDispatcher
, you could apply the proper operations to call Controller and Method.
For example:
class RequestDispatcher implements DispatcherInterface {
public function __construct(Request $request) {
$this->request = $request;
}
public function dispatch(Router $router) {
$route = $router->findByRequest($this->request);
if (! $route) return $this->notFound();
$response = call_user_func($route->callAction(), $route->getParameters());
return $response;
}
}
That is, in your Dispatcher, you can add the operations required to search the route within the collection. When it is not found, you can invoke an action for the 404 error loop. When it is found, you can call the action defined for the given url and call it, then return a Response.
Trying to summarize it all:
The Dispatcher looks for the route within a route collection, which was created by the router. Then, if the Dispatcher finds the route, it converts the return of the route action (it can be a method of a Controller or a Closure) to a Response, which is finally sent to the output.
You mentioned in your question about a Handler, which is passed through a callback.
I think you're talking about an anonymous function, which some libraries often use to "tie" a particular functionality to a route in order to run at the end.
In my library I also did this. Can I use either a method of a class (the Controller) or an anonymous function. I think this is very useful in cases where the route does not make sense to point to a specific controller, because it is not something that does not have "relationship" with the rest.
Example:
$router->get('/json/cep/{num}', function ($number) {
$url = sprintf('https://cep.correios.com.br/%s.json', $number);
$dados = json_decode(file_get_contents($url));
// Transforma em JsonResponse
return $dados;
});