Create Custom Module in Drupal 9 (ConfigForm, Controller and cache tags)

Lyubomir Filipov
4 min readDec 19, 2020

--

In order to proceed you should have:
- Drupal installed
- virtual host ready for you
- xampp setup or other web server
- composer installed
- drush installed

#STEP 1: Create required files

We will create a new folder strucutre in web/modules folder.

Folder and file structured required for our custom module

All the required file content is listed below.

my_module.info.yml

name: My Custom Module
description: This is my new module for Drupal 9
package: Custom
type: module
core_version_requirement: 9.x

We add meta data for our module — name, description and any requirements related to our module.

my_module.routing.yml

my_module.show_text:
path: '/terms'
defaults:
_controller: '\Drupal\my_module\Controller\HelloController::showContent'
_title: 'Terms and conditions'
requirements:
_permission: 'access content'

my_module.config_form:
path: '/my_module/config'
defaults:
_form: '\Drupal\my_module\Form\MyConfigForm'
_title: 'My Custom Config Form'
requirements:
_permission: 'administer content'

It is important to notice the _permission option. In the public page we have access content which is general permission, while on the configuration form we have administer content.

Users which does not have access to admin area could not see the configuration form.

We don’t want to allow users which are not logged into the admin area and does not have administer content rights to edit this page.

MyConfigForm.php

<?php

namespace Drupal\my_module\Form;

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

class MyConfigForm extends ConfigFormBase {

public function getFormId()
{
return 'my_module_config_form';
}

protected function getEditableConfigNames()
{
return [
'my_module.settings'
];
}

public function buildForm(array $form, FormStateInterface $form_state)
{
$config = $this->config('my_module.settings');

$form['terms_and_conditions'] = [
#'#type' => 'textarea',
'#type' => 'text_format',
'#title' => 'Shop terms and conditions',
#'#default_value' => $config->get('terms_and_conditions'),
'#default_value' => $config->get('terms_and_conditions')['value'],
'#required' => TRUE,

];

$form['country_of_origin'] = [
'#type' => 'textfield',
'#title' => 'Country of all shops origins',
'#default_value' => $config->get('country_of_origin'),
];

$form['open_shops'] = [
'#type' => 'checkbox',
'#title' => 'Shops availability',
'#default_value' => $config->get('open_shops'),
];

$form['submit'] = [
'#type' => 'submit',
'#value' => 'Submit form',
];

return $form;
}

public function submitForm(array &$form, FormStateInterface $form_state)
{
$config = $this->config('my_module.settings');
$config->set('terms_and_conditions', $form_state->getValue('terms_and_conditions'));
$config->set('country_of_origin', $form_state->getValue('country_of_origin'));
$config->set('open_shops', $form_state->getValue('open_shops'));
$config->save();


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

Note: getEditableConfigNames() method states which settings are we going to alter. Inside buildForm(…) method we are listing all the fields and setting default values based on data coming from the configuration.

terms_and_conditions field is of type text_format which is responsible for the Rich Text Editor that is shown.

HelloController.php

<?php

namespace Drupal\my_module\Controller;

use Drupal\Core\Controller\ControllerBase;

class HelloController extends ControllerBase {

public function showContent() {
return [
'#type' => 'markup',
'#markup' => \Drupal::config('my_module.settings')->get('terms_and_conditions')['value'],
];
}
}

Once we have created all the files it is time for us to enable the module.

#STEP 2: Enable module

First clear cache

drush cr

then navigate to admin area and enable the module

You click on the checkbox on the left and click Install.

Once this is done you should see

#STEP 3: Populate form

This means that we have enabled our module. Next steps will be to navigate to to: http://my-drupal.com/my_module/config (my-drupal.com is my virtual host, at your side it is expected to be different)

You have to be logged in with admin user to see the form.

Populate the form with text and save it. Then navigate to http://my-drupal.com/terms (my-drupal.com is the url of my instance, yours will be different). When accessing the page you should see the terms and conditions that you have added in the previous step.

#STEP 4: Fix issue with cache

If we continue using it like this we will have to clear cache everytime when we save a new configuration. This is handled in Drupal using cache tags. We need to alter HelloController.php and MyConfigForm.php

Changes to HelloController.php

'#cache' => [
'tags' => ['MY_CUSTOM_UNIQUE_TAG'],
],

We are adding a cache tag for our page which is served by the controller.

Changes to MyConfigForm.php

//Invalidate cache
\Drupal\Core\Cache\Cache::invalidateTags(array('MY_CUSTOM_UNIQUE_TAG'));

We are adding a rule in submitForm metod which will invalidate the cache tag MY_CUSTOM_UNIQUE_TAG which is used by our controller. This means that with every new form submission we are invalidating the cache and on next page load Drupal will go and fetch the latest configuration data.

--

--