drupal模块_构建一个Drupal 8模块-配置和服务容器

drupal模块

Please be aware that due to the development process Drupal 8 has been undergoing at the time of writing, some parts of the code might be outdated. Take a look at this repository in which I try to update the example code and make it work with the latest Drupal 8 release.

请注意,由于在编写本文时Drupal 8正在进行开发过程,因此某些代码部分可能已过时。 看看这个存储库 ,我尝试在其中更新示例代码并使之与最新的Drupal 8版本一起使用。

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.

上一篇有关Drupal 8模块开发的文章中 ,我们研究了创建块类型和形式。 我们已经看到,块现在可以重用,并且定义块类型所需的所有操作如何在一个类中发生。 类似地,表单生成函数也被归类为一类,它们使用特定的方法执行类似于我们在Drupal 7中所使用的任务。

In this tutorial, I will continue where we left off. I will illustrate how we can turn our DemoForm into 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.

在本教程中,我将继续我们停下来的地方。 我将说明如何通过Drupal 8配置系统将DemoForm转换为用于存储值的表单。 接下来,我们将通过示例的方式来讨论服务容器和依赖项注入。

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.

当我们第一次定义了我们的DemoForm ,我们扩展了FormBase类,这是最简单的实现FormInterface 。 但是,Drupal 8还带有ConfigFormBase ,它提供了一些附加功能,使与配置系统的交互变得非常容易。

What we will do now is transform DemoForm into 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):

我们现在要做的是将DemoForm转换为一个将用于存储用户输入的电子邮件地址的DemoForm 。 我们应该做的第一件事是用ConfigFormBase替换扩展类(当然要使用它):

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.

在继续更改表单中的其他内容之前,让我们先了解一下Drupal 8中简单配置的工作方式。我之所以说简单 ,是因为还有一些配置实体更复杂,我们今天将不介绍。 就目前而言,模块(核心或贡献)提供的配置存储在YAML文件中。 启用模块后,此数据将导入数据库(为了在使用时提高性能)。 通过UI,我们可以更改此配置,然后轻松将其导出到YAML文件以在不同站点之间进行部署。

A module can provide default configuration in a YAML file located in the config/install folder 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:

模块可以在模块根目录下config/install文件夹中的YAML文件中提供默认配置。 命名该文件的约定是在其前面加上模块名称。 因此,让我们创建一个名为demo.settings.yml 。 在此文件中,让我们粘贴以下内容:

demo:
  email_address: demo@demo.com

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.

这是一个嵌套结构(类似于PHP中的关联数组)。 在键demo ,我们还有另一个键值对。 通常,为了访问这些嵌套值,我们使用点( . )。 在我们的示例中为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:

这是buildForm()方法现在的样子:

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 ConfigFormBase class 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.

首先,相对于FormBase ,所述ConfigFormBase类实现该方法中,以元素添加到形式阵列(提交按钮)。 因此我们可以在添加自己的元素之前使用父级所做的事情。

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. ConfigFormBase is one such class.

现在是配置部分。 Drupal 8提供了一个Config对象,我们可以使用它与配置进行交互。 一些类已经可以通过依赖注入来使用它。 ConfigFormBase就是这样的一类。

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

如您所见,我们正在使用父类的config()方法来检索用我们的demo.settings简单配置填充的Config对象。 然后,对于电子邮件表单元素的#default_value ,我们使用Config对象的get()方法检索电子邮件地址的值。

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

接下来,我们只需要更改Submit处理程序,因为validateForm()方法现在可以保持不变:

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 for our configuration (like we did before). Then, we use its set() method to change the value of the email_address to 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).

在此方法中,我们首先为我们的配置检索Config对象(就像我们之前所做的那样)。 然后,我们使用其set()方法将email_address的值更改为用户提交的值。 然后,我们使用save()方法保存配置。 最后,我们扩展了父提交处理程序,因为它确实包含一些功能(在这种情况下,它在屏幕上设置了Drupal消息)。

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.yml file won’t change of course, but you can go and export the demo.settings configuration and import it into another site.

就是这样。 您可以清除缓存并尝试一下。 通过提交新的电子邮件地址,您将其存储在配置中。 模块demo.settings.yml文件当然不会改变,但是您可以导出demo.settings配置并将其导入另一个站点。

