Drupal 8 - Create New Menu Group under Admin/Config

Submitted by nigel on Saturday 19th May 2018
Admin/config group

In this blog we will create a new menu group on the admin/config page and create a link within the group to our site's configuration form page. This can be extended easily to contain multiple links should your own website need further configuration links leading to configuration forms. 

To achieve our goal we will need to create a new custom module. I will be using a Drupal 8 clean build which has been built with the hussainweb/drupal-composer-init composer template. This template has the Drupal Console and Drush built in for additional convenience. 

Create the Custom Module
I used the Drupal Console to do this interactively, but it's so neat it also generates an output containing the actual command it will use to perform the work! Here's mine:
$ drupal generate:module  \
> --module="myconfig" \ 
> --machine-name="myconfig" \
> --module-path="modules/custom" \
> --description="Sample Configuration Administration Code" \
> --core="8.x" \
> --package="Custom" \
> --learning \
> --uri="http://default" \
> --no-interaction
By changing directory we can see what has been generated.
$ cd /var/www/html/clean/docroot/modules/custom/myconfig
$ tree
.
└── myconfig.info.yml
$ cat  myconfig.info.yml 
name: 'myconfig'
type: module
description: 'Sample Configuration Administration Code'
core: 8.x
package: 'Custom'
All we have is the info.yml file that defines our module which is to be expected. I deliberately opted for the Drupal Console to not generate a .moddule file since we won't need one.
Create the Configuration Form
It's possible to create the configuration form using Drupal Console also, so that is what I have done. This may require a little bit of editing (such as adding #required keys for mandatory fields) but it's a great way to get a basic form up and running easily.
$ drupal generate:form:config \
> --module="myconfig" \
> --class="ConfigurationForm" \
> --form-id="myconfig_admin_settings_form" \
> --inputs='"name":"config_input_1", "type":"text_format", "label":"Config Input 1", "options":"", "description":"Sample Text Input 1", "maxlength":"", "size":"", "default_value":"", "weight":"0", "fieldset":""' \
> --inputs='"name":"config_input_2", "type":"text_format", "label":"Config Input 2", "options":"", "description":"Sample Text Input 2", "maxlength":"", "size":"", "default_value":"", "weight":"0", "fieldset":""' \
> --path="/admin/config/myconfig/configuration" \
> --menu-link-gen \
> --menu-link-title="Sample Configuration Form" \
> --menu-parent="myconfig.group.admin" \
> --menu-link-desc="Configure MySite" \
> --learning --uri="http://default" \
> --no-interaction
The tree structure has now grown:
$ tree
.
├── myconfig.info.yml
├── myconfig.links.menu.yml
├── myconfig.routing.yml
└── src
    └── Form
        └── ConfigurationForm.php
We now have a routing yml file, menu links yml file, and the PHP generated form. Let's have a look at them.
myconfig.links.menu.yml
myconfig.myconfig_admin_settings_form:
  title: 'Sample Configuration Form'
  route_name: myconfig.myconfig_admin_settings_form
  description: 'Configure MySite'
  parent: system.admin_config
  weight: 99
This is the definition for our menu link when it appears on /admin/config. Note the parent that is specified: myconfig.group.admin - this hasn't actually be created yet, but will ultimately be in our custom group of links we add to configure our site.
myconfig.routing.yml
myconfig.myconfig_admin_settings_form:
  path: '/admin/config/myconfig/configuration'
  defaults:
    _form: '\Drupal\myconfig\Form\ConfigurationForm'
    _title: 'ConfigurationForm'
  requirements:
    _permission: 'access administration pages'
  options:
    _admin_route: TRUE
The routing file contains our path - which we will try to navigate to in a minute. Also included in the yml file is the location of the configuration form so the system knows where to look when it comes to write out to the page.
ConfigurationForm.php
<?php
namespace Drupal\myconfig\Form;

