Modules can declare their interest in being notified when a specific event, or events, are triggered by implementing an event subscriber. This can be used to react to events in order to add your own logic, or to alter Drupal's existing functionality.
In this tutorial we'll cover how to subscribe to an event from your own module. After completing this tutorial you should be able to subscribe to any event and ensure that your custom code is executed when the specific event is triggered.
What Are Events?
FreeDrupal uses events to allow modules and various subsystems to communicate with one another in an object-oriented manner. Understanding how the Event API works is critical knowledge for all module developers. In this tutorial we'll:
- Explain what events are
- Discuss the use case for subscribing to events
- Give an example use case for dispatching events
By the end of this tutorial you should be able to explain what events are, understand when to use them in your own code, and know where to learn more about how to do so.
Asynchronous JavaScript And XML (Ajax) is a programming practice for building more complex, dynamic webpages using a technology known as XMLHttpRequest. It allows you to asynchronously perform server-side operations without requiring a refresh, thus allowing for more complex user interaction and, in some cases, improved user experience.
In this tutorial we'll:
- Define what Ajax is
- Look at how Ajax is implemented in the Drupal Form API
- Provide links to additional resources to learn more about implementing Ajax in your own forms
By the end of this tutorial you should be able to explain what Ajax is, when you might want to use it, and how to get started doing so with Drupal's Form API.
A common use of Ajax is to alter a form by adding, removing, or updating parts of the form in response to actions taken by the user. The resulting altered form is still eventually submitted with a traditional HTTP POST request. For example, one might update the options available in a city dropdown field after someone has chosen a value in the country dropdown, or add an additional textfield for collecting a person's name when the user clicks an "Add another person" button.
In this tutorial we'll:
- Understand why certain types of modifications to a form require the use of Ajax
- Use
#ajax
in conjunction with a<select>
field to demonstrate how to update a form with Ajax - Learn about responding to user interaction with
#ajax
By the end of this tutorial you should be able to use the #ajax
attribute on any form element to respond to user actions and update the form displayed to the user with new content and options.
Using Ajax allows you to create forms that are submitted to the server, and processed, without requiring a page reload.
In this tutorial we'll:
- Use
#ajax
with a'#type' => 'submit'
button in order to submit a form via Ajax - Look at how form build, validation, and processing are used when submitting a form via Ajax
- Use the form's internal storage to track data across multiple requests
- Discuss some best practices to keep in mind when using Ajax for form submissions
By the end of this tutorial you should know how to update an existing form so that it is submitted via Ajax and no longer requires a page refresh to work.
You'll often need to make minor, or major, alterations to an existing form provided by another module. The Form API allows you to alter any existing form through a series of hooks without having to change the existing module's code at all. This is probably one of the most powerful features of the Drupal Form API. Knowing how to implement and leverage hook_form_alter()
and its variations is an essential skill for any module developer.
In this tutorial we'll:
- Learn how to implement
hook_form_alter()
andhook_form_FORM_ID_alter()
in a module - Modify existing elements, or add new elements, to a form provided by another module
- Understand how to add new validation and submission handlers when altering an existing form
By the end of this tutorial you should know how to alter almost everything about the way any form in Drupal works without having to hack the module that provides the form.
Module developers can add new elements to a form by adding their definition to the $form
array in the buildForm()
method of their controller or via an implementation of hook_form_alter()
. Doing so requires knowing the element #type
, and details about any element-type-specific properties.
In this tutorial we'll:
- Determine the element type to use for the HTML input element we want to use
- Consult the documentation for the two form element types we're using
- Add a checkbox and a select list to our form via the
buildForm()
method of our form controller
By the end of this tutorial you'll know how to add new elements to an existing $form
array in order to collect additional data from users.
Forms are used for both collecting new data, and editing existing data. In order to allow users to modify existing data you need to pre-populate the elements on the form with the data you previously stored.
In this tutorial we'll look at the ways in which forms can be pre-populated with existing data, including:
- Providing default values for form elements which a user can edit with the
#default_value
property - The differences between the
#value
and#default_value
properties
By the end of this tutorial you should know how to populate forms using existing data.
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.
Forms can be displayed as the main content of a route or by using the form_builder
service to retrieve and display a form directly. Which one you choose will depend on where on the page you want the form to appear.
In this tutorial we'll:
- Go over the two different ways a form can be retrieved and displayed
- Demonstrate how to display a form inside a custom block using the
form_builder
service
By the end of this tutorial you'll know how to retrieve a form and have it rendered anywhere on the page.
Drupal core provides a couple dozen different input #type
elements that can be added to forms. This includes one for every standard HTML5 input element, and some Drupal-specific ones that encapsulate more complex interactions like uploading files. But how can you know what elements exist? Where do you find information about what Render API properties each element uses?
In this tutorial we'll:
- Define what
FormElements
are and how they relate to the Render API - Find a list of all available input element types, additional documentation and usage examples
- See examples of the most common element types
By the end of this tutorial you should be able to discover the different types of elements you can add to a $form
array and find usage examples for each.
This tutorial will help you understand the complete life cycle of a Drupal form: receiving the request from a browser, displaying a page with a form, rendering the form as HTML, handling the submitted form, validating input, handling errors, and processing data. We'll point out the common places that module developers might want to inject additional functionality into the process. And we'll link to tutorials with more details about each integration point in a form's life cycle.
In this tutorial, we'll:
- List the steps of the life cycle of a Drupal form.
- Describe how Drupal determines which form to display, and which form handles an HTTP POST request.
- Understand the role of
FormStateInterface
in the life cycle of a form.
By the end of this tutorial, you should have a solid understanding of the life cycle of a form within Drupal.
Form API Overview
FreeDrupal's Form API is a set of interfaces, utility classes, and conventions that when combined together allow module developers to create forms that collect, validate, and process user-submitted data. The Form API is closely related to the Render API. It uses Render Arrays to define forms, and adds an additional level of workflow and processing to enhance the Render API with some features specific to handling forms.
Given that forms are one of the primary means of interacting with a Drupal site via the UI, understanding how the Form API works is a critical part of learning to develop modules for Drupal. While you may not need to know all the nitty-gritty details, every Drupal module developer is likely to encounter aspects of the Form API at some point. Understanding the basics should be considered required knowledge.
Theme developers are also likely to encounter some aspects of the Form API, as forms are inherently part of the look and feel of a site. Knowing how to make changes to the UX of a form is an important skill.
In this tutorial we'll:
- Describe what forms are and how they are used in Drupal
- Explain the relationship between Form API and the Render API
- List some of the benefits of using the Form API over generic HTML forms
By the end of this tutorial you should have a solid understanding of the role of the Form API in Drupal.
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()
andsubmitForm()
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.
You probably created a form with the intent of collecting user input and then doing something with that input. Using the submitForm()
method of our form class we can access the validated, and sanitized, user input data and make use of it for our own needs. We might do things like save the collected data as configuration, update an entity, or use it as part of a search query.
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
- Look at alternative ways to affect the submission handling of a form like
#submit
callbacks
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 that gets invoked when the form passes validation.
Sometimes you need to add additional processing of input to forms where your module doesn't implement the form controller. In order to do this you can use the #submit
property of the root level $form
element, or of a specific button on a form, to add one or more callbacks. These functions, or methods, will be automatically called when the Form API is processing a submitted form and give your custom code an opportunity to do whatever it needs to do.
In this tutorial we'll:
- Look at alternative ways to affect the submission handling of a form like
#submit
callbacks
By the end of this tutorial you should know how to add a #submit
callback to an entire form, or a specific button in a form.
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.
When a form is submitted you'll need to check the data input by the user to ensure that it matches certain constraints, and to raise errors when necessary. Is the email address in the proper format? Is the title field long enough? Does the ASIN ID entered match a valid Amazon product? This process is called validation and is handled by a combination of the validateForm()
method of a form controller, and validation callbacks.
In this tutorial we'll:
- Explain the use case for both the
validateForm()
method of a form controller, and validation callbacks - Discuss additional uses for validation handlers beyond just checking the length of a text field, or format of a phone number field
By the end of this tutorial you should know how to start adding custom validation logic to any form in Drupal.
When working with forms that are not created by your code, where you're not implementing the form controller but rather interacting with the form via implementations of hook_form_alter()
, you can use the #validate
property of the root form element to add additional validation logic in the form of a callback function or method.
In this tutorial we'll:
- Implement a
#validate
callback that raises an error if specific conditions are not met
By the end of this tutorial you should know how to add custom validation logic to any form in Drupal by using a #validate
callback.
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.