Overview
When working with Drupal, sometimes it is desirable to extend the configuration provided by core or other modules, extending a form provided by core or other modules, and/or overriding forms on a route. This tutorial will explain how to add a new field to the site for site description, to be used in structured data (aka RDF) on sites. This setting will be global to the site, so the most natural place for this setting is on the basic site settings page, located at /admin/config/system/site-information
. This page represents the system.site
configuration, and this tutorial will explain how to add a new form element to this page, adding the submitted data to the system.site
configuration.
Requirements and Setup
To follow along with this tutorial, you will need to understand how to create a module in Drupal, and how the Form API works, as knowledge of these topics is assumed. 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
The following steps achieve the goal of adding a field for site description to the site settings page:
- Extend Drupal core's
system.site
configuration, creating thesystem.site.description
configuration - 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...
The goal is to add an additional item to this configuration: description
. First, create the following file: describe_site.module
, and add the following code:
/** * Implements hook_config_schema_info_alter(). */ function describe_site_config_schema_info_alter(&$definitions) { $definitions['system.site']['mapping']['description'] = [ 'type' => 'string', 'label' => 'Site Description', ]; }
This code maps description
, a string, to the system.site
configuration. When Drupal compiles the configuration, it merges the original system.site
configuration with the configuration alterations provided in the above code, maintaining the original configuration with the extended configuration.
The next step is to create a form to set the value of the description.
Step 2: Extend the site settings form
The next step is to add a textarea to the existing site settings form. To keep the code as slim and maintainable as possible, the form class provided by core for the basic settings page is extended, and the new textarea into which the description will be added. This discription will be saved to configuration when the form is submitted.
The original form provided by core is Drupal\system\Form\SiteInformationForm. The buildForm()
method is overridden to add the textarea, and the submitForm()
method is overridden to save the submitted description value.
The first thing to do is create the form at describe_site/src/Form/DescribeSiteSiteInformationForm.php
:
<?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 that was added to the 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, a new textarea named site_description
has been added to the form, and the submitted value is saved to configuration when the form is submitted. The last thing to do is tell Drupal to use this overridden 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 needs to be overriden. In this case, the route is system.site_information_settings
which can be found in the file system.routing.yml. 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'
This route loads the original form by setting the _form
key in the defaults on the route.
Overriding this requires creating a route subscriber class that extends the Drupal\Core\Routing\RouteSubscriberBase, and implementing the alterRoutes() method.
The following code goes in to describe_site/src/Routing/DescribeSiteRouteSuscriber.php
:
<?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 // Act only on the system.site_information_settings route. if ($route = $collection->get('system.site_information_settings')) { // Next, set the value for _form to the form override. $route->setDefault('_form', 'Drupal\describe_site\Form\DescribeSiteSiteInformationForm'); } } }
The last step that needs to happen before this route will be used is to register the route subscriber. This happens in 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 the new class when accessing the route for the site settings page. When it builds the route, the overridden form is used instead of the one provided by core.
Summary
So there you have it. With the code in this tutorial, the form provided by Drupal core was overridden with a form that captures a description, and saves it to the site configuration.
After enabling this module (or clearing the cache if the module was already enabled), when visiting the system settings page, there will be a new textarea into which the site description can be saved and migrated.
Complete Code
describe_site.module
:
/** * Implements hook_config_schema_info_alter(). */ function describe_site_config_schema_info_alter(&$definitions) { $definitions['system.site']['mapping']['description'] = [ 'type' => 'string', 'label' => 'Site Description', ]; }
describe_site/src/Form/DescribeSiteSiteInformationForm.php
:
<?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 that was added to the 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); } }
describe_site/src/Routing/DescribeSiteRouteSuscriber.php
:
<?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 // Act only on the system.site_information_settings route. if ($route = $collection->get('system.site_information_settings')) { // Next, set the value for _form to the form override. $route->setDefault('_form', 'Drupal\describe_site\Form\DescribeSiteSiteInformationForm'); } } }
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 }