As we learned in Configuration Data Types, simple configuration is suitable for storing module settings as boolean values, integers, or text strings in one or more key/value pairs. In this tutorial, we'll walk through creating a schema and providing default configuration to store initial settings that a module needs to function.
Not every environment or copy of a site you may be working on will be created equally. You may want to enable logging on a development site or need to use different API keys depending on the environment. But you also need to make sure that your instance-specific configuration overrides don't make it into the database, mistakenly get exported, or compromise security.
In this tutorial, you will learn how to:
- Override the global
$config
array in settings.php (or settings.local.php) - Retrieve overridden (immutable) configuration (read-only mode)
- Retrieve original (mutable) configuration for updating (read/write mode)
- Set dynamic values for configuration instead of overriding values
When you create a module for Drupal, it can be useful to provide default configuration. This can be settings for a form, the placement of a block, or something more complex like the default image styles provided by the Image module in core. A module can provide default configuration for simple configuration or configuration entities.
In this tutorial, we will cover:
- Possible locations for default configuration
- What happens with configuration when a module is installed or uninstalled
- Managing dependencies in configuration
- Where to find examples of default configuration
Configuration entities are suitable for creating user-defined configuration, such as image styles, views, content types, etc. A configuration entity type is defined by a module, default configuration is provided by that module as well as any other module, and then users can create zero or more configuration entities through Drupal's administrative UI.
In this tutorial, you will learn about:
- What configuration entities are
- Configuration entity types versus configuration entities
- An example in core: image style
- Overview of the process of creating your own configuration entity types in a module
When working on configuration in a module, whether as part of a migration that uses Migrate Plus configuration entities, or while developing custom configuration entities, you'll often need to re-import the configuration stored in the .yml files of the modules config/install/ or config/optional/ directories. This is tricky though, because Drupal only reads in those default configuration settings when the module is first enabled. So any changes you make to those files after the module has been installed will not be reflected without these workarounds.
Knowing how to do this can improve the developer experience of adding (or debugging) the default configuration that's provided with a module. Or for anyone using Migrate Plus configuration entities as part of a migration.
In this tutorial we'll:
- Learn about the Configuration Development module
- Look at how you can use Drush to perform a partial configuration import
- Write an implementation of
hook_uninstall()
to remove a module's configuration when it's uninstalled
By the end of this tutorial you should be able to re-import the configuration provided by a module without having to uninstall and then reinstall the module.
Now that we have some default simple configuration stored in a settings YAML file, let's utilize it in a form that our site administrators can use to update those values. We'll make use of some services and methods in Drupal's Configuration API in order to retrieve, update, and save simple configuration values with a form.
In this tutorial, we'll cover how to load configuration entity data in a module. We'll change the AdminSettingsForm.php we created and replace the simple textfield we were using with a dropdown select list. Then we'll use data from our Transcode Profile module's configuration entity, loaded by the EntityTypeManager
via the services container, to choose our preferred Transcode Profile.
By the end of this tutorial, you should be able to:
- Know how to load configuration entities using
EntityTypeManager
via the services container - Update the AdminSettingsForm.php to use a dropdown select list
- Save your preferred transcode profile from a list of transcode profile entities
- Update the default configuration provided with the demo module to include transcode profiles
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.
When defining a new FormElement
plugin, or if your constraints are only relevant for a single element, you can use the #element_validate
property of an individual element to specify additional validation callbacks.
In this tutorial we'll:
- Add an
#element_validate
callback to a single element in a form to perform validation of just that element.
By the end of this tutorial you should know how to add #element_validate
callbacks to any form element in Drupal.
How do you figure out what hooks are available to implement? How do you know which hook to implement to accomplish a task?
The list of hooks that are available to implement in your custom Drupal module varies depending on the modules enabled for a site. Each module can optionally invoke new hooks. There are also some hooks invoked by Drupal core subsystems like Form API that are always present. This can make it a little bit tricky sometimes to figure out what hooks are available, and which one to implement.
In this tutorial we'll look at:
- Different ways to get a list of available hooks
- Where to find the documentation for a hook so you can know if it's the one you want to implement
By the end of this tutorial you should be able to browse a list of hooks and their related documentation.
Hooks allow module developers to execute custom code at key moments during Drupal's request processing life cycle. They can be used to react to actions or conditions, alter forms and existing configuration, and extend Drupal in various ways. Knowing how to implement a hook is an essential skill for any Drupal developer.
In this tutorial we'll walk through the process of locating the documentation for a specific hook, and then implementing it. All hooks are implemented in the same way, so once you know how to implement one, you'll be able to implement any.