Phần 3 viết Drupal 8 Module: Configuration Management and the Service Container

Phần 3 viết Drupal 8 Module: Configuration Management and the Service Container

In the previous article on Drupal 8 module development, we’ve looked at creating block types and forms. We’ve seen that blocks are now reusable and how everything we need to do for defining block types happens in one single class. Similarly, form generation functions are also grouped under one class with specific methods performing tasks similar to what we are used to in Drupal 7.

>> Phần 1 viết Drupal 8 Module: Routing, Controllers and Menu Links

>> Phần 2 viết Drupal 8 Module: Blocks and Forms

In this tutorial, I will continue where we left off. I will illustrate how we can turn our DemoForminto a form used to store a value through the Drupal 8 configuration system. Following that, we will talk a bit about the service container and dependency injection by way of illustration.

Don’t forget that you can check out this repository if you want to get all the code we write in this tutorial series.

Configuration forms

When we first defined our DemoForm, we extended the FormBase class which is the simplest implementation of the FormInterface. However, Drupal 8 also comes with a ConfigFormBase that provides some additional functionality which makes it very easy to interact with the configuration system.

What we will do now is transform DemoForminto one which will be used to store the email address the user enters. The first thing we should do is replace the extended class with ConfigFormBase(and of course use it):

use Drupal\Core\Form\ConfigFormBase;
class DemoForm extends ConfigFormBase {

Before we move on to changing other things in the form, let’s understand a bit how simple configuration works in Drupal 8. I say simple because there are also configuration entities that are more complex and that we will not cover today. As it stands now, configuration provided by modules (core or contrib) is stored in YAML files. On enabling a module, this data gets imported into the database (for better performance while working with it). Through the UI we can change this configuration which is then easily exportable to YAML files for deployment across different sites.

A module can provide default configuration in a YAML file located in the config/installfolder in the module root directory. The convention for naming this file is to prefix it with the name of the module. So let’s create one called demo.settings.yml. Inside this file, let’s paste the following:

demo:

  email_address: [email protected]

This is a nested structure (like an associative array in PHP). Under the key demo, we have another key|value pair. And usually to access these nested values we use a dot(.). In our case demo.email_address.

Once we have this file in place, an important thing you need to remember is that this file gets imported only when the module is installed. So go ahead and reinstall it. And now we can turn back to our form and go through the methods that need adapting one by one.

This is how the buildForm()method should look like now:

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 of all, as opposed to FormBase, the ConfigFormBaseclass implements this method as well in order to add elements to the form array (a submit button). So we can use what the parent did before adding our own elements.

Now for the configuration part. Drupal 8 provides a Config object that we can use to interact with the configuration. Some classes already have it available through dependency injection. ConfigFormBaseis one such class.

As you can see, we are using the config() method of the parent class to retrieve a Configobject populated with our demo.settingssimple configuration. Then, for the #default_valueof the email form element, we use the get() method of the Configobject to retrieve the value of the email address.

Next, we only need to change the submit handler because the validateForm()method can stay the same for now:

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 Configobject for our configuration (like we did before). Then, we use its set() method to change the value of the email_addressto the value the user submitted. Then we use the save() method to save the configuration. Lastly, we extend the parent submit handler because it does contain some functionality (in this case it sets a Drupal message to the screen).

And that’s pretty much it. You can clear the cache and try it out. By submitting a new email address, you are storing it in the configuration. The module demo.settings.ymlfile won’t change of course, but you can go and export the demo.settingsconfiguration and import it into another site.

The service container and dependency injection

The next thing we are going to look at is the service container. The idea behind services is to split functionality into reusable components. Therefore a service is a PHP class that performs some global operations and that is registered with the service container in order to be accessed.

Dependency injection is the way through which we pass objects to other objects in order to ensure decoupling. Each service needs to deal with one thing and if it needs another service, the latter can be injected into the former. But we’ll see how in a minute.

Going forward, we will create a very simple service and register it with the container. It will only have one real method that returns a simple value. Then, we will inject that service as a dependency to our DemoControllerand make use of the value provided by the service.

In order to register a service, we need to create a demo.services.ymlfile located in the root of our module, with the following contents:

services:

    demo.demo_service:

        class: Drupal\demo\DemoService

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. It follows to create the DemoService.phpclass file in the src/folder of our module. This is what my service does (nothing really, it’s just to illustrate how to use it):

<?php


/**

 * @file

 * Contains Drupal\demo\DemoService.

 */


namespace Drupal\demo;


class DemoService {

  

  protected $demo_value;

  

  public function __construct() {

    $this->demo_value = 'Upchuk';

  }

  

