drupal8文档

tech2022-09-18  93

drupal8文档

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版本一起使用。

With the incorporation of many Symfony components into Drupal in its 8th version, we are seeing a shift away from many Drupalisms towards more modern PHP architectural decisions. For example, the both loved and hated hook system is getting slowly replaced. Plugins and annotations are taking away much of the need for info hooks and the Symfony Event Dispatcher component is replacing some of the invoked hooks. Although they remain strong in Drupal 8, it’s very possible that with Drupal 9 (or maybe 10) hooks will be completely removed.

随着在第8版的Drupal中集成了许多Symfony组件 ,我们看到了从许多Drupalism转向更现代PHP体系结构决策的转变。 例如,爱与恨的钩子系统正在逐渐被替换。 插件和注释消除了对info钩子的大部分需求,Symfony Event Dispatcher组件取代了一些调用的钩子。 尽管它们在Drupal 8中仍然很强大,但很有可能在Drupal 9(或10)中完全删除了钩子。

In this article we are going to primarily look at how the Symfony Event Dispatcher component works in Drupal. Additionally, we will see also how to invoke and then implement a hook in Drupal 8 to achieve similar goals as with the former.

在本文中,我们将主要研究Symfony Event Dispatcher组件在Drupal中的工作方式。 另外,我们还将看到如何在Drupal 8中调用然后实现一个钩子,以实现与前者类似的目标。

To follow along or to get quickly started, you can find all the code we work with here in this repository. You can just install the module and you are good to go. The version of Drupal 8 used is the first BETA release so it’s preferable to use that one to ensure compatibility. Alpha 15 should also work just fine. Let’s dive in.

要继续学习或快速入门,您可以在此存储库中找到我们使用的所有代码。 您只需要安装模块就可以了。 使用的Drupal 8版本是第一个BETA版本,因此最好使用该版本以确保兼容性。 Alpha 15也应该可以正常工作。 让我们潜入。

什么是事件调度程序组件? (What is the Event Dispatcher component?)

A very good definition of the Event Dispatcher component can be found on the Symfony website:

可以在Symfony网站上找到Event Dispatcher组件的很好定义:

The EventDispatcher component provides tools that allow your application components to communicate with each other by dispatching events and listening to them.

EventDispatcher组件提供了一些工具,使您的应用程序组件可以通过分派事件并侦听事件来相互通信。

I recommend reading up on that documentation to better understand the principles behind the event dispatcher. You will get a good introduction to how it works in Symfony so we will not cover that here. Rather, we will see an example of how you can use it in Drupal 8.

我建议阅读该文档,以更好地了解事件分派器背后的原理。 您将很好地介绍它在Symfony中的工作原理,因此我们不在此介绍。 相反,我们将看到一个示例,说明如何在Drupal 8中使用它。

Drupal 8和事件调度程序 (Drupal 8 and the Event Dispatcher)

For the better part of this article, we will focus on demonstrating the use of the Event Dispatcher in Drupal 8. To this end, we will create a simple demo module (event_dispatcher_demo) that has a configuration form which saves two values as configuration. Upon saving this form, we will dispatch an event that contains the config object and which will allow other parts of the application to intercept and modify it before being saved. Finally, we will do just that by demonstrating how to subscribe (or listen) to these events.

对于本文的更好部分,我们将集中于演示在Drupal 8中使用Event Dispatcher。为此,我们将创建一个简单的演示模块( event_dispatcher_demo ),该演示模块具有一个配置形式,该配置形式将两个值保存为配置。 保存此表单后,我们将分派一个包含config对象的事件,该事件将允许应用程序的其他部分在保存之前对其进行拦截和修改。 最后,我们将通过演示如何订阅(或监听)这些事件来做到这一点。

In Drupal 7, this type of modularity is only achieved with hooks. Hooks are being invoked and modules have the option to implement them and contribute with their own data. At the end of this article, we will see how to do that as well in Drupal 8. But first, let’s get on with our demo module.

在Drupal 7中,这种类型的模块化只能通过钩子实现。 挂钩被调用,模块可以选择实现它们并使用自己的数据进行贡献。 在本文的最后,我们将在Drupal 8中看到如何做。但是,首先,让我们继续演示模块。

If you don’t know the basics of Drupal 8 module development, I recommend checking out my previous articles in this series.

如果您不了解Drupal 8模块开发的基础知识,建议您阅读本系列以前的文章。

表格 (The form)

The first thing we need is a simple config form with two fields. In a file called DemoForm.php located in the src/Form folder, we have the following:

我们需要的第一件事是具有两个字段的简单配置表单。 在src/Form文件夹中的DemoForm.php文件中,我们具有以下内容:

