A central task solved by Pagekit is _Routing_. When the browser hits a URL, the framework determines, what action will be called.
The most common way of creating routes in Pagekit is to define a controller. A controller is responsible for handling requests, setting routes and rendering views.
You can register a controller inside your module configuration. Use the routes
property to mount controllers to a route.
'routes' => [
'/hello' => [
'name' => '@hello/admin',
'controller' => [
'Pagekit\\Hello\\Controller\\HelloController'
]
]
],
The class is annotated with @Route("/hello")
, causing the controller to be mounted at http://example.com/hello/
. That means it will respond to all requests to that URL and sub-URLs like http://example.com/hello/settings
.
namespace Pagekit\Hello\Controller;
/**
* @Route("/hello")
*/
class HelloController
{
public function indexAction()
{
// ...
}
public function settingsAction()
{
// ...
}
}
By default, your extension (or theme) will be booted and a set of default routes will be generated automatically. You can use the developer toolbar to view the newly registered routes (along with all core routes).
Here is how to understand a route:
Route | Example | Description |
---|---|---|
Name | @hello/hello/settings |
The name of the route can be used to generate URLs (has to be unique). |
URI | /hello/settings |
The path to access this route in the browser. |
Action | Pagekit\Hello\Controller\DefaultController::settingsAction |
The controller action that will be called. |
By default, routes will be of the form http://example.com/<extension>/<controller>/<action>
. A special action is indexAction
, which will not be mounted at .../index
, but at .../
. Advanced options for custom routes are available of course, as you will see in the next sections.
Note If a route is not unique across your application, the one that has been added first is the one being used. As this is framework internal behavior that might change, you should not rely on this but rather make sure your routes are unique.
A lot of the controller's behavior is determined by information annotated to the class and methods. Here is a quick overview, a detailed description for each annotation follows below.
Annotation | Description |
---|---|
@Route |
Route to mount an action or the whole controller. |
@Request |
Handle parameter passing from the http request to the method. |
@Access |
Check for user permissions. |
Note Annotations will only work if you start the multiline comment with two asterisks, not just with one.
// will NOT work:
/* @Route("/hello") */
// will work:
/** @Route("/hello") */
// will work:
/**
* @Route("/hello")
*/
Define the route the controller (or controller action) will be mounted at. It can be annotated to class and method definitions.
By default, a method called greetAction
will be mounted as /greet
under the route of the class. To add custom routes, you can add any number of additional routes to a method. Routes can also include dynamic parameters, which will be passed to the method.
/**
* @Route("/greet", name="@hello/greet/world")
* @Route("/greet/{name}", name="@hello/greet/name")
*/
public function greetAction($name = 'World')
{
// ...
}
Parameters can be specified to fulfill certain requirements (for example limit the value to numbers). You can name a route so that you can reference from your code. Use PHP argument defaults at the method definition to make a parameter optional.
Routes can be bound to certain HTTP-methods (e.g. GET
or POST
). This is especially useful for RESTful APIs.
/**
* @Route("/view/{id}", name="@hello/view/id", requirements={"id"="\d+"}, methods="GET")
*/
public function viewAction($id = 0)
{
// ...
}
Note For detailed information and more examples, have a look at Symfony's own documentation on the @Route
annotation.
You can specify the types of data passed via a request and match them to parameters passed to the annotated method.
The array maps name to type. name is the key inside the request data. type can be int
, string
, array
and advanced types like int[]
for an array of integers. If no type is specified, string
is assumed by default.
The order of the keys will define the order, in which parameters are being passed to the method. The parameter name in the method head can be anything.
/**
* @Request({"id": "int", "title", "config": "array"}, csrf=true)
*/
public function saveAction($id, $title, $config)
{
// ...
}
You can also check for a token to protect against CSRF. Add csrf=true
to your request annotation and include the @token
call in the view that submits a form to this method.
Check out Pagekit\Filter\FilterManager
for a complete list of available filters. Some filters have additional options, like pregreplace
.
/**
* @Request({"folders":"pregreplace[]"}, options={"folders" = {"pattern":"/[^a-z0-9_-]/i"}})
*/
public function deleteFolders($folders)
{
// ...
}
You can specify certain user permissions required to access a specific method or the whole controller.
Controllers should always be specific to the frontend or the admin panel. So far, we have seen controllers for the frontend. An administration controller will only be accessible for users with the Access admin area
permission. Also, all routes for that controller will have a leading admin/
in the URL. As a result, views will also render in the admin layout and not in the default theme layout.
/**
* @Access(admin=true)
*/
class SettingsController
{
// ...
}
Now, only users with the Access admin area permission can access the controller actions. If you want to use further restrictions and only allow certain users to do specific actions (like manage users etc.), you can add restrictions to single controller actions.
Define permissions in the extension.php
(or theme.php
) file and combine them however you want. Access restrictions from the controller level will be combined with access restrictions on the single actions. Therefore you can set a basic minimum access level for your controller and limit certain actions, like administrative actions, to users with more specific permissions.
/**
* @Access("hello: manage users")
*/
public function saveAction()
{
// ...
}
Of course, you can also use these restrictions even if the controller is not an admin area controller. You can also check for admin permissions on single controller actions.
/**
* @Access("hello: edit article", admin=true)
*/
public function editAction()
{
// ...
}
Using the URL service, you can generate URLs to your routes.
$this['url']->route('@hello/default/index') // '/hello/default/index'
$this['url']->route('@hello/default/index', true) // 'http://example.com/hello/default/index'
$this['url']->route('@hello/view/id', ['id' => 23]) // '/hello/view/23'
Pagekit's routes can be described by an internal route syntax. They consist of the route name (e.g. '@hello/name'), optionally followed by GET parameters (e.g. '@hello/name?name=World'). This is called a link. It separates the actual route URI from the route itself.