Overview
Vanilla has 3 primary ways of dispatching URLs and they are tried in the following order.
- Routes registered with
Garden\Web\Dispatcher
. - Pages using
Gdn_Dispatcher
. - Redirects from
Gdn_Router
.
Garden\Web\Dispatcher Routes
There are primarily 2 types of routes dispatched from Garden\Web\Dispatcher
.
AbstractApiControllers
(APIv2 Endpoints).PageDispatchControllers
(ReactJS pages).
These 2 types of routes build the foundation of modern Vanilla. Almost all of knowledge base is built using these routes as well as all of APIv2.
Route Matching Rules with ResourceRoute
Route matching is configured by registering an instance of Garden\Web\Route
with the dispatcher. This generally always done as configuration of Garden\Container
.
Here's an example of the definition of the APIv2 route.
$container
->rule('@api-v2-route') // Choose a name for our route instance.
->setClass(\Garden\Web\ResourceRoute::class)
// Set the route prefix & the pattern of files to match.
->setConstructorArgs(['/api/v2/', '*\\%sApiController'])
// Set a default content type.
->addCall('setMeta', ['CONTENT_TYPE', 'application/json; charset=utf-8'])
$container
->rule(\Garden\Web\Dispatcher::class)
->setShared(true)
// Add our previously defined route instance to the container.
// The second parameter, 'api-v2' is an optional unique key that allows
// other addons to modify the route if needed.
->addCall('addRoute', ['route' => new Reference('@api-v2-route'), 'api-v2'])
In this case we are using ResourceRoute
which matches certain classes and function names to URLs automatically. Here's how it works.
- Gather all classes matching the provided pattern. (
*\\%sApiController
) . - The defined
/api/v2
becomes the first part of the path. - The prefix in the last part of the classname after the namespaces (
%s
) becomes the second part of the path. If this part of the path was camel cased in the class name it will become dash-cased in the URL. Eg. MyTestApiController
becomes /api/v2/my-test
. - A matching method names makes up the remaining part of the path.
Method Name Matching
Methods are matched against various patterns in the method name. In the following example we will use the class TestApiController
as an example.
Wherever type
is used and of the following HTTP request . types are to be used as a substitute: get
, index
, post
, patch
, options
, delete.
A method type of any of the following will only match a request of the equivalent HTTP request type. For example a put
method will only match against incoming requests of the type GET
. index
is a special exception and is equivalent to get
. More info on that below.
The following are common examples:
type()
This will match request in the form of TYPE /api/v2/test
as well.
type(int $id)
This will matching a request in the form of TYPE /api/v2/test/$id
where ID is some number. Note: The variable must be named $id
.
type(string $path)
This will matching a request in the form of TYPE /api/v2/test/$path
where ID is some number. Note: The variable must be named $path
.
This will even include subpaths, such as /api/v2/test/$path/$subpath/$deeper-subpath
so if you use this type be sure to strictly validate all paths and return a 404 Not Found
when appropriate.
type_subpath()
This will matching a request in the form of TYPE /api/v2/test/subpath
.
type_subpath(int $id)
This will matching a request in the form of TYPE /api/v2/test/subpath
.
Special Cases
index()
This will match request in the form of GET /api/v2/test
. This is a special case to allow the function to exist in PHP at the same time as get(int $id)
.
get(int $id)
This will matching a request in the form of GET /api/v2/test/$id
where ID is some number. This pattern is the reason for the index()
being supported. It is common to support this and the index case in the same controller.
Query Parameters
Any of the above methods may take an optional additional argument array $query
. This value will be an array of all query parameters passed to the endpoint.
Bodies
post
, patch
, and put
endpoints may take and optional array $body
parameter. This will transform a JSON encoded body of the request into an array and pass it to the method.
Gdn_Dispatcher & Gdn_Controller
This is and older form of URL mapping and controllers. Prefer using Garden\Dispatcher
.
URL matching
First the dispatcher looks for an application name, then a controller name, then a method name, then any arguments which will be passed in the order given.
An example that includes all of these is: /dashboard/profile/notifications/1/Lincoln
. This calls the Dashboard’s ProfileController
invoking the Notifications
method, which it passes the arguments 1
and Lincoln
, in that order. It roughly translates to:
$profileController = new ProfileController();
$profileController->notifications('1', 'Lincoln');
If the application is omitted, it will automatically search enabled applications for a suitably named controller. Therefore, avoid controller name overlap. If the method name is omitted, the index()
method will be invoked. Therefore, the basic profile URL /profile/1/Lincoln
could be more verbosely written as /dashboard/profile/index/1/Lincoln
to more clearly understand what code it is invoking.
Caveats
There are a few caveats to the route matching her:
- This dispatcher does not discriminate between request types. In the previous example
/profile/1/Lincoln
will respond to GET, POST, PUT, etc. This can lead to security problems like CSRF vulnerabilities if not handled carefully. - This dispatcher does not provide post bodies or query parameters. They will need to be taken off of the request object.
Redirects with Gdn_Router
This section is currently under construction.
Event Based Routes