Overview
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
Steps
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 thesystem.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
- 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:
# Extend the system.site configuration system.site: mapping: # The field name is 'description' description: type: string 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:
<?php namespace Drupal\describe_site\Form; // Classes referenced in this class: use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\ConfigFormBase; // This is the form being extended: /** * Configure site information settings for this site. */ class DescribeSiteSiteInformationForm extends SiteInformationForm { /** * {@inheritdoc} */ // Retrieve the system.site configuration $site_config = $this->config('system.site'); // Get the original form from the class we are extending $form = parent::buildForm($form, $form_state); // Add a textarea to the site information section of the form for the description $form['site_information']['site_description'] = [ '#type' => 'textarea', '#title' => $this->t('Site description'), // The default value is the new value we added to our configuration // in step 1 '#default_value' => $site_config->get('description'), '#description' => $this->t('The description of the site'), ]; return $form; } /** * {@inheritdoc} */ $config = $this->config('system.site'); // The site_description is retrieved from the submitted form values // and saved to the 'description' element of the system.site configuration. $new_description = $form_state->getValue('site_description'); $config->set('description', new_description); // Save the configuration $config ->save(); // Pass the remaining values off to the parent form that is being extended, // so that that the parent form can process the values. parent::submitForm($form, $form_state); } }
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:
system.site_information_settings: path: '/admin/config/system/site-information' defaults: _form: 'Drupal\system\Form\SiteInformationForm' _title: 'Basic site settings' requirements: _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:
<?php namespace Drupal\describe_site\Routing; // Classes referenced in this class use Drupal\Core\Routing\RouteSubscriberBase; use Symfony\Component\Routing\RouteCollection; /** * Listens to the dynamic route events. */ class DescribeSiteRouteSubscriber extends RouteSubscriberBase { /** * {@inheritdoc} */ protected function alterRoutes(RouteCollection $collection) { // Change form for the system.site_information_settings route // to Drupal\describe_site\Form\DescribeSiteSiteInformationForm // First, we need to act only on the system.site_information_settings route. if ($route = $collection->get('system.site_information_settings')) { // Next, we need to set the value for _form to the form we have created. $route->setDefault('_form', 'Drupal\describe_site\Form\DescribeSiteSiteInformationForm'); } } }
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
:
services: # This is an arbitrary name, but should be made descriptive describe_site.route_subscriber: # Tell Drupal which class to use class: 'Drupal\describe_site\Routing\DescribeSiteRouteSubscriber' # This next code tells Drupal to use this class when # building routes. 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.
Summary
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.