服务容器和依赖项注入 (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.

接下来要看的是服务容器。 服务背后的想法是将功能拆分为可重用的组件。 因此,服务是一个PHP类,它执行一些全局操作,并已向服务容器注册以便被访问。

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 DemoController and make use of the value provided by the service.

展望未来,我们将创建一个非常简单的服务并将其注册到容器中。 它只有一个返回简单值的实数方法。 然后,我们将该服务作为对DemoController的依赖项进行注入,并利用该服务提供的值。

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

为了注册服务,我们需要在模块的根目录中创建一个demo.services.yml文件,其内容如下:

services:
    demo.demo_service:
        class: Drupal\demo\DemoService

The file naming convention is module_name.services.yml.

文件命名约定为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.php class 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):

第一行创建一系列服务。 第二行定义了第一个服务(称为demo_service ,以模块名称作为前缀)。 第三行指定将为此服务实例化的类。 接下来是在模块的src/文件夹中创建DemoService.php类文件。 这就是我的服务所做的(实际上什么也没做,只是为了说明如何使用它):

<?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 DemoController and use this service. There are two ways we can do this: accessing the container globally through the \Drupal class 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:

因为这是非常基本的,所以在这里无需解释任何内容。 接下来,让我们DemoController我们的DemoController并使用此服务。 我们有两种方法可以做到这一点:通过\Drupal类全局访问容器,或使用依赖注入将此类的对象传递给我们的控制器。 最佳实践表明,我们应该使用第二种方法,所以这就是我们要做的。 但是有时您需要全局访问服务。 为此,您可以执行以下操作:

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

And now $service is an object of the class DemoService we just created. But let’s see how to inject our service in the DemoController class 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.

现在$service是我们刚刚创建的DemoService类的对象。 但是,让我们看看如何将我们的服务作为依赖项注入到DemoController类中。 我将首先解释需要做什么,然后您将看到整个控制器及其所有更改。

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 ControllerBase so we’ll need to use that class.

首先,我们需要访问服务容器。 使用控制器,这真的很容易。 我们可以扩展ControllerBase类,除了其他一些帮助器之外,它还为我们提供了这一类。 另外,我们的控制器可以实现ContainerInjectionInterface ,它也使我们能够访问容器。 但是我们会坚持使用ControllerBase因此我们需要使用该类。

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.

接下来,我们还需要使用 Symfony 2 ContainerInterface作为create()方法的要求,该方法实例化控制器类的另一个对象并将所需的服务传递给该对象。

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.

最后,我们将需要一个构造函数来获取传递的服务对象( create()返回的服务对象)并将它们分配给属性以供以后使用。 create()方法返回对象的顺序必须以它们传递给构造函数的顺序来反映。

So let’s see our revised DemoController:

因此,让我们看看我们修改后的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 DemoService class gets stored in the $demoService property, 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.

如您所见,所有步骤都在那里。 create()方法创建控制器类的新实例,并将从容器中检索到的服务传递给该实例。 最后, DemoService类的实例存储在$demoService属性中,我们可以使用它来调用其getDemoValue()方法。 然后在Hello消息中使用该值。 清除缓存,然后尝试一下。 转到demo/路径,您应该会看到Hello Upchuk! 打印在页面上。

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.

我确定您可以看到服务容器的功能,因为我们现在可以编写解耦的功能并将其传递到需要的地方。 我没有向您展示如何操作,但是您可以在注册服务时声明依赖关系。 这意味着当Drupal实例化服务对象时,它也会对其所有依赖项进行初始化,并将它们传递给其构造函数。 您可以在此文档页面上阅读有关如何执行此操作的更多信息。

结论 (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 ConfigFormBase is 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.

在本文中,我们研究了很多很酷的东西。 我们已经了解了配置系统如何管理简单的配置以及为此提供的形式上可用的内容。 我鼓励您探索ConfigFormBase的实现方式以及扩展后可获得的功能。 此外,您应该在UI中使用站点之间的导入/导出配置进行操作。 从现在开始,这对于部署过程将是一个巨大的改进。

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.

然后,我们研究了服务,它们是什么以及它们如何工作。 维护可从任何地方访问的可重用和分离的功能的绝妙方法。 我确实希望依赖注入的概念不再那么可怕(如果它适合您)。 它基本上等效于将参数传递给过程函数,但是由Symfony及其强大的服务容器在内部使用构造函数方法(或setter)完成。

翻译自: https://www.sitepoint.com/building-drupal-8-module-configuration-management-service-container/

drupal模块

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值