Controllers
Plugins and modules can provide custom controllers (opens new window) to Craft installations.
Controllers should live within a controllers/
folder within the plugin or modules’s base source folder, and be named
in the format FooBarController.php
(the Controller
suffix is required).
For the most part, writing controllers for Craft is identical to writing controllers for Yii, so be sure to read the Yii documentation (opens new window) as a starting point.
Craft controllers should extend craft\web\Controller (opens new window), which offers a few advantages over its parent, yii\web\Controller (opens new window):
- You can easily control whether the controller should allow anonymous access by overriding $allowAnonymous (opens new window). (An active user session is required by default.)
- If an exception is thrown by a controller action and the request accepts a JSON response, the response will
automatically be formatted as JSON, with an
error
key. - It provides several helper methods that ease development.
If you’re writing a custom module and not a plugin, make sure your module’s $controllerNamespace (opens new window) property sets the right namespace for your controllers.
# Request Validation Methods
craft\web\Controller (opens new window) offers several methods you can call from within your actions, to validate the current request:
Method | Description |
---|---|
requireLogin() (opens new window) | Requires that a user is logged in. |
requireGuest() (opens new window) | Requires that the user is anonymous. |
requireAdmin() (opens new window) | Requires that the user is logged in with an Admin account. |
requirePermission() (opens new window) | Requires that the user is logged in with an account that has a given permission. |
requireAuthorization() (opens new window) | Requires that the user has been granted authorization to do something (whether or not they are logged in). |
requireElevatedSession() (opens new window) | Requires that the user has an elevated session. |
requirePostRequest() (opens new window) | Requires that the request was sent as a POST request. |
requireAcceptsJson() (opens new window) | Requires that the request was sent with an Accept: application/json header. |
requireToken() (opens new window) | Requires that the request was sent with a token (opens new window). |
requireCpRequest() (opens new window) | Requires that the request URI begins with the control panel trigger. |
requireSiteRequest() (opens new window) | Requires that the request URI doesn’t begin with the control panel trigger. |
public function actionFoo()
{
// This action should only be available to the control panel
$this->requireCpRequest();
// ...
}
# Requesting Your Controller Action
There are several ways to access your controller action in a request.
# POST action
Param
Provide an action
param set to your controller’s action path:
curl -d "action=plugin-handle/controller/action" \
-X POST https://my-project.tld/
# Custom Route
Create your own endpoint for requests with a custom URL rule that resolves to your controller action.
For example, in config/routes.php
:
return [
'my/custom/endpoint' => 'plugin-handle/controller/action',
];
# The actions/<action-path>
Route
By default, Craft makes an actions/
route available for appending any valid action path. This can be customized with the actionTrigger config setting.
curl -X POST https://my-project.tld/actions/plugin-handle/controller/action
# Default Route Format
Default plugin and module action routes follow the same pattern:
Action Trigger + Plugin/Module Handle + Controller Name + Action Method
Each URL segment follows Yii’s conventions (opens new window) and is lower-kebab-cased:
- Handle
my-plugin
(from composer.json) ormy-module
(from config/app.php) - Controller
SuperWidgetController
becomessuper-widget
- Action
SuperWidgetController::actionReticulateWidget()
becomesreticulate-widget
# Handling Requests
A controller action’s primary job is to handle an incoming web request, and determine the response. There are a few ways an action could go about that, depending on the needs.
# Rendering Templates
Controller actions can render and return Twig templates using craft\web\Controller::renderTemplate() (opens new window).
use yii\web\Response;
use craft\web\View;
public function actionFoo(): Response
{
// Render and return the plugin’s `foo.twig` template
return $this->renderTemplate(
'plugin-handle/foo.twig',
$variables,
View::TEMPLATE_MODE_CP
);
}
craft\web\Controller::renderTemplate() (opens new window) calls craft\web\View::renderPageTemplate() (opens new window) internally, which
ensures all registered JS and CSS resources have been added to the rendered HTML, and then it will set the
Content-Type
header on the response, based on the MIME type of the template being rendered (using text/html
as the
default if the MIME type isn’t known).
# Returning JSON
Controller actions can return JSON responses using yii\web\Controller::asJson() (opens new window).
use Craft;
use yii\web\Response;
public function actionFoo(): Response
{
if (Craft::$app->request->acceptsJson) {
return $this->asJson([
'foo' => true,
]);
}
// ...
}
You can call craft\web\Controller::asErrorJson() (opens new window) instead for an easy way to return a JSON response with an error
key.
# Redirecting the Request
Controller actions can redirect the request using craft\web\Controller::redirect() (opens new window).
use yii\web\Response;
public function actionFoo(): Response
{
return $this->redirect('bar');
}
Or, if the request may contain a hashed redirect
param, you can redirect to that using craft\web\Controller::redirectToPostedUrl() (opens new window).
use yii\web\Response;
public function actionFoo(): Response
{
// Redirect the request based on a 'redirect' param
return $this->redirectToPostedUrl();
}
If the controller action is saving something, you may want to allow forms’ redirect
params to include dynamic tokens
such as {id}
, which should be replaced with the object’s attribute values. To support that, pass the object into
redirectToPostedUrl() (opens new window).
use yii\web\Response;
public function actionFoo(): Response
{
// ...
// Redirect the request based on a 'redirect' param,
// which can contain entry attribute tokens, such as {id}
return $this->redirectToPostedUrl($entry);
}