How to Create an Administration Form in Drupal 8

The majority of Drupal projects I’ve worked on always needed some type of custom administration form.

In the past, I’ve created a basic form where an editor was able to change a bit of content next to the logo. Instead of hard coding it in the template, the editor would simply change the content from within Drupal.

In Drupal 7, you can build a basic administration form by defining a form and passing it through the system_settings_form() function. And of course, creating a menu item, so the form is accessible.

In Drupal 8, however, things are slightly different.

In this tutorial, I’ll show you how to create a basic administration form in Drupal 8. I’ll create a form for the Code Snippets module that allows a user to select which element (pre or code) should wrap the code displayed by the module.

Fig 1.0

So let’s jump right in.

Step 1: Create Form Controller

The first obvious step is to create the actual form, and we do this by creating a form controller that extends the ConfigFormBase class.

The ConfigFormBase class does a lot of the heavy lifting and most importantly it gives the form controller access to the config object, allowing us to store the selected form values. It also saves us from writing a lot of boilerplate code.

1. So first create a file called SnippetsSettingsForm.php and place it in <module>/Drupal/snippets/Form/SnippetsSettingsForm.php.

In the php file we’ll define a class called SnippetsSettingsForm that extends ConfigFormBase. Also, add the namespace DrupalsnippetsForm and DrupalCoreFormConfigFormBase as the single use statement.

/**
 * @file
 * Contains DrupalsnippetsFormSnippetsSettingsForm.
 */

namespace DrupalsnippetsForm;

use DrupalCoreFormConfigFormBase;

class SnippetsSettingsForm extends ConfigFormBase { }

2. Next, we need to give the form a unique ID. We do this by creating a getFormId() method and simply return a unique form ID as a string.

/**
 * {@inheritdoc}
 */
public function getFormId() {
  return 'snippets_admin_settings';
}

3. Now we need to build the actual form. To do this, we need to add a method called buildForm() and define the form elements (using Form API) in this method.

Also, make sure you return the parent method or you won’t get a “Save configuration” button.

/**
 * {@inheritdoc}
 */
public function buildForm(array $form, array &$form_state) {
  $config = $this->config('snippets.settings');

  $elements = drupal_map_assoc(array('pre', 'code'));

  $form['snippets_wrapping_element'] = array(
    '#type' => 'select',
    '#title' => $this->t('Select wrapping element'),
    '#default_value' => $config->get('element'),
    '#options' => $elements,
  );

  return parent::buildForm($form, $form_state);
}

Currently the form is pretty basic. It’s a single select field that allows you to select pre or code and that’s it. Also we’re retrieving the stored value using the new Configuration API in Drupal 8, but more on that later.

4. No form would be complete without a submit handler, so the last thing we need to do is add a submit method that’ll save the select form values.

So we’ll add a method called submitForm() that will be used to save the form values.

/**
 * {@inheritdoc}
 */
public function submitForm(array &$form, array &$form_state) {
  $this->config('snippets.settings')
      ->set('element', $form_state['values']['snippets_wrapping_element'])
      ->save();

  parent::submitForm($form, $form_state);
}

Click here to see the whole file.

Step 2: Create Route and Menu Item

Now that we have created our form controller we need to make it accessible via a menu in Drupal’s administration section. We do this by defining a route in snippets.routing.yml and a menu item in snippets.module.

Let’s start with the routing yaml file. (snippets.routing.yml)

1. Create a file called snippets.routing.yml and place it in the module root directory, where snippets.info.yml is located. Now in the yaml file, add the route for the form controller.

snippets.settings:
  path: '/admin/config/content/snippets'
  defaults:
    _form: 'DrupalsnippetsFormSnippetsSettingsForm'
  requirements:
    _permission: 'administer site configuration'

The route that we’re using is fairly basic. The path is set to /admin/config/content/snippets, _form points to the form controller that we created previously and _permission defines which permission is required to access the route.

2. Even though we defined the route in the yaml file, we still need to implement hook_menu() and create a menu item for the route.

Open up snippets.module and implement hook_menu(). Then create a menu item similar to how you would in Drupal 7, and map the menu item to the route using the route_name attribute on the menu ('route_name' => 'snippets.settings',).

/**
 * Implements hook_menu().
 */
function snippets_menu() {
  $items['admin/config/content/snippets'] = array(
    'title' => 'Code snippets',
    'description' => "Configure Code Snippets module.",
    'route_name' => 'snippets.settings',
  );

  return $items;
}

We only touched the surface of the new routing system in Drupal 8, if you want to learn more then check out the links below:

Click here to see the whole file.

Step 3: Define Configuration Settings

The third and final part of the administration form is storing the actual form values. In Drupal 7, the selected form values are usually stored in the ‘variable’ table and retrieved by using the variable_get('name'); API function.

Drupal 8, on the other hand, comes with a new configuration system that stores site configuration directly on the file system using YAML files and not in the database. This means that site ‘content’ and ‘configuration’ is totally separate and it allows site builders to sync config changes between sites.

1. The first thing we need to do is create a snippets.settings.yml file that’ll store the default values for the module.

Create a snippets.settings.yml file and place it in a folder called config from within the module root directory. The path to the yaml should end up being <module>/config/snippets.settings.yml.

In this yaml file we’ll add a single key called element with the default value of pre. Of course, over time this file will end up with more keys but for now it has one.

element: pre

2. Now the code to retrieve and store the config values is already in the form controller.

In the buildForm() method we’re using $config = $this->config('snippets.settings'); to load the yaml, then to get the element value we use $config->get('element') and that’s it.

Then, in the submitForm() method, we use the set() method to simply save the form changes.

  $this->config('snippets.settings')
      ->set('element', $form_state['values']['snippets_wrapping_element'])
      ->save();

If you want to learn more about the configuration system, be sure to check out the Configuration API in Drupal 8 page on drupal.org.

Click here to see the whole file.

Conclusion

The concept of creating an administration form in Drupal 8 is not that different than in Drupal 7. You still need to define a form, a menu and then store the selected values.

One thing I do like about form controllers is that every form gets their own file. Often in Drupal 7 you have to deal with monolithic include files that have ten different forms and a thousand lines of code.

In Drupal 8, every form gets their own file and this makes it a lot easier to find and navigate through forms.

About The Author

11 thoughts on “How to Create an Administration Form in Drupal 8”

  1. Thanks, Ivan! This is a great write up. Why do we need to implement hook_menu() in addition to the routing yaml file? I like the look of the yaml routing syntax, but it seems hook_menu should be redundant.

      1. You can create a [my_module].link.menu.yml file in your module to define the menu link instead of hook_menu.

        [my_module].admin_settings_form:
        title: ‘Some title’
        parent: system.admin_config_system
        description: ‘Some description.’
        route_name: [your_defined_route_name]

  2. I’m not up to speed with drupal 8, but how come the path in the hook_menu is different from the path in the yml file, ie ‘admin/structure/snippets’ in the hook_menu() and ‘admin/config/content/snippets’ in snippets.routing.yml file?

    1. If you are specifying routes in hook_menu, yours items can now consist of some unique identifier. So you could just define it as $items[‘snippets_configuration’] = array(…);

      1. Thanks for covering this topic so thoroughly Ivan; it’s just what I need at the moment! I’m eagerly awaiting your updates so I can implement them in my own custom module.
        Cheers!
        ~ Kevin

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top