<?php /** * @file * Contains Drupal\event_dispatcher_demo\Form\DemoForm. */ namespace Drupal\event_dispatcher_demo\Form; use Drupal\Core\Form\ConfigFormBase; use Drupal\Core\Form\FormStateInterface; class DemoForm extends ConfigFormBase { /** * {@inheritdoc} */ public function getFormID() { return 'demo_form'; } /** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { $config = $this->config('event_dispatcher_demo.demo_form_config'); $form['my_name'] = [ '#type' => 'textfield', '#title' => $this->t('My name'), '#default_value' => $config->get('my_name'), ]; $form['my_website'] = [ '#type' => 'textfield', '#title' => $this->t('My website'), '#default_value' => $config->get('my_website'), ]; return parent::buildForm($form, $form_state); } /** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { parent::submitForm($form, $form_state); $config = $this->config('event_dispatcher_demo.demo_form_config'); $config->set('my_name', $form_state->getValue('my_name')) ->set('my_website', $form_state->getValue('my_website')); $config->save(); } }

Let’s also create a route for it (in the event_dispatcher_demo.routing.yml file) so we can access the form in the browser:

我们还为它创建一条路由(在event_dispatcher_demo.routing.yml文件中),以便我们可以在浏览器中访问该表单:

event_dispatcher_demo.demo_form: path: 'demo-form' defaults: _form: '\Drupal\event_dispatcher_demo\Form\DemoForm' _title: 'Demo form' requirements: _permission: 'access administration pages'

So now if you point your browser to example.com/demo-form, you should see the form. Submitting it will create and persist a configuration object called event_dispatcher_demo.demo_form_config that contains two fields: my_name and my_website .

因此,现在如果将浏览器指向example.com/demo-form ,您应该会看到该表单。 提交它会创建并保留一个名为event_dispatcher_demo.demo_form_config的配置对象,该对象包含两个字段: my_name和my_website 。

事件调度员 (The event dispatcher)

Now it’s time to work on the form submit handler (the formSubmit() method) and dispatch an event when the form is saved. This is what the new method will look like:

现在是时候处理表单提交处理程序( formSubmit()方法)并在保存表单时调度事件了。 这是新方法的外观:

public function submitForm(array &$form, FormStateInterface $form_state) { parent::submitForm($form, $form_state); $config = $this->config('event_dispatcher_demo.demo_form_config'); $config->set('my_name', $form_state->getValue('my_name')) ->set('my_website', $form_state->getValue('my_website')); $dispatcher = \Drupal::service('event_dispatcher'); $e = new DemoEvent($config); $event = $dispatcher->dispatch('demo_form.save', $e); $newData = $event->getConfig()->get(); $config->merge($newData); $config->save(); }

So what happens here? After we take the submitted values and add them to the config object like before, we retrieve the event dispatcher object from the service container:

那么这里发生了什么? 在像以前一样将提交的值添加到配置对象后,我们从服务容器中检索事件分配器对象:

$dispatcher = \Drupal::service('event_dispatcher');

Please keep in mind that it’s recommended you inject this service into your class, but for brevity, we will retrieve it statically. You can read this article about dependency injection and the service container for more information.

请记住,建议您将此服务注入您的班级,但是为了简洁起见,我们将静态检索它。 您可以阅读有关依赖项注入和服务容器的本文,以获取更多信息。

Then we create a new DemoEvent object and pass it the $config through its constructor (we have not yet created the DemoEvent class, we will do that in a minute). Next, we use the dispatcher to dispatch an event of our type and assign this action the identifier demo_form.save. This will be used when subscribing to events (we’ll see this later). The dispatch() method returns the event object with modifications made to it so we can retrieve the config values that may or may not have been altered elsewhere and merge them into our original configuration. Finally, we save this object like we did initially.

然后,我们创建一个新的DemoEvent对象,并通过其构造函数将$config传递给它(我们尚未创建DemoEvent类,我们将在一分钟内完成此操作)。 接下来,我们使用调度程序来调度我们类型的事件,并将此动作分配给标识符demo_form.save 。 订阅事件时将使用它(我们将在后面看到)。 dispatch()方法返回事件对象并对其进行了修改,因此我们可以检索在其他地方可能已更改或未更改的配置值,并将它们合并到我们的原始配置中。 最后,我们像最初那样保存该对象。

Before moving onto the event subscription part of our application, let’s create the DemoEvent class we just instantiated above. In a file called DemoEvent.php located in the src/ folder of our module, we have the following:

在进入应用程序的事件订阅部分之前,让我们创建上面刚刚实例化的DemoEvent类。 在我们模块的src/文件夹中的一个名为DemoEvent.php文件中,我们具有以下内容:

<?php /** * @file * Contains Drupal\event_dispatcher_demo\DemoEvent. */ namespace Drupal\event_dispatcher_demo; use Symfony\Component\EventDispatcher\Event; use Drupal\Core\Config\Config; class DemoEvent extends Event { protected $config; /** * Constructor. * * @param Config $config */ public function __construct(Config $config) { $this->config = $config; } /** * Getter for the config object. * * @return Config */ public function getConfig() { return $this->config; } /** * Setter for the config object. * * @param $config */ public function setConfig($config) { $this->config = $config; } }

As you can see, this is a simple class that extends the default Event class and which defines setters and getters for the config object we will be passing around using this event. And since we created it, let’s also make sure we use it in the file where we defined the form:

如您所见,这是一个简单的类,它扩展了默认的Event类,并为我们将使用此事件传递的config对象定义了setter和getter。 既然创建了它,我们还要确保在定义表单的文件中use它:

use Drupal\event_dispatcher_demo\DemoEvent;

活动订阅者 (The event subscriber)

Now that our form is functioning normally and an event is being dispatched when the form is saved, we should take advantage of that and subscribe to it. Let’s start with the event subscriber class that implements the EventSubscriberInterface. Inside a file called ConfigSubscriber.php (name of your choice) located in the src/EventSubscriber/ folder, we have the following:

现在,我们的表单可以正常运行,并且在保存表单时将调度一个事件,我们应该利用它并订阅它。 让我们从实现EventSubscriberInterface的事件订阅者类开始。 在src/EventSubscriber/文件夹中的名为ConfigSubscriber.php (您选择的名称)的文件中,我们具有以下内容:

<?php /** * @file * Contains Drupal\event_dispatcher_demo\EventSubscriber\ConfigSubscriber. */ namespace Drupal\event_dispatcher_demo\EventSubscriber; use Symfony\Component\EventDispatcher\EventSubscriberInterface; class ConfigSubscriber implements EventSubscriberInterface { static function getSubscribedEvents() { $events['demo_form.save'][] = array('onConfigSave', 0); return $events; } public function onConfigSave($event) { $config = $event->getConfig(); $name_website = $config->get('my_name') . " / " . $config->get('my_website'); $config->set('my_name_website', $name_website); } }

So what happens here? The EventSubscriberInterface has only one required method called getSubscribedEvents(). This method is used to register events and callbacks to these events. So above we registered the callable onConfigSave() (found in the same class below) to the event dispatched with the identifier of demo_form.save. And in the callback method we simply add another value to the config object (based on a concatenation of the existing two values). The latter part is just for our demo purposes: here you can do what you want.

那么这里发生了什么? EventSubscriberInterface只有一个必需的方法,称为getSubscribedEvents() 。 此方法用于注册事件和这些事件的回调。 所以上面我们注册的可调用 onConfigSave()以下同级别中)的事件调度与标识符demo_form.save 。 在回调方法中,我们只需向配置对象添加另一个值(基于现有两个值的串联)。 后面的部分只是出于演示目的:在这里您可以做自己想做的。