  public function getDemoValue() {

    return $this->demo_value;

  }  

}

No need to explain anything here as it’s very basic. Next, let’s turn to our DemoControllerand use this service. There are two ways we can do this: accessing the container globally through the

\Drupalclass or use dependency injection to pass an object of this class to our controller. Best practice says we should do it the second way, so that’s what we’ll do. But sometimes you will need to access a service globally. For that, you can do something like this:

$service = \Drupal::service('demo.demo_service');

And now $serviceis an object of the class DemoServicewe just created. But let’s see how to inject our service in the DemoControllerclass as a dependency. I will explain first what needs to be done, then you’ll see the entire controller with all the changes made to it.

First, we need access to the service container. With controllers, this is really easy. We can extend the ControllerBase class which gives us that in addition to some other helpers. Alternatively, our Controller can implement the ContainerInjectionInterface that also gives us access to the container. But we’ll stick to ControllerBaseso we’ll need to use that class.

Next, we need to also use the Symfony 2 ContainerInterface as a requirement of the create()method that instantiates another object of our controller class and passes to it the services we want.

Finally, we’ll need a constructor to get the passed service objects (the ones that create()returns) and assign them to properties for later use. The order in which the objects are returned by the create()method needs to be reflected in the order they are passed to the constructor.

So let’s see our revised DemoController:

<?php

/**
 * @file
 * Contains \Drupal\demo\Controller\DemoController.
 */
namespace Drupal\demo\Controller;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
 * DemoController.
 */
class DemoController extends ControllerBase {  
  protected $demoService;  
  /**
   * Class constructor.
   */
  public function __construct($demoService) {
    $this->demoService = $demoService;
  }

  /**
   * {@inheritdoc}
   */

  public static function create(ContainerInterface $container) {

    return new static(

      $container->get('demo.demo_service')
    );

  }
  /**

   * Generates an example page.

   */

  public function demo() {

    return array(

      '#markup' => t('Hello @value!', array('@value' => $this->demoService->getDemoValue())),

    );

  }

}

As you can see, all the steps are there. The create()method creates a new instance of our controller class passing to it our service retrieved from the container. And in the end, an instance of the DemoServiceclass gets stored in the $demoServiceproperty, and we can use it to call its getDemoValue()method. And this value is then used in the Hello message. Clear your cache and give it a try. Go to the demo/path and you should see Hello Upchuk!printed on the page.

I’m sure you can see the power of the service container as we can now write decoupled functionality and pass it where it’s needed. I did not show you how, but you can also declare dependencies when you register services. This means that when Drupal instantiates a service object, it will do so for all its dependencies as well, and pass them to its constructor. You can read more about how to do that on this documentation page.

Conclusion

In this article we’ve looked at a lot of cool stuff. We’ve seen how the configuration system manages simple configuration and what we have available form-wise for this. I do encourage you to explore how the ConfigFormBaseis implemented and what you have available if you extend it. Additionally, you should play around in the UI with importing/exporting configuration between sites. This will be a great improvement for the deployment process from now on.

Then, we looked at services, what they are and how they work. A great way of maintaining reusable and decoupled pieces of functionality accessible from anywhere. And I do hope the concept of dependency injection is no longer so scary (if it was for you). It is basically the equivalent of passing parameters to procedural functions, but done using constructor methods (or setters), under the hood, by Symfony and its great service container.

Bạn thấy bài viết này như thế nào?: 
Average: 3.9 (7 votes)
Ảnh của Khanh Hoang

Khanh Hoang - Kenn

Kenn is a user experience designer and front end developer who enjoys creating beautiful and usable web and mobile experiences.

Bình luận (0)

 

Add Comment

Filtered HTML

  • Các địa chỉ web và email sẽ tự động được chuyển sang dạng liên kết.
  • Các thẻ HTML được chấp nhận: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Tự động ngắt dòng và đoạn văn.

Plain text

  • No HTML tags allowed.
  • Các địa chỉ web và email sẽ tự động được chuyển sang dạng liên kết.
  • Tự động ngắt dòng và đoạn văn.
CAPTCHA
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.

Tìm kiếm bất động sản

 

Advertisement

 

jobsora

Dich vu khu trung tphcm

Dich vu diet chuot tphcm

Dich vu diet con trung

Quảng Cáo Bài Viết

 
Những nết nổi bật của phiên bản Joomla 3.0

Những nét nổi bật của phiên bản Joomla 3.0

Joomla là một trong những CMS dùng để thiết kế website thông dụng và mạnh mẽ nhất hiện nay. Ngày 27/09, phiên bản joomla 3.0.0 đã chính thức được ra mắt. Đây là phiên bản được phát hành theo cơ chế STS “Standard Term Support”.

Tự tạo trang đăng nhập cực đẹp dành cho Facebook

Tự tạo trang đăng nhập cực đẹp dành cho Facebook

Đôi khi bạn cảm thấy nhàm chán với giao diện đăng nhập của Facebook và muốn thay đổi chúng. Hướng dẫn dưới đây sẽ giúp bạn giải quyết vấn đề đó.

Thị trường bắt đầu xuất hiện động thái gom hàng giá rẻ

Thị trường bắt đầu xuất hiện động thái gom hàng giá rẻ

Trong khi phần lớn người dân vẫn trông chờ giá bất động sản sẽ giảm thêm thì trên thị trường bắt đầu xuất hiện động thái gom hàng giá rẻ của một bộ phận giới đầu cơ.

Công ty diệt chuột T&C

 

Diet con trung