The majority of Drupal projects I’ve worked on always needed some type of custom administration form.
In the past, I’ve created a basic form where an editor was able to change a bit of content next to the logo. Instead of hard coding it in the template, the editor would simply change the content from within Drupal.
In Drupal 7, you can build a basic administration form by defining a form and passing it through the system_settings_form()
function. And of course, creating a menu item, so the form is accessible.
In Drupal 8, however, things are slightly different.
In this tutorial, I’ll show you how to create a basic administration form in Drupal 8. I’ll create a form for the Code Snippets module that allows a user to select which element (pre
or code
) should wrap the code displayed by the module.
So let’s jump right in.
Step 1: Create Form Controller
The first obvious step is to create the actual form, and we do this by creating a form controller that extends the ConfigFormBase
class.
The ConfigFormBase
class does a lot of the heavy lifting and most importantly it gives the form controller access to the config object, allowing us to store the selected form values. It also saves us from writing a lot of boilerplate code.
1. So first create a file called SnippetsSettingsForm.php
and place it in <module>/Drupal/snippets/Form/SnippetsSettingsForm.php
.
In the php file we’ll define a class called SnippetsSettingsForm
that extends ConfigFormBase
. Also, add the namespace DrupalsnippetsForm
and DrupalCoreFormConfigFormBase
as the single use statement.
/**
* @file
* Contains DrupalsnippetsFormSnippetsSettingsForm.
*/
namespace DrupalsnippetsForm;
use DrupalCoreFormConfigFormBase;
class SnippetsSettingsForm extends ConfigFormBase { }
2. Next, we need to give the form a unique ID. We do this by creating a getFormId()
method and simply return a unique form ID as a string.
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'snippets_admin_settings';
}
3. Now we need to build the actual form. To do this, we need to add a method called buildForm()
and define the form elements (using Form API) in this method.
Also, make sure you return the parent method or you won’t get a “Save configuration” button.
/**
* {@inheritdoc}
*/
public function buildForm(array $form, array &$form_state) {
$config = $this->config('snippets.settings');
$elements = drupal_map_assoc(array('pre', 'code'));
$form['snippets_wrapping_element'] = array(
'#type' => 'select',
'#title' => $this->t('Select wrapping element'),
'#default_value' => $config->get('element'),
'#options' => $elements,
);
return parent::buildForm($form, $form_state);
}
Currently the form is pretty basic. It’s a single select field that allows you to select pre
or code
and that’s it. Also we’re retrieving the stored value using the new Configuration API in Drupal 8, but more on that later.
4. No form would be complete without a submit handler, so the last thing we need to do is add a submit method that’ll save the select form values.
So we’ll add a method called submitForm()
that will be used to save the form values.
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, array &$form_state) {
$this->config('snippets.settings')
->set('element', $form_state['values']['snippets_wrapping_element'])
->save();
parent::submitForm($form, $form_state);
}
Click here to see the whole file.
Step 2: Create Route and Menu Item
Now that we have created our form controller we need to make it accessible via a menu in Drupal’s administration section. We do this by defining a route in snippets.routing.yml
and a menu item in snippets.module
.
Let’s start with the routing yaml file. (snippets.routing.yml
)
1. Create a file called snippets.routing.yml
and place it in the module root directory, where snippets.info.yml
is located. Now in the yaml file, add the route for the form controller.
snippets.settings: path: '/admin/config/content/snippets' defaults: _form: 'DrupalsnippetsFormSnippetsSettingsForm' requirements: _permission: 'administer site configuration'
The route that we’re using is fairly basic. The path
is set to /admin/config/content/snippets
, _form
points to the form controller that we created previously and _permission
defines which permission is required to access the route.
2. Even though we defined the route in the yaml file, we still need to implement hook_menu()
and create a menu item for the route.
Open up snippets.module
and implement hook_menu()
. Then create a menu item similar to how you would in Drupal 7, and map the menu item to the route using the route_name
attribute on the menu ('route_name' => 'snippets.settings',
).
/**
* Implements hook_menu().
*/
function snippets_menu() {
$items['admin/config/content/snippets'] = array(
'title' => 'Code snippets',
'description' => "Configure Code Snippets module.",
'route_name' => 'snippets.settings',
);
return $items;
}
We only touched the surface of the new routing system in Drupal 8, if you want to learn more then check out the links below:
- hook_menu() has been replaced by new routing system (and others)
- Using Drupal 8’s new route controllers
- Drupal 8 Routing
Click here to see the whole file.
Step 3: Define Configuration Settings
The third and final part of the administration form is storing the actual form values. In Drupal 7, the selected form values are usually stored in the ‘variable’ table and retrieved by using the variable_get('name');
API function.
Drupal 8, on the other hand, comes with a new configuration system that stores site configuration directly on the file system using YAML files and not in the database. This means that site ‘content’ and ‘configuration’ is totally separate and it allows site builders to sync config changes between sites.
1. The first thing we need to do is create a snippets.settings.yml
file that’ll store the default values for the module.
Create a snippets.settings.yml
file and place it in a folder called config
from within the module root directory. The path to the yaml should end up being <module>/config/snippets.settings.yml
.
In this yaml file we’ll add a single key called element
with the default value of pre
. Of course, over time this file will end up with more keys but for now it has one.
element: pre
2. Now the code to retrieve and store the config values is already in the form controller.
In the buildForm()
method we’re using $config = $this->config('snippets.settings');
to load the yaml, then to get the element
value we use $config->get('element')
and that’s it.
Then, in the submitForm()
method, we use the set()
method to simply save the form changes.
$this->config('snippets.settings')
->set('element', $form_state['values']['snippets_wrapping_element'])
->save();
If you want to learn more about the configuration system, be sure to check out the Configuration API in Drupal 8 page on drupal.org.
Click here to see the whole file.
Conclusion
The concept of creating an administration form in Drupal 8 is not that different than in Drupal 7. You still need to define a form, a menu and then store the selected values.
One thing I do like about form controllers is that every form gets their own file. Often in Drupal 7 you have to deal with monolithic include files that have ten different forms and a thousand lines of code.
In Drupal 8, every form gets their own file and this makes it a lot easier to find and navigate through forms.
Thanks, Ivan! This is a great write up. Why do we need to implement hook_menu() in addition to the routing yaml file? I like the look of the yaml routing syntax, but it seems hook_menu should be redundant.
My understanding is that “hook_menu” is still required to display a route as a menu item. There is an issue to fix this; https://drupal.org/node/2047633
It’s important to understand that Drupal 8 is still being developed, so for now you still need “hook_menu”.
You can create a [my_module].link.menu.yml file in your module to define the menu link instead of hook_menu.
[my_module].admin_settings_form:
title: ‘Some title’
parent: system.admin_config_system
description: ‘Some description.’
route_name: [your_defined_route_name]
Thanks.
This tutorial is out of date, needs to be fixed up. 🙂
I’m not up to speed with drupal 8, but how come the path in the hook_menu is different from the path in the yml file, ie ‘admin/structure/snippets’ in the hook_menu() and ‘admin/config/content/snippets’ in snippets.routing.yml file?
Hmm, that’s a bug they should be the same.
I fixed up the tutorial. Thanks for the find.
If you are specifying routes in hook_menu, yours items can now consist of some unique identifier. So you could just define it as $items[‘snippets_configuration’] = array(…);
Thanks for the tip I’ll look into it.
Thanks for the tutorial, though it’s a little bit out-dated by now. The code won’t work in current versions of D8.
Hi Norman,
Yes I’m aware. I’m in the process of updating it.
Cheers,
Ivan
Thanks for covering this topic so thoroughly Ivan; it’s just what I need at the moment! I’m eagerly awaiting your updates so I can implement them in my own custom module.
Cheers!
~ Kevin