Maybe you can describe what you are trying to accomplish so we can better describe what will be important to your use case, but here's a general overview:
If you are creating an extension, you generally create a controller and map it to a route, which is a path definition like GET /api/things/<id>
. The extender system of Flarum means you don't have to worry about how the routing is actually done behind the scenes.
In addition to controllers and routes, Flarum uses 2 additional concepts, middlewares and frontend namespaces/prefixes.
The frontend namespace/prefix (I'm not actually sure if we have an official name for this) is a concept specific to Flarum, where /
(forum), /admin
and /api
are separated at the start to make extensibility easier. Everything is handled like if it was 3 separate web applications, each with their own list of routes and behavior (for instance, the API part doesn't need anything that generates HTML, and the admin part can block non-admins early).
The next piece are middlewares, a common concept in web frameworks. Laravel has something similar, though the signatures are a bit different because they don't use the PSR interfaces like Flarum does. There are 3 middleware stacks, one for each frontend. Those middlewares are pieces of code that run for every request and handle things like session, rate limiting and error pages.
At the end of the middleware stack, when everything that must happen "before" a request is done, the actual controller for the given request must be run, this is where the router is used to pick the correct controller for the current request.
Flarum uses the Fastroute library for routing, which is essentially a mapping of route definitions to controller names. It then compiles all that into a single regular expression that is run against the request URL and returns the controller name, which Flarum executes.
Something else that is specific to Flarum is that the last part is actually split into 2 steps. The ResolveRoute
middleware is actually where the router is called and outputs the controller name. The ExecuteRoute
middleware is placed at the very end and executes the controller that was specified by ResolveRoute
. The reason this is split into 2 different middlewares is so extensions can inject an additional middleware in-between and change the behavior.
It's not a common use case to modify what the ResolveRoute
has outputted, however a common use case is to use that information in custom middlewares to know which route is about to be executed by its name, instead of having to execute your own regex against the path. This is still an advanced use case, as most extensions don't need middlewares at all.
As to how the route collection and Fastroute get the list of routes from the extender, that's all done through container bindings, like most Flarum extenders. The container is a concept from Laravel which is heavily used in Flarum as well. When extenders run, they get access to the container, and through the container they can access the list of routes and add to it. Then the resolver middleware does the same, retrieving the list of routes from the container.
I hope this makes sense. I don't know whether you have any prior experience with these concepts from other frameworks?