When we subscribed our onConfigSave() method to listen to the demo_form.save event, we passed a weight of 0. If you register multiple callbacks to the same event, this weight becomes important (the higher the number, the earlier it gets called). And if a callback alters the same values as one triggered before, they will get overridden. It’s good to keep this in mind.

当我们订阅onConfigSave()方法以监听demo_form.save事件时,我们传递的权重为0。如果您向同一事件注册多个回调,则此权重就变得很重要(数字越大,调用的时间越早) 。 而且,如果回调更改的值与之前触发的值相同,则它们将被覆盖。 记住这一点很好。

Now in order for this event subscriber to work, we need to define it as a service and give it the event_subscriber tag. So in a file called event_dispatcher_demo.services.yml found in the root folder of our module, we will have this:

现在,为了使此事件订阅者正常工作,我们需要将其定义为服务,并event_subscriber赋予event_subscriber标记。 因此,在模块的根文件夹中找到的名为event_dispatcher_demo.services.yml的文件中,我们将具有以下内容:

services: event_dispatcher_demo.config_subscriber: class: Drupal\event_dispatcher_demo\EventSubscriber\ConfigSubscriber tags: - { name: event_subscriber }

This is a simple service definition with the right tag that will make the container automatically instantiate an object of this class whenever the dispatcher is in play. And that is pretty much it. Clear the cache and if you now save the form again, the configuration object that gets saved will always contain a new value that is based on the first two.

