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) or my-module (from config/app.php)
  • Controller SuperWidgetController becomes super-widget
  • Action SuperWidgetController::actionReticulateWidget() becomes reticulate-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);
}