use 
Drupal\Core\Form\ConfigFormBase;
use 
Drupal\Core\Form\FormStateInterface;

/**
 * Class ConfigurationForm.
 */
class ConfigurationForm extends ConfigFormBase {

  
/**
   * {@inheritdoc}
   */
  
protected function getEditableConfigNames() {
    return [
      
'myconfig.configuration',
    ];
  }

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

  
/**
   * {@inheritdoc}
   */
  
public function buildForm(array $formFormStateInterface $form_state) {
    
$config $this->config('myconfig.configuration');
    
$form['config_input_1'] = [
      
'#type' => 'text_format',
      
'#title' => $this->t('Config Input 1'),
      
'#description' => $this->t('Sample Text Input 1'),
      
'#default_value' => $config->get('config_input_1'),
    ];
    
$form['config_input_2'] = [
      
'#type' => 'text_format',
      
'#title' => $this->t('Config Input 2'),
      
'#description' => $this->t('Sample Text Input 2'),
      
'#default_value' => $config->get('config_input_2'),
    ];
    return 
parent::buildForm($form$form_state);
  }

  
/**
   * {@inheritdoc}
   */
  
public function validateForm(array &$formFormStateInterface $form_state) {
    
parent::validateForm($form$form_state);
  }

  
/**
   * {@inheritdoc}
   */
  
public function submitForm(array &$formFormStateInterface $form_state) {
    
parent::submitForm($form$form_state);

    
$this->config('myconfig.configuration')
      ->
set('config_input_1'$form_state->getValue('config_input_1'))
      ->
set('config_input_2'$form_state->getValue('config_input_2'))
      ->
save();
  }

}
?>
The generated PHP extends the ConfigFormBase class - which is why we chose to issue the console generate:form:config command. We need a skeleton configuration form, and this has been generated along with sundry methods we can utilise such as form submission and form validation opportunities.

Ok - time to enable our module to see if it actually works!
$ drush en myconfig -y
 [success] Successfully enabled: myconfig
Checking the Configuration Form
Configuration Form

By navigating to admin/config/myconfig/configuration the config form can be seen. As part of the Drupal Console command to generate the form I specified two text_format fields which can be clearly seen. Furthermore, any values added to the fields will be saved as the module's configuration once the form is submitted, and re-presented to the user for editing on subsequent loads of the page. 

However, we aren't done yet. We want our own group of menu items on the admin/config page and we haven't achieved that so far...

Add to the admin/config page
Our objective is to get the configuration link on the top level admin/config page, and to achieve this we need to add entries to our links and our routing files. Let's give it a go.
myconfig.links.menu.yml - add to the bottom
myconfig.group.admin:
  title: 'Mysite'
  route_name: system.admin_config_myconfig
  parent: system.admin_config
  description: 'Myconfig Configuration'
  weight: -999
Here we have defined our new group link for any configuration forms our site will need. We have also added a weight of -999 to ensure it is positioned at the top left of the admin/config page. Note that the parent is specified as system.admin_config - by saying this we are saying we want our entry to be a child of the /admin/config page and not be included in any other groups.
myconfig.routing.yml - add to the bottom
system.admin_config_myconfig:
  path: '/admin/config/mysite'
  defaults:
    _controller: '\Drupal\system\Controller\SystemController::systemAdminMenuBlockPage'
    _title: 'Mysite'
  requirements:
    _permission: 'access administration pages'
This provides the route to our new config page at /admin/config/mysite and says we are piggy-backing on the pre-existing systemAdminMenuBlockPage controller so there is no need to add our own. Furthermore, it will contain the attributes of all the other admin group menus on the site. Now we just need to clear the caches and try it out.
$ drush cr
 [success] Cache rebuild complete.
The Config Menus
Submenu

If we now navigate to admin/config/mysite (above screenshot) and admin/config/myconfig/configuration (screenshot at the page top) we can see that we have succeeded! Success without writing a single line of PHP code - all our PHP was generated by Drupal Console!