Module Development
Topic

Forms (Form API) for Drupal 7, 8, 9, and 10

Drupal provides a standard, secured method for module developers to add forms to a Drupal website. This method involves building render arrays in PHP and using predefined keys to specify various form element properties. The Form API describes properties for use in form arrays as well as base classes to extend, interfaces to implement, and methods for identifying, building, validating, and submitting the form.

The Configuration API also includes a special class for handling configuration settings forms, the ConfigFormBase class. See the Configuration API for Developers topic for more details about implementing configuration forms in Drupal.

Example tasks

  • Create custom forms in Drupal
  • Alter existing forms
  • Configure Drupal's core Contact module-provided forms (see the External Resources section)

Confidence

While there are many similarities to the Drupal 7 Form API, Drupal's current API is distinct, mostly in its implementation of object-oriented PHP. When browsing resources on the Form API, tutorials based on Drupal 7’s Form API will likely need at least some minor adjustments in order to work in a Drupal form today. It's best to use resources written for Drupal 8 and above.

Drupalize.Me resources

Categories
Drupal 8, 9, and 10
More information

Drupal'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.

Categories
Drupal 8, 9, and 10
More information

Understanding the flow--or life cycle--of a Drupal form will help you better understand how to build new forms or influence existing ones. 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 link to tutorials with more details about how to do so.

In this tutorial, we'll:

  • List the major steps in the life cycle of a Drupal form in the order that they are encountered
  • Describe how Drupal determines which form to display, and/or 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.

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.

Categories
Drupal 8, 9, and 10
More information

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.

Categories
Drupal 8, 9, and 10
More information

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.

Categories
Drupal 8, 9, and 10
More information

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.

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 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.

Categories
Drupal 8, 9, and 10
More information

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.

Categories
Drupal 8, 9, and 10
More information

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.

More information

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.

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.

More information

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.

More information

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() and hook_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.

Categories
Drupal 8, 9, and 10
More information

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.

Categories
Drupal 8, 9, and 10
More information

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.

More information

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.

Categories
Drupal 8, 9, and 10
More information

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.

More information

As a developer, within a module, you can define settings for the module and provide a configuration form for administrators to update the values of those settings. In this tutorial, we'll create a configuration settings form for a module and define default values for each setting. We'll use Drupal Console, a command-line utility for Drupal to do some code scaffolding and speed up the process.

By the end of this lesson, you should be able to get a basic settings form up and running inside a custom module complete with default settings and a menu link.

More information

By default, individual forms in Drupal are not output using Twig template files. It's possible to associate a form with a Twig template file by creating a new theme hook, and then referencing that theme hook from the $form array that defines the form. Doing so allows theme developers to customize the layout of the elements in the form using HTML and CSS.

This is useful when you want to change the layout of the entire form. For example, putting the elements into 2 columns. If you want to change individual elements in the form, you can often do so by overriding element specific Twig template files.

In this tutorial, we'll:

  • Learn how to create a new theme hook that can be used to theme an element in a render array.
  • Associate the $form we want to theme with the new theme hook we created.
  • Create a Twig template file for the theme hook that will allow us to lay out the form elements using custom HTML.

By the end of this tutorial, you should be able to associate a Twig template file with any form in Drupal, so that you can customize its layout using HTML and CSS.

The Form API uses the Render API, so it’s a good idea to familiarize yourself with the Render API. It may help you better understand Form API.

Drupal 8, 9, and 10
More information

The Render API consists of two parts: structured arrays that provide data and hints about how that data should be rendered, and a rendering pipeline that can be used to render these arrays into various output formats. Understanding at least the basics of how the Render API works, the difference between elements and properties, and the concept of callback functions is an integral part of learning Drupal.

In this tutorial we'll:

  • Look at the fundamentals of the Drupal Render API
  • Point to additional material to provide more detail about the inner workings of the Render API and how content is output in Drupal

Drupal 7

More information

This presentation provides an overview of the different phases of the form submission and validation process that Drupal uses with the Forms API. This will give you a good foundation for implementing forms yourself in the following videos.

More information

This videos goes through the process of creating a form with Drupal Forms API that is single select list that has a validation, submission and redirect functionality.

NOTE:

Their is a typo in the code displayed in the video. The function

function form_fun_cake(&$form_state)

is missing the $form paramater and should instead be

function form_fun_cake($form, &$form_state)

Curious about when you're supposed to translate "title" and when you should leave it alone? Here's the answer: You don't need to translate the 'title' attribute of an array that defines a menu item because Drupal will take care of that automatically. In fact, you shouldn't translate those as they'll end up getting double translated if you do. This is because Drupal end's up using this string of text in a number of different ways some of which don't actually need to be translated and others that do like for example when it's the title of a page or the text of a link in the menu system. The #title (and #description) property for elements in Form API array however are your responsibility to translate.

More information

This chapter shows how to cluster different form elements into fieldsets as well as how to expand the Forms API renderable array a tree that preserves the structure and hierarchy of the form. We'll expand the Form Fun example module and talk about the #tree property. This video uses krumo() and dsm() functions. You will need to download, install, and enable the devel module to use these functions. These functions allow you to see what variables are available to you. To accomplish the same task without using the devel module, you can add the following snippet to your module: drupal_set_message('' . print_r($vars, true) .'');

 

Note: There is a typo in the code used in this video. The function form_fun_tree() is missing a parameter, and should be as follows function form_fun_tree($form, &$form_state).

More information

This video shows the basic syntax for creating form dependencies with the states system. For a more information about the states system, then be sure to check out JavaScript form dependencies with the States System.

Note: There is a typo in the code used in this video. The function form_fun_states_form() is missing a parameter, and should be: function form_fun_states_form($form, &$form_state).

More information

When writing code for the web it is very important that you pay attention to security. Drupal provides many tools to help you out and in this presentation we'll look at what those are and how to make sure you use them properly.

Additional resources

Sanitization functions
Writing secure code

More information

This video shows how to target a specific form with the hook_form_FORM_ID_alter and creating a customized validation function for a form.

Note: There is a typo in this video. (The code is correct in the downloadable example file attached to the previous video.)

In the demo_validate_password() function, the following line shown in the video if (in_array($form['values']['pass'], $badpasswords)) { should be if (in_array($form_state['values']['pass'], $badpasswords)) {.

More information

This video goes through the process of creating a configuration form in order to save settings to the variables table in the database, and how to integrate those variables into your module.

Correction

The $item array in the function demo_menu() should be named $items, to match the return $items; line. (Either that or return $items; should be fixed to return $item;.) Just make sure the array you are building matches the name of the variable you are returning.

Additional resources

Form validation in Drupal

Guides

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

Navigate guides

External resources

Contact module

  • Contact module overview (Drupal.org)
    • Learn how to configure Drupal's built-in personal and site-wide contact forms which are provided by the core Contact module.
  • Drupal Contact Form & using SMTP to Send Email (designkojo.com)
    • Learn how to configure Drupal's built-in contact form and your server's SMTP settings in order to send email to your site's users via the contact form.