Creating a form

In this recipe, we will create a form, which will be accessible from a menu path. This will involve creating a route that tells Drupal to invoke our form and display it to the end user.

Forms are defined as classes, which implement DrupalCoreFormFormInterface. The DrupalCoreFormFormBase serves as a utility class that is intended to be extended. We will extend this class to create a new form.

Getting ready

Since we will be writing the code, you will want to have a custom module. Creating a custom module in Drupal is simply creating a folder and an info.yml file. For this recipe, we will create a folder under /modules in your Drupal folder called drupalform.

In the drupalform folder, create drupalform.info.yml. The info.yml file is what Drupal will parse to discover modules. An example of a module's info.yml file is as follows:

name: Drupal form example
description: Create a basic Drupal form, accessible from a route
type: module
version: 1.0
core: 8.x

The name will be your module's name, and the description will be listed on the Extend page. Specifying the core tells Drupal what version of Drupal it is built for. Chapter 4, Extending Drupal covers how to create a module in depth.

How to do it…

  1. Create an src folder in your module directory. In this directory, create a Form directory, which will hold the class that defines your form.
  2. Next, create a file called ExampleForm.php in your module's src/Form directory.

    Note

    Drupal utilizes PSR4 to discover and autoload classes. For brevity, this defines that there should be one class per file, with each filename matching the class name. The folder structure will also mimic the namespace expected.

  3. We will edit the ExampleForm.php file and add the proper PHP namespace, classes used, and the class itself:
    <?php
    
    /**
     * @file
     * Contains DrupaldrupalformFormExampleForm.
     **/
    
    namespace DrupaldrupalformForm;
    
    use DrupalCoreFormFormBase;
    use DrupalCoreFormFormStateInterface;
    
    class ExampleForm extends FormBase {
    
    }

    The namespace defines the class in your module's Form directory. The autoloader will now look into the drupalform module path and load the ExampleForm class from the src/Form directory.

    The use statement allows us to use just the class name when referencing FormBase and, in the next steps, FormStateInterface. Otherwise, we would be forced to use the fully qualified namespace path for each class whenever it is used.

  4. DrupalCoreFormFormBase is an abstract class and requires us to implement four remaining interface methods: getFormId, buildForm, validateForm, and submitForm. The latter two are covered in their own recipes; however, we will need to define the method stubs:
    class ExampleForm extends FormBase {
    
      /**
       * {@inheritdoc}
       */
      public function getFormId() {
        return 'drupalform_example_form';
      }
    
      /**
       * {@inheritdoc}
       */
      public function buildForm(array $form, FormStateInterface $form_state) {
         // Return array of Form API elements.
      }
    
      /**
       * {@inheritdoc}
       */
      public function validateForm(array &$form,  FormStateInterface $form_state) {
        // Validation covered in later recipe, required to satisfy interface
      }
    
      /**
       * {@inheritdoc}
       */
      public function submitForm(array &$form,  FormStateInterface $form_state) {
        // Validation covered in later recipe, required to satisfy interface
      }
    }
    • This code flushes out the initial class definition from the previous step. FormBase provides utility methods and does not satisfy the interface requirements for FormStateInterface. We define those here, as they are unique across each form definition.
    • The getFormId method returns a unique string to identify the form, for example, site_information. You may encounter some forms that append _form to the end of their form ID. This is not required, and it is just a naming convention often found in previous versions of Drupal.
    • The buildForm method is covered in the following steps. The validateForm and submitForm methods are both called during the Form API processes and are covered in later recipes.
  5. The buildForm method will be invoked to return Form API elements that are rendered to the end user. We will add a simple text field to ask for a company name and a submit button:
      /**
       * {@inheritdoc}
       */
    public function buildForm(array $form, FormStateInterface $form_state) {
      $form['company_name'] = array(
        '#type' => 'textfield',
        '#title' => $this->t('Company name'),
      );
      $form['submit'] = array(
        '#type' => 'submit',
        '#value' => $this->t('Save'),
      );
      return $form;
    }

    We have added a form element definition to the form array. Form elements are defined with a minimum of a type to specify what the element is and a title to act as the label. The title uses the t method to ensure that it is translatable.

    Adding a submit button is done by providing an element with the type submit.

  6. To access the form, we will create drupalform.routing.yml in the module's folder. A route entry will be created to instruct Drupal to use DrupalCoreFormFormBuilder to create and display our form:
    drupalform.form:
      path: '/drupal-example-form'
      defaults:
        _title: 'Example form'
        _form: 'DrupaldrupalformFormExampleForm'
      requirements:
        _access: 'TRUE'

    In Drupal, all routes have a name, and this example defines it as drupalform.form. Routes then define a path attribute and override default variables. This route definition has altered the route's title, specified it as a form, and given the fully qualified namespace path to this form's class.

    Routes need to be passed a requirements property with specifications or else the route will be denied access.

  7. Visit the Extend page and enable the Drupal form example module that we created.
  8. Visit /drupal-example-form and the form is now visible, as shown in the following screenshot:
    How to do it…

How it works…

This recipe creates a route to display the form. By passing the _form variable in the defaults section of our route entry, we are telling the route controller how to render our route's content. The fully qualified class name, which includes the namespace, is passed to a method located in the form builder. The route controller will invoke Drupal::formBuilder()->getForm (DrupaldrupalformFormExampleForm) based on the recipe. At the same time, this can be manually called to embed the form elsewhere.

A form builder instance that implements DrupalCoreFormFormBuilderInterface will then process the form by calling buildForm and initiate the rendering process. The buildForm method is expected to return an array of form elements and other API options. This will be sent to the render system to output the form as HTML.

There's more…

Many components make up a form created through Drupal's Form API. We will explore a few of them in depth.

Form element definitions

A form is a collection of form elements, which are types of plugin in Drupal 8. Plugins are small pieces of swappable functionalities in Drupal 8. Plugins and plugin development are covered in Chapter 7, Plug and Play with Plugins. At the time of writing this module, the Drupal.org Form API reference table was severely out of date and did not reflect all of the form element types available.

Here are some of the most common element properties that can be used:

  • weight: This is used to alter the position of a form element in a form. By default, elements will be displayed in the order in which they were added to the form array. Defining a weight allows a developer to control element positions.
  • default_value: This gives a developer the ability to prefill the element with a value. For example, when building configuration forms that have existing data or when editing an entity.
  • placeholder: This is new to Drupal 8. Drupal 8 provides a new HTML5 support, and this attribute will set the placeholder attribute on the HTML input.

The form state

The DrupalCoreFormFormStateInterface object represents the current state of the form and its data. The form state contains user-submitted data for the form along with build state information. Redirection after form submission is handled through the form state as well. You will interact more with the form state during the validation and submission recipes.

The form cache

Drupal utilizes a cache table for forms. This holds the build table, as identified by form build identifiers. This allows Drupal to validate forms during AJAX requests and easily build them when required. It is important to keep the form cache in persistent storage; otherwise, there may be repercussions, such as loss of form data or invalidating forms.

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset