- A+
在关于Drupal 8的前一篇文章模块开发,我们已经研究了创建块类型和表单。我们已经看到,块现在是可重用的,定义块类型所需做的一切都发生在一个类中。类似地,表单生成函数也被分组在一个类下,其特定方法执行类似于我们在Drupal 7中使用的任务。
在本教程中,我将继续我们结束的地方。我将演示我们如何将我们的
1
|
DemoForm
|
转换为用于通过Drupal 8配置系统存储值的表单。在此之后,我们将通过演示来讨论服务容器和依赖项注入。
别忘了你可以退房这个储存库如果您想获得我们在本教程系列中编写的所有代码。
组态形式
当我们第一次定义
1
|
DemoForm
|
,我们扩展了FormBase类的最简单实现。表单接口...但是,Drupal 8还附带了一个ConfigFormBase这提供了一些额外的功能,使得与配置系统的交互非常容易。
我们现在要做的是改变
1
|
DemoForm
|
它将用于存储用户输入的电子邮件地址。我们应该做的第一件事是将扩展类替换为
1
|
ConfigFormBase
|
(当然还有使用)):
1
2 3 |
use Drupal\Core\Form\ConfigFormBase; class DemoForm extends ConfigFormBase { |
在我们继续修改表单中的其他内容之前,让我们先了解一下Drupal 8中的简单配置是如何工作的。简约因为也有更复杂的配置实体,我们今天将不讨论这些实体。目前,由模块(核心或控制)提供的配置存储在YAML文件中。在启用模块时,这些数据会导入到数据库中(以便在使用时获得更好的性能)。通过UI,我们可以更改这个配置,这个配置可以轻松地导出到YAML文件中,以便跨不同的站点进行部署。
模块可以在位于
1
|
config/install
|
模块根目录中的文件夹。命名这个文件的惯例是在它的前面加上模块的名称。所以让我们创建一个叫做
1
|
demo.settings.yml
|
...在这个文件中,我们粘贴以下内容:
1
2 |
demo:
email_address: demo@demo.com |
这是一个嵌套结构(类似于PHP中的关联数组)。在钥匙下面
1
|
demo
|
,我们有另一个键值对。通常,为了访问这些嵌套的值,我们使用一个点(
1
|
.
|
)。在我们的情况下
1
|
demo.email_address
|
.
一旦我们有了这个文件,您需要记住的一件重要的事情是,这个文件只有在模块安装时才会被导入。所以请继续重新安装它。现在,我们可以回到我们的形式,并通过方法,需要一个一个地适应。
这就是为什么
1
|
buildForm()
|
方法应该如下所示:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 |
public function buildForm(array $form, array &$form_state) { $form = parent::buildForm($form, $form_state); $config = $this->config('demo.settings'); $form['email'] = array( return $form; |
首先,相对于
1
|
FormBase
|
,
1
|
ConfigFormBase
|
类也实现此方法,以便向表单数组(提交按钮)添加元素。因此,在添加自己的元素之前,我们可以使用父元素所做的操作。
现在是配置部分。Drupal 8提供了一个配置对象,我们可以使用该对象与配置交互。有些类已经通过依赖项注入获得了它。
1
|
ConfigFormBase
|
就是这样的一个阶级。
如您所见,我们使用的是config()方法检索
1
|
配置
|
对象中填充了
1
|
demo.settings
|
简单的配置。然后,为了
1
|
#default_value
|
在电子邮件表单元素中,我们使用得到()方法
1
|
配置
|
对象检索电子邮件地址的值。
接下来,我们只需要更改提交处理程序,因为
1
|
validateForm()
|
方法现在可以保持不变:
1
2 3 4 5 6 7 8 |
public function submitForm(array &$form, array &$form_state) { $config = $this->config('demo.settings'); return parent::submitForm($form, $form_state); |
在此方法中,我们首先检索
1
|
配置
|
对象,用于我们的配置(就像我们以前做的那样)。然后,我们使用它的SET()方法更改
1
|
email_address
|
到用户提交的值。然后我们使用保存()方法来保存配置。最后,我们扩展父提交处理程序,因为它确实包含一些功能(在本例中,它将Drupal消息设置为屏幕)。
差不多就是这样。您可以清除缓存并试用它。通过提交一个新的电子邮件地址,您将它存储在配置中。模块
1
|
demo.settings.yml
|
文件不会更改,但是您可以去导出
1
|
demo.settings
|
配置并将其导入另一个站点。
服务容器和依赖项注入
接下来我们要看的是服务容器。服务背后的理念是将功能分解为可重用的组件。因此,服务是一个PHP类,它执行一些全局操作,并在服务容器中注册以便被访问。
依赖注入是我们将对象传递给其他对象以确保解耦的方式。每个服务都需要处理一件事,如果它需要另一种服务,则可以将后者注入前者。但我们马上就会知道。
接下来,我们将创建一个非常简单的服务,并将其注册到容器中。它将只有一个真正的方法返回一个简单的值。然后,我们将该服务作为依赖项注入到我们的
1
|
DemoController
|
并利用服务提供的价值。
为了注册服务,我们需要创建一个
1
|
demo.services.yml
|
文件位于我们模块的根目录中,包含以下内容:
1
2 3 |
services:
demo.demo_service: class: Drupal\demo\DemoService |
文件命名约定是
1
|
module_name.services.yml
|
.
第一行创建一个服务数组。第二行定义第一个服务(称为
1
|
demo_service
|
,以模块名作为前缀)。第三行指定将为此服务实例化的类。下面创建
1
|
DemoService.php
|
类文件中的
1
|
src/
|
我们模块的文件夹。这就是我的服务所做的(实际上,它只是为了说明如何使用它):
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?php /** namespace Drupal\demo; class DemoService { protected $demo_value; public function __construct() { public function getDemoValue() { } |
没有必要在这里解释任何事情,因为这是非常基本的。接下来,让我们转到我们的
1
|
DemoController
|
使用这个服务。有两种方法可以做到这一点:通过
1
|
\Drupal
|
类或使用依赖项注入将该类的对象传递给控制器。最好的做法是我们应该用第二种方法来做,所以这就是我们要做的。但有时您需要在全球范围内访问某个服务。为此,你可以这样做:
1
|
$service = \Drupal::service('demo.demo_service');
|
而现在
1
|
$service
|
是类的对象。
1
|
DemoService
|
我们刚刚创造了。但是让我们看看如何将我们的服务注入到
1
|
DemoController
|
类作为依赖项初始化。我将首先解释需要做什么,然后您将看到整个控制器所做的所有更改。
首先,我们需要访问服务容器。对于控制器来说,这真的很容易。我们可以扩展控制器库类,它除了给我们一些其他的帮助之外,还提供了其他的帮助。或者,我们的控制器可以实现容器注入接口这也让我们可以访问容器。但我们会坚持
1
|
控制器库
|
所以我们需要使用那个班。
接下来,我们也需要使用西弗尼2容器界面作为
1
|
create()
|
方法,该方法实例化控制器类的另一个对象,并将所需的服务传递给它。
最后,我们需要一个构造函数来获取传递的服务对象(
1
|
create()
|
返回),并将它们分配给属性以供以后使用。对象返回对象的顺序。
1
|
create()
|
方法需要按照传递给构造函数的顺序来反映。
所以让我们看看我们修改过的
1
|
DemoController
|
:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
<?php /** namespace Drupal\demo\Controller; use Drupal\Core\Controller\ControllerBase; /** protected $demoService; /** /** /** |
如你所见,所有的台阶都在那里。这个
1
|
create()
|
方法创建控制器类的新实例,将从容器检索的服务传递给它。最后,
1
|
DemoService
|
类存储在
1
|
$demoService
|
属性,我们可以使用它调用其
1
|
getDemoValue()
|
方法。然后将此值用于你好留言。清除缓存并试一试。去
1
|
demo/
|
路径和你应该看到
1
|
Hello Upchuk!
|
印在纸上。
我相信您可以看到服务容器的强大功能,因为我们现在可以编写解耦的功能,并将其传递到需要的地方。我没有向您展示如何实现,但是您也可以在注册服务时声明依赖项。这意味着,当Drupal实例化一个服务对象时,它也会对它的所有依赖项进行实例化,并将它们传递给它的构造函数。您可以阅读更多关于如何在这方面进行操作的内容。