Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Forms

A form in Drupal is described with an array, not with markup. From that description the system renders the fields, checks required values, protects against request forgery and calls your validation and submit methods. Let us make a form for subscribing to new blog articles.

Where a form lives

Each form is a separate class in the src/Form folder of the module. Create the folder and the file by hand.

The form class

// web/modules/custom/blog/src/Form/SubscribeForm.php
namespace Drupal\blog\Form;
 
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
 
class SubscribeForm extends FormBase
{
    public function getFormId(): string
    {
        return 'blog_subscribe_form';
    }
 
    public function buildForm(array $form, FormStateInterface $form_state): array
    {
        $form['email'] = [
            '#type' => 'email',
            '#title' => $this->t('Email'),
            '#required' => TRUE,
        ];
 
        $form['actions']['submit'] = [
            '#type' => 'submit',
            '#value' => $this->t('Subscribe'),
        ];
 
        return $form;
    }
 
    public function submitForm(array &$form, FormStateInterface $form_state): void
    {
        $email = $form_state->getValue('email');
 
        $this->messenger()->addStatus($this->t('The address @email is subscribed.', ['@email' => $email]));
    }
}
MethodWhat it does
getFormId()Returns the unique name of the form
buildForm()Describes the fields and buttons
submitForm()Runs after a successful submission

Fields are the same render arrays with a #type property. The values of the submitted form are kept in the $form_state object.

The form route

A form needs no controller: the route names _form instead of _controller:

# web/modules/custom/blog/blog.routing.yml
blog.subscribe:
  path: '/blog/subscribe'
  defaults:
    _form: '\Drupal\blog\Form\SubscribeForm'
    _title: 'Subscription'
  requirements:
    _permission: 'access content'

After drush cr open /blog/subscribe: a field and a button appear. The browser does not send an empty form, and after a submission with an address a green message appears at the top of the page.

Validation

Your own validation rules go into the validateForm() method:

// web/modules/custom/blog/src/Form/SubscribeForm.php
public function validateForm(array &$form, FormStateInterface $form_state): void
{
    if (str_ends_with($form_state->getValue('email'), '@example.com')) {
        $form_state->setErrorByName('email', $this->t('Enter a real address.'));
    }
}

setErrorByName() marks the field with an error: the form returns to the visitor with a red message and submitForm() does not run. Drupal checked the address format and the required flag itself — from #type and #required.

Form elements

TypeField
textfieldA single line
textareaMulti-line text
email, tel, url, numberFields with format validation
selectA list: options in #options
checkbox, checkboxes, radiosCheckboxes and radio buttons
dateA date
submitThe submit button

Common properties: #title, #description, #default_value, #required.

After submission

// web/modules/custom/blog/src/Form/SubscribeForm.php
$this->messenger()->addStatus($this->t('Done.'));
$this->messenger()->addError($this->t('Could not save.'));
 
$form_state->setRedirect('blog.list');

A message is shown once — on the next page. setRedirect() sends the visitor to a route by name; without it the visitor stays on the form page. The form starts saving the address to the database in the lesson about the database.

A form inside a page

A form can be output as a part of a render array in a controller:

// web/modules/custom/blog/src/Controller/BlogController.php
$build['subscribe'] = $this->formBuilder()->getForm(\Drupal\blog\Form\SubscribeForm::class);

Common mistake

Do not read data from $_POST: it holds values before validation and without forgery protection. Use $form_state->getValue('email') — it holds only the fields described in the form that passed validation. And do not output a form with plain HTML tags: without the Form API it has no protection token.

Thanks for reading!

Donate to the site — we work hard for you.