这是带有正确标记的简单服务定义,它将使容器在调度程序进行操作时自动实例化此类的对象。 就是这样。 清除缓存,如果现在再次保存表单,则保存的配置对象将始终包含基于前两个值的新值。

钩子 (Hooks)

In the final part of this article we will demonstrate the use of hooks to achieve a similar goal.

在本文的最后一部分,我们将演示如何使用钩子实现类似的目标。

First, let’s change the form submit handler and instead of dispatching events, we will invoke a hook and pass the config values to it. This is what the new submitForm() method will look like:

首先,让我们更改表单提交处理程序,而不是调度事件,我们将调用一个钩子并将配置值传递给它。 这是新的submitForm()方法的外观:

public function submitForm(array &$form, FormStateInterface $form_state) { parent::submitForm($form, $form_state); $config = $this->config('event_dispatcher_demo.demo_form_config'); $config->set('my_name', $form_state->getValue('my_name')) ->set('my_website', $form_state->getValue('my_website')); $configData = $config->get(); $newData = \Drupal::service('module_handler')->invokeAll('demo_config_save', array($configData)); $config->merge($newData); $config->save(); }

We are not using any event objects nor the dispatcher service. Instead, we retrieve the Module Handler service that contains the invokeAll() method used to invoke hook implementations from all modules. This is essentially replacing the Drupal 7 module_invoke_all() helper. And again, it is recommended to inject this service, but for brevity, we’ll retrieve it statically.

我们没有使用任何事件对象,也没有使用调度程序服务。 取而代之的是,我们检索模块处理程序服务,该服务包含用于从所有模块调用挂钩实现的invokeAll()方法。 这实际上是在替换Drupal 7 module_invoke_all()帮助器。 再一次,建议注入此服务,但为简便起见,我们将静态检索它。

The hook implementation invoked in our case is hook_demo_config_save and it gets one parameter, an array of values pulled from our config object. Inside $newData we will have an array of values merged from all the implementations of this hook. We then merge that into our config object and finally save it.

在本例中,调用的hook实现是hook_demo_config_save ,它获取一个参数,即从我们的config对象中提取的一组值。 在$newData内部,我们将有一个由该钩子的所有实现合并而成的值数组。 然后,我们将其合并到配置对象中,最后将其保存。

Let’s quickly see an example hook implementation. As with Drupal 7, these can only be in .module files:

让我们快速查看示例挂钩实现。 与Drupal 7一样,这些只能位于.module文件中:

/** * Implements hook_demo_config_save(). */ function event_dispatcher_demo_demo_config_save($configValues) { $configValues['my_name_website'] = $configValues['my_name'] . " / " . $configValues['my_website']; return $configValues; }

As you can see, we are adding a new value to the config array that will later be merged into the object getting persisted. And we have essentially the same thing as we did with the event dispatcher.

如您所见,我们正在向配置数组添加一个新值,该值以后将被合并到持久化的对象中。 我们在本质上与事件调度程序具有相同的功能。

结论 (Conclusion)

In this article we have taken a look at how the Symfony Event Dispatcher component works in Drupal 8. We’ve learned how flexible it makes our application when it comes to allowing others to extend functionality. Additionally, we’ve seen how the invoked hooks work in the new version of Drupal. Not much has changed since Drupal 7 in this respect apart from the frequency with which they are used. Many hooks have been replaced by plugins and annotations and the Event Dispatcher component has also taken on a big chunk of what was in D7 a hook responsibility.

在本文中,我们研究了Symfony Event Dispatcher组件在Drupal 8中的工作方式。我们了解了在允许其他人扩展功能时,它使我们的应用程序具有多大的灵活性。 此外,我们已经看到了新版本的Drupal中调用的钩子如何工作。 自从Drupal 7在这方面以来,除了使用频率之外,没有什么变化。 许多挂钩已被插件和注释所取代,并且Event Dispatcher组件也承担了D7中很大一部分的挂钩责任。

Although the Event Dispatcher approach is more verbose, it is the recommended way to go forward. Where possible, we no longer use the old procedural approach characteristic to hooks but rather object oriented, decoupled and testable solutions. And Symfony helps greatly with that.

尽管“事件分派器”方法较为冗长,但这是推荐的前进方式。 在可能的情况下,我们不再将旧的过程方法特性用于钩子,而是使用面向对象,分离和可测试的解决方案。 Symfony对此提供了很大帮助。

翻译自: https://www.sitepoint.com/drupal-8-hooks-symfony-event-dispatcher/

drupal8文档

相关资源:drupal使用hook_form_alter()修改表单实例
最新回复(0)