Core points
ConfigFormBase
class provides additional functionality to interact with the configuration system, allowing tools to convert forms to stored values. This can be done by replacing the extension class with ConfigFormBase
and making the necessary changes in the form. The configuration in Drupal 8 is stored in a YAML file and can be changed through the UI for deployment across different sites. demo.services.yml
file in the root directory of the module. ControllerBase
class or implementing the ContainerInjectionInterface
. You can also use the Drupal
class to access the service globally. Note that some code parts may be outdated because Drupal 8 is in development at the time of writing. Please check out this repository, I tried to update the sample code and make it compatible with the latest Drupal 8 version.
In previous articles on Drupal 8 module development, we looked at creating block types and forms. We have seen that blocks are now reusable, and that everything needed to define block types is done in one class. Similarly, form generation functions are also grouped in a class where the tasks performed by a particular method are similar to those we are used to in Drupal 7.
In this tutorial, I will continue from where we ended last time. I'll explain how to convert our DemoForm
into a form used to store values through Drupal 8 configuration system. After that, we will illustrate service containers and dependency injection with examples.
Don't forget, if you want to get all the code written in this tutorial series, you can check out this repository.
Configuration form
When we first defined DemoForm
, we extended the FormBase
class, which is the easiest implementation of FormInterface
. However, Drupal 8 also comes with a ConfigFormBase
, which provides some additional features that make interacting with the configuration system very easy.
What we have to do now is convert DemoForm
into a form that stores the email address entered by the user. The first thing we should do is replace the extension class with ConfigFormBase
(and of course use
it):
use Drupal\Core\Form\ConfigFormBase; class DemoForm extends ConfigFormBase {
Before we continue to change the rest of the form, let's take a look at how simple configurations in Drupal 8 work. I said "simple" because there are more complex configuration entities, which we will not introduce today. For now, the configuration provided by the module (core or contrib) is stored in the YAML file. When the module is enabled, this data is imported into the database (to improve performance when used). With the UI, we can change this configuration and then easily export it to a YAML file for deployment across different sites.
The module can provide a default configuration in the YAML file in the config/install
folder in the module root directory. The naming convention for this file is to prefix the module's name. So let's create a file called demo.settings.yml
. In this file, let's paste the following:
demo: email_address: demo@demo.com
This is a nested structure (like an associative array in PHP). Under the demo
key, we have another key-value pair. Typically, to access these nested values, we use dots (.). In our case it is demo.email_address
.
Once we have this file, one important thing you need to remember is that this file will be imported only when the module is installed. So, keep reinstalling it. Now we can go back to our form and see one by one the methods that need to be adjusted.
This is what buildForm()
Method now looks like:
public function buildForm(array $form, array &$form_state) { $form = parent::buildForm($form, $form_state); $config = $this->config('demo.settings'); $form['email'] = array( '#type' => 'email', '#title' => $this->t('Your .com email address.'), '#default_value' => $config->get('demo.email_address') ); return $form; }
First, contrary to FormBase
, the ConfigFormBase
class also implements this method in order to add elements to the form array (submit button). So, before adding our own elements, we can use what we did before the parent class.
Now for the configuration section. Drupal 8 provides a Config
object that we can use to interact with the configuration. Some classes have obtained it through dependency injection. ConfigFormBase
This is such a class.
As you can see, we are using the config()
method of the parent class to retrieve a Config
object that has been populated with our demo.settings
simple configuration. Then, for the #default_value
of the email form element, we use the Config
method of the get()
object to retrieve the value of the email address.
Next we just need to change the commit handler, because the validateForm()
method can now remain the same:
public function submitForm(array &$form, array &$form_state) { $config = $this->config('demo.settings'); $config->set('demo.email_address', $form_state['values']['email']); $config->save(); return parent::submitForm($form, $form_state); }
In this method, we first retrieve the Config
object we configured (like we did before). We then use its set()
method to change the value of email_address
to the user-submitted value. Then we use the save()
method to save the configuration. Finally, we extend the parent commit handler because it does include some functionality (in this case it sets the Drupal message to the screen).
That's it. You can clear the cache and give it a try. By submitting a new email address, you store it in the configuration. The demo.settings.yml
file will certainly not change, but you can export the demo.settings
configuration and import it to another site.
Service container and dependency injection
The next thing we want to look at is the service container. The philosophy behind the service is to break down functionality into reusable components. Therefore, a service is a PHP class that performs some global operations and registers to the service container for access.
Dependency injection is the way we pass objects to ensure decoupling. Each service needs to handle one thing, and if it needs another service, the latter can be injected into the former. But we will see how to do it right away.
Next, we will create a very simple service and register it into the container. It only has one real way to return simple values. We then inject the service as a dependency into our DemoController
and use the value provided by the service.
In order to register a service, we need to create a demo.services.yml
file located in the root directory of the module, with the following content:
use Drupal\Core\Form\ConfigFormBase; class DemoForm extends ConfigFormBase {
The file naming convention is module_name.services.yml
.
The first line creates an array of services. The second line defines the first service (called demo_service
, prefixed by the module name). The third line specifies the class that will be instantiated for this service. Next is to create the src/
class file in the DemoService.php
folder of our module. This is what my service does (actually nothing, just to illustrate how to use it):
demo: email_address: demo@demo.com
Nothing needs to be explained here, as it is very basic. Next, let's turn to our DemoController
and use this service. We can do this in two ways: access the container globally through the Drupal
class or pass an object of this class to our controller using dependency injection. Best practice suggests we should take the second approach, so that's what we're going to do. But sometimes you need global access to the service. To do this, you can do the following:
public function buildForm(array $form, array &$form_state) { $form = parent::buildForm($form, $form_state); $config = $this->config('demo.settings'); $form['email'] = array( '#type' => 'email', '#title' => $this->t('Your .com email address.'), '#default_value' => $config->get('demo.email_address') ); return $form; }
Now $service
is the object of the DemoService
class we just created. But let's see how to inject our service as a dependency in the DemoController
class. I'll explain what needs to be done first, and then you'll see a complete controller with all the changes made to it.
First, we need to access the service container. This is very easy for the controller. We can extend the ControllerBase
class, which provides us with this in addition to some other helper programs. Alternatively, our controller can implement ContainerInjectionInterface
, which also allows us to access the container. But we will stick to ControllerBase
, so we need use
the class.
Next, we need use
Symfony 2 ContainerInterface
as a requirement of the create()
method, which instantiates another object of the controller and passes the service we want to it.
Finally, we will need a constructor to get the passed service objects (the returned object of create()
) and assign them to properties for later use. create()
The order in which the method returns objects needs to reflect the order in which they are passed to the constructor.
So let's look at our modified DemoController
:
use Drupal\Core\Form\ConfigFormBase; class DemoForm extends ConfigFormBase {
As you can see, all the steps are here. The create()
method creates a new instance of our controller class and passes the service retrieved from the container to it. Finally, an instance of the DemoService
class is stored in the $demoService
property, which we can use to call its getDemoValue()
method. This value will then be used in the "Hello" message. Clear the cache and give it a try. Go to the demo/
path and you should see the "Hello Upchuk!" printed on the page.
I believe you can see the power of the service container, as we can now write decoupled functions and pass them where needed. I'm not showing you how to do it, but you can declare dependencies when registering for a service. This means that when Drupal instantiates a service object, it will do so for all its dependencies, and pass them to its constructor. You can read more about how to do this on this document page.
Conclusion
In this article, we have looked at a lot of cool stuff. We have seen how the configuration system manages simple configurations and what "form" features are provided for this. I encourage you to explore how ConfigFormBase
is implemented and what features are available when extending it. Additionally, you should practice using import/export configurations between sites in the UI. From now on, this will be a big improvement to the deployment process.
We then looked at the services, what they are, and how they work. A great way to maintain reusable and decoupled functional blocks that are accessible from anywhere. I hope the concept of dependency injection is no longer as scary (if it is for you). It is basically the same as passing parameters to procedural functions, but is done behind the scenes by Symfony and its powerful service containers using constructor methods (or setters).
Frequently Asked Questions about Building Drupal 8 Modules: Configuration Management and Service Containers
The service container in Drupal 8 is a key component that manages the creation of services, objects that are used globally in Drupal applications. It ensures that each service is instantiated only once, saving memory and improving performance. The service container also handles dependency injection, a design pattern that allows one object to provide dependencies for another object. This makes the code more modular, easier to test and promotes better organization.
To define a new service in Drupal 8, you need to create a services.yml
file in the root directory of the module. This file should contain the name, class, and parameters of the service. This class should be the fully qualified name of the class that implements the service, and the parameters should be any service or parameters on which the service depends.
Configuration Management in Drupal 8 is a system that allows you to manage site configuration data in a consistent manner. It allows you to import, export, and synchronize configuration data, which is useful when moving configuration changes from the development environment to the production site. It also provides a way to track and manage site configuration changes over time.
To export configuration data in Drupal 8, you can use the Configuration Management interface in the Admin Panel or use the Drush command. The exported data will be in YAML format and can be read and edited easily. To import configuration data, you can upload the exported YAML file through the configuration management interface or use the Drush command. Remember to back up your site before importing configuration data to prevent any potential data loss.
Dependency injection is a design pattern that allows one object to provide dependencies for another object. In Drupal 8, it is used to make services and controllers more modular and easier to test. Instead of creating dependencies inside an object, they are passed (injected) through a constructor or setter method. This makes the code easier to test, more flexible and less coupled.
To inject dependencies into services in Drupal 8, you need to define them in the definition of services in the services.yml
file. Dependencies should be listed under the arguments
key. When a service is created, the service container will automatically pass these dependencies to the service's constructor.
In Drupal 8, a service is an object that performs global tasks in an application, while a plug-in is an object that performs specific tasks in a pluggable manner. Services are defined in the services.yml
file and managed by the service container, while plug-ins are discovered and instantiated by the plug-in manager.
To overwrite a service in Drupal 8, you need to define a service with the same name as the service you want to overwrite in the services.yml
file of the module. Your new service should extend the original service's class and override the method you want to change.
The configuration management system in Drupal 8 provides a way to track site configuration changes through a configuration snapshot system. This system takes a snapshot of the site's active configuration whenever you import or synchronize configuration data. You can then compare these snapshots to see what changes have been made.
services.yml
What does a file do in Drupal 8? The services.yml
file in Drupal 8 is the place where module services are defined. Each service is defined using a unique name, a fully qualified name of the class that implements the service, and any service or parameters the service depends on. services.yml
Files are read by the service container, and the service container manages the creation and injection of the service.
The above is the detailed content of Drupal 8 Modules - Configuration Management and the Service Container. For more information, please follow other related articles on the PHP Chinese website!