Drupal: Extending Core Configuration, Extending Core Forms, and Overriding Core Routes

04/03/2017 - 14:51


When working with Drupal, sometimes we will want to do things like extending the configuration of core or another module, extending a form provided by core or another module, and/or overriding forms on a route. At Jaypan, we recently came across a situation like this, where we needed to add a new field to the site for site description, to be used in structured data (aka RDF) for the site we were building. This needed to be a global field, so the most natural place to set the value for the field was on the basic site settings page, located at /admin/config/system/site-information.

In this tutorial, we will explain the method we used to achieve this functionality, going over the given steps, with an explanation of what is happening along the way.

Requirements and Setup

To follow along with this tutorial, you will have to know how to create a module in Drupal, and you will need to understand how the Form API works, as we will not be covering these topics at all. On top of this, a knowledge of routes, services, and configuration will be beneficial, though not absolutely required.

To get started on this tutorial, you will create a module with a machine name of describe_site


In order to achieve our goal of adding a field for site description to the site settings page, we need to go through the following steps:

  • Extend Drupal core's system.site configuration, creating the system.site.description configuration exists
  • Extend the site settings form, adding a textarea for the site description
  • Tell the routing system to use the extended form, rather than the form provided by core

So, let's get to it!

Step 1: Extend Drupal core's system.site configuration

Drupal core provides the system.site configuration with the following values:

  • uuid
  • name
  • mail
  • slogan
  • etc...

We want to add an additional item to this configuration: description. To do this, we will create the following file: describe_site/config/schema/describe_site.schema.yml, and add the following code:

  1. # Extend the system.site configuration
  2. system.site:
  3.   mapping:
  4. # The field name is 'description'
  5.   description:
  6.   type: string
  7.   label: 'Site Description'

In the above code, we have mapped description, which is a string, to the system.site configuration. When Drupal compiles the configuration, it merges the original system.site configuration with the configuration provided by our module, maintaining the original configuration with the configuration we've added.

Next, we need to create a form that will allow us to set the value of the description.

Step 2: Extend the site settings form

The next step we need to take is to add a textarea to the existing site settings form. To keep our code as slim and maintainable as possible, we will use the form provided by core, by extending it to provide a new textarea into which the description will be added, and setting our description to be saved to configuration when the form is submitted.

The original form provided by core is Drupal\system\Form\SiteInformationForm. We will extend this form, overriding the buildForm() to add our textarea, and overriding the submitForm() method to save the value.

The first thing we need to do is create our form at describe_site/src/Form/DescribeSiteSiteInformationForm.php, where we will add the following class:

  1. <?php
  3. namespace Drupal\describe_site\Form;
  5. // Classes referenced in this class:
  6. use Drupal\Core\Form\FormStateInterface;
  7. use Drupal\Core\Form\ConfigFormBase;
  9. // This is the form being extended:
  10. use Drupal\system\Form\SiteInformationForm;
  12. /**
  13.  * Configure site information settings for this site.
  14.  */
  15. class DescribeSiteSiteInformationForm extends SiteInformationForm {
  17. /**
  18.   * {@inheritdoc}
  19.   */
  20. public function buildForm(array $form, FormStateInterface $form_state) {
  21. // Retrieve the system.site configuration
  22. $site_config = $this->config('system.site');
  24. // Get the original form from the class we are extending
  25. $form = parent::buildForm($form, $form_state);
  27. // Add a textarea to the site information section of the form for the description
  28. $form['site_information']['site_description'] = [
  29. '#type' => 'textarea',
  30. '#title' => $this->t('Site description'),
  31. // The default value is the new value we added to our configuration
  32. // in step 1
  33. '#default_value' => $site_config->get('description'),
  34. '#description' => $this->t('The description of the site'),
  35. ];
  37. return $form;
  38. }
  40. /**
  41.   * {@inheritdoc}
  42.   */
  43. public function submitForm(array &$form, FormStateInterface $form_state) {
  44. $config = $this->config('system.site');
  46. // The site_description is retrieved from the submitted form values
  47. // and saved to the 'description' element of the system.site configuration.
  48. $new_description = $form_state->getValue('site_description');
  49. $config->set('description', new_description);
  51. // Save the configuration
  52. $config ->save();
  54. // Pass the remaining values off to the parent form that is being extended,
  55. // so that that the parent form can process the values.
  56. parent::submitForm($form, $form_state);
  57. }
  58. }

As you can see in the above code, we've added a new textarea named site_description to the form, and we are saving that value to the configuration when it is submitted. So now the last thing we need to do is tell Drupal to use our form, instead of the original form, when accessing the site settings page.

Step 3: Tell the routing system to use the extended form

The first thing to do in determining how to override the route, is to determine exactly what route it is we need to override. In this case, the route we want to override is system.site_information_settings which can be found in the file system.routing.yml. Let's take a look at the route in question:

  1. system.site_information_settings:
  2.   path: '/admin/config/system/site-information'
  3.   defaults:
  4.   _form: 'Drupal\system\Form\SiteInformationForm'
  5.   _title: 'Basic site settings'
  6.   requirements:
  7.   _permission: 'administer site configuration'

As you can see, this route loads the form we've overridden, by setting _form in the defaults for the route.

To override this, we will create a route subscriber class that extends the Drupal\Core\Routing\RouteSubscriberBase, implementing the alterRoutes() method.

To do this, we first need to create the file describe_site/src/Routing/DescribeSiteRouteSuscriber.php. In this file, we will place the following code:

  1. <?php
  3. namespace Drupal\describe_site\Routing;
  5. // Classes referenced in this class
  6. use Drupal\Core\Routing\RouteSubscriberBase;
  7. use Symfony\Component\Routing\RouteCollection;
  9. /**
  10.  * Listens to the dynamic route events.
  11.  */
  12. class DescribeSiteRouteSubscriber extends RouteSubscriberBase {
  13. /**
  14.   * {@inheritdoc}
  15.   */
  16. protected function alterRoutes(RouteCollection $collection) {
  17. // Change form for the system.site_information_settings route
  18. // to Drupal\describe_site\Form\DescribeSiteSiteInformationForm
  19. // First, we need to act only on the system.site_information_settings route.
  20. if ($route = $collection->get('system.site_information_settings')) {
  21. // Next, we need to set the value for _form to the form we have created.
  22. $route->setDefault('_form', 'Drupal\describe_site\Form\DescribeSiteSiteInformationForm');
  23. }
  24. }
  25. }

There is one last step that needs to happen before this route will be used. We need to tell Drupal that we have a class that we have created a route subscriber. To do this, we will create the file describe_site/describe_site.services.yml:

  1. services:
  2. # This is an arbitrary name, but should be made descriptive
  3.   describe_site.route_subscriber:
  4. # Tell Drupal which class to use
  5.   class: 'Drupal\describe_site\Routing\DescribeSiteRouteSubscriber'
  6. # This next code tells Drupal to use this class when # building routes.
  7.   tags: - { name: event_subscriber }

With this .yml file, Drupal now knows to look for our class when it is building routes. When it builds the route, it will set our form to be used instead of the default provided by core.


So there you have it. With the code in this tutorial, we've overridden the form provided by Drupal core, with our own form that extends the form provided by Drupal core, and our form ties in with our configuration value that we've added by extending Drupal core's configuration.

After enabling this module (or clearing the cache if the module was already enabled), when you go to the system settings page, there will be a new textarea into which you can save the site description.