Skip to main content

Building Forms Programmatically

Drupal provides a Form API in order to achieve consistency in its form processing and presentation while simplifying code and reducing the amount of HTML that a module must explicitly generate.

Creating forms

Forms are classes that implement the \Drupal\Core\Form\FormInterface and are built using the \Drupal\Core\Form\FormBuilder class.

Drupal provides a couple of utility classes that can be extended as a starting point for most basic forms, the most commonly used of which is \Drupal\Core\Form\FormBase.

FormBuilder handles the low level processing of forms such as rendering the necessary HTML, initial processing of incoming $_POST data, and delegating to your implementation of FormInterface for validation and processing of submitted data.

Here is an example of a Form class:

namespace Drupal\mymodule\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;


class ExampleForm extends FormBase {

  public function getFormId() {
    // Unique ID of the form.
    return 'example_form';
  }

  public function buildForm(array $form, FormStateInterface $form_state) {
    // Create a $form API array.
    $form['phone_number'] = array(
      '#type' => 'tel',
      '#title' => $this
        ->t('Your phone number'),
    );
    $form['save'] = array(
      '#type' => 'submit',
      '#value' => $this
        ->t('Save'),
    );
    return $form;
  }

  public function validateForm(array &$form, FormStateInterface $form_state) {
    // Validate submitted form data.
  }

  public function submitForm(array &$form, FormStateInterface $form_state) {
    // Handle submitted form data.
  }

}

 

 

Retrieving and displaying forms

\Drupal::formBuilder()->getForm() should be used to handle retrieving, processing, and displaying a rendered HTML form.

Given the ExampleForm defined above, \Drupal::formBuilder()->getForm('Drupal\mymodule\Form\ExampleForm') would return the rendered HTML of the form defined by ExampleForm::buildForm(), or call the validateForm() and submitForm(), methods depending on the current processing state.

The argument to \Drupal::formBuilder()->getForm() is the name of a class that implements FormInterface.

Any additional arguments passed to the getForm() method will be passed along as additional arguments to the ExampleForm::buildForm() method.

For example:

$extra = '612-123-4567';
$form = \Drupal::formBuilder()->getForm('Drupal\mymodule\Form\ExampleForm', $extra);
...
public function buildForm(array $form, FormStateInterface $form_state, $extra = NULL)
  $form['phone_number'] = array(
    '#type' => 'tel',
    '#title' => $this->t('Your phone number'),
    '#value' => $extra,
  );
  return $form;
}

 

Alternatively, forms can be built directly via the routing system which will take care of calling \Drupal::formBuilder()->getForm().

The following example demonstrates the use of a routing.yml file to display a form at the given route.

example.form:
  path: '/example-form'
  defaults:
    _title: 'Example form'
    _form: '\Drupal\mymodule\Form\ExampleForm'

 

The $form argument to form-related functions is a specialized render array containing the elements and properties of the form.

For more about render arrays, see the Render API topic. 

For more detailed explanations of the Form API workflow, see the Form API documentation section. 

In addition, there is a set of Form API tutorials in the Examples for Developers project.

In the form builder, validation, submission, and other form methods, $form_state is the primary influence on the processing of the form and is passed to most methods, so they can use it to communicate with the form system and each other.

$form_state is an object that implements \Drupal\Core\Form\FormStateInterface.

 

 

Practical Information

Based on the FormInterface when building a form there are 4 methods that must be redefined:

1. getFormId

2. buildForm

3. validateForm

4. submitForm

review example above

here is a template for all four which you can copy-paste into the form class

  
public function getFormId() {
  // Unique ID of the form.
  return 'unique.id';
}

public function buildForm(array $form, FormStateInterface $form_state) {
  // Create a $form API array.
  $form['phone_number'] = array(
    '#type' => 'tel',
    '#title' => $this->t('Your phone number'),
  );

  $form['actions'] = [
    '#type' => 'actions',
    '#attributes' => [
      'class' => ['my_actions'],
    ],              
  ]; 

  $form['actions']['save'] = array(
    '#type' => 'submit',
    '#value' => $this->t('Save'),
  );
           
  $form['actions']['submit'] = array(
    '#type' => 'submit',
    '#value' => $this->t('Submit'),
    '#ajax' => [
      'callback' => '::callbacksubmit',
    ], 
    '#attributes' => [
      'class' => ['my_button','button'],
    ],                
  ); 

  return $form;
}

public function validateForm(array &$form, FormStateInterface $form_state) {
  // Validate submitted form data.
}

public function submitForm(array &$form, FormStateInterface $form_state) {
  // Handle submitted form data.
}

 

 

The Form Location

Drupal 9 has a module structure strategy which means that the form should be set in a specific folder

The directory structure should look like that:  module_folder/src/Form/your_form.php

And the first line in the form code is: namespace Drupal\module_name\Form;