Module Development

Controllers for Drupal 8, 9, and 10

When Drupal receives an incoming request the HTTP Kernel identifies the appropriate route for the requested path. The routing system then matches this route with a controller that has been registered to handle responses for these types of requests. Sections of code responsible for generating the response to these requests are called Controllers.

Example tasks

  • Write the code in a custom module that responds to particular incoming requests
  • Understand how to identify the controllers responsible for responding to a given url, path and route.


Drupal's system of mapping request routes to controllers is quite well established. Since it's based on Symfony components it is unlikely to change dramatically in the foreseeable future.

Drupalize.Me resources

More information

Sometimes we need the response returned from a controller to be something other than HTML content wrapped with the rest of a Drupal theme. Maybe we need to return plain text, or JSON structured data for an application to consume. Perhaps we need greater control over the HTTP headers sent in the response. This is possible by building on the fact that controllers can return generic Response objects instead of renderable arrays, allowing you to gain complete control over what is sent to the requesting agent.

In this tutorial we'll:

  • Look at how to return a plain text response, and JSON data
  • Show how to make your responses cacheable by adding cacheability metadata
  • Learn about how to use a generic Symfony Response to gain greater control over what gets returned

By the end of this tutorial, you should be able to return responses from a controller in a Drupal module that are not HTML content wrapped in a Drupal theme.

Controllers and Form API

More information

Each form is defined by a controller, a class that implements the \Drupal\Core\Form\FormInterface. Form controllers declare the unique ID of the form, the $form array that describes the content of the form, how to validate the form, and what to do with the data collected.

In this tutorial we'll:

  • Define a new form controller class
  • Implement the required methods to describe a form
  • Add a route that can be used to access our form

By the end of this tutorial you should be able to define a form that adheres to the FormInterface requirements and know where to find more information about how to further customize your form controller.

More information

When your module defines the form and the form controller, you can add your validation logic as part of the form controller. This is done via the implementation of a validateForm() method. The FormBuilder service will automatically invoke this method at the appropriate time during the process of submitting a form. While the validateForm() method is required by \Drupal\Core\Form\FormInterface, an empty method will fulfill that requirement. It's up to you to provide appropriate validation code inside the method.

In this tutorial we'll:

  • Use the validateForm() method of a form controller to verify user input
  • Demonstrate how to raise errors on a form element when it doesn't pass validation

By the end of this tutorial you should know how to validate your custom forms.

More information

When you create a custom form for Drupal and your module defines the form controller, the best way to handle processing of submitted data is via the submitForm() method of your controller. This method is called automatically by the Form API during the process of handling a user-submitted form. It can be used to save incoming data to the database, trigger a workflow based on user input, and instruct the Form API where to send the user after form processing has completed.

In this tutorial we'll:

  • Demonstrate how to add a submitForm() method to a form controller class
  • Access the value(s) of form input elements via the $form_state object
  • Set a redirect after performing processing in a form submission handler

By the end of this tutorial you should know how to access the values of a submitted form, and how to write custom processing code inside of the submitForm() method of a form controller.

More information

Eventually you'll want to do something with the information your form collects beyond just printing it to the screen. It's generally considered a best practice to keep business logic out of your form controller so that it can be reused. In order to accomplish that you'll generally define your business logic in a service, and then call out to that service from your form controller. Or, you can make use of one of the existing services provided by Drupal core to save data.

In this tutorial we'll:

  • Use dependency injection to inject a service into a form controller
  • Make use of a service injected into a form controller from within the buildForm() and submitForm() methods

By the end of this tutorial you'll understand how to inject one or more services into your form controller and then make use of them.

Related topics


Drupal 8, 9, and 10
More information

Drupal's Routing API maps the URL of an incoming request to the code responsible for generating the content that is rendered in response.


Drupal 8, 9, and 10
More information

Symfony is a set of reusable PHP components, and a framework for building PHP applications. Drupal makes use of various Symfony components.


Not sure where to start? Our guides provide useful learning tracks for all skill levels.

Navigate guides

External resources

  • Adding a basic controller (
    • This tutorial covers creating a very basic controller.
  • Examples for Developers project's page_example module (
    • The Examples project contains many sub-modules that demonstrate various Drupal sub-systems through well-documented code. To learn more about how routing and controllers work, take a look at the page_example module here.