drupal8 自定义实体_Drupal 8自定义插件类型

drupal8 自定义实体

Drupal 8 comes with a great addition to the backend developer toolkit in the form of the plugin system. Completely new, specific to Drupal and evolved from serving only a few specific purposes, plugins have become the go-to system for reusable functionality in Drupal 8.

Drupal 8以插件系统的形式对后端开发人员工具包进行了很大的补充。 插件是Drupal的全新产品,仅出于几个特定目的而发展,插件已成为Drupal 8中可重用功能的首选系统。

Drupal 8 logo

In this article series of two parts, we will use this system to build a feature that allows the use of custom forms together with node entities. After we’re done, we’ll be able to do the following:

在这个由两部分组成的系列文章中,我们将使用该系统构建一个功能,该功能允许将自定义表单与节点实体一起使用。 完成后,我们将能够执行以下操作:

  • configure node bundles to use one of multiple form types to be displayed together with the node display

    配置节点包以使用多种表单类型之一与节点显示一起显示
  • easily define new form types by extending from a sensible base class

    通过明智的基础类扩展轻松定义新的表单类型

Because the topic is very well covered elsewhere, I will not go into the details of how plugins work. But do feel free to brush up on the theory before diving into the crux of it here. And if you want to take a look at the end result, the code we write in both articles can be found in this repository.

因为该主题在其他地方介绍得非常好 ,所以我不会详细介绍插件的工作方式。 但是在进入本文的症结之前,请务必重温该理论。 而且,如果您想查看最终结果,可以在此存储库中找到我们在这两篇文章中编写的代码。

We will get started by creating our custom plugin type. To this end, we will have 2 interfaces and 6 classes. It sounds like much, but I assure you they are rather boilerplate and quick to set up. Then, in the next installment of this series, we will see how to use it for our reusable forms attached to nodes.

我们将通过创建自定义插件类型开始。 为此,我们将有2个接口和6个类。 听起来很像,但我向您保证,它们只是样板而且易于设置。 然后,在本系列的下一部分中,我们将看到如何将其用于附加到节点的可重用表单中。

插件管理器 (Plugin manager)

Responsible for discovering and loading plugins, the most important part in any plugin type is the manager. However, it’s very simple to create one because Drupal already provides us with a sensible default base to extend. So in our module’s /src folder we can have this class inside a ReusableFormManager.php file (the de facto name of our plugin type becoming ReusableForm):

负责发现和加载插件,任何插件类型中最重要的部分是manager 。 但是,创建一个非常简单,因为Drupal已经为我们提供了一个明智的默认扩展基础。 因此,在模块的/src文件夹中,我们可以将此类放在ReusableFormManager.php文件中(我们的插件类型的实际名称变为ReusableForm ):

<?php
namespace Drupal\reusable_forms;

use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;

class ReusableFormsManager extends DefaultPluginManager {

public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
    parent::__construct('Plugin/ReusableForm', $namespaces, $module_handler, 'Drupal\reusable_forms\ReusableFormPluginInterface', 'Drupal\reusable_forms\Annotation\ReusableForm');
    $this->alterInfo('reusable_forms_info');
    $this->setCacheBackend($cache_backend, 'reusable_forms');
  }
}

As I mentioned, our manager extends the DefaultPluginManager class and just overrides the constructor to call the parent one with some important information about our plugin type:

如前所述,我们的管理器扩展了DefaultPluginManager类,并重写了构造函数以使用有关我们的插件类型的一些重要信息来调用父类:

  • Plugin/ReusableForm – the subdirectory where plugins of this type will be found within any module

    Plugin/ReusableForm –子目录,可在任何模块中找到此类型的插件

  • Drupal\reusable_forms\ReusableFormPluginInterface – the interface each of our plugins will need to implement

    Drupal\reusable_forms\ReusableFormPluginInterface –每个插件都需要实现的接口

  • Drupal\reusable_forms\Annotation\ReusableForm – the annotation class that will define our plugin properties (such as ID, name, etc.)

    Drupal\reusable_forms\Annotation\ReusableForm –注释类,它将定义我们的插件属性(例如ID,名称等)

Additionally, we create an alter hook which can be implemented by various modules to alter the plugin definitions and we set a key for our plugins in the cache backend. For more information about plugin managers, what they do and how they are set up, you should consult the documentation page available on Drupal.org.

另外,我们创建了一个alter hook,可以通过各种模块来实现以更改插件定义,并在缓存后端为插件设置密钥。 有关插件管理器,它们做什么以及如何设置的更多信息,您应该查阅Drupal.org上的文档页面

插件界面 (Plugin interface)

Next, let’s create that interface the manager expects all our plugins to implement. Inside a file called ReusableFormPluginInterface.php located in the src/ folder of our module, we can have this:

接下来,让我们创建该接口,经理希望我们的所有插件都能实现。 在我们模块的src/文件夹中的一个名为ReusableFormPluginInterface.php文件中,我们可以拥有:

<?php

namespace Drupal\reusable_forms;

use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Component\Plugin\PluginInspectionInterface;

interface ReusableFormPluginInterface extends PluginInspectionInterface, ContainerFactoryPluginInterface {

  /**
   * Return the name of the reusable form plugin.
   *
   * @return string
   */
  public function getName();

  /**
   * Builds the associated form.
   *
   * @param $entity EntityInterface.
   *   The entity this plugin is associated with.
   *
   * @return array().
   *   Render array of form that implements \Drupal\reusable_forms\Form\ReusableFormInterface
   */
  public function buildForm($entity);
}

This is a very simple interface that enforces only two methods: getName() and buildForm(). The first will return the name of the plugin while the latter is expected to be passed an entity object and to return a render array of a form definition that implements \Drupal\reusable_forms\Form\ReusableFormInterface (the interface we will set up for our actual forms). You’ll also notice that we are extending two other interfaces. Those provide us with some extra helpful methods and allow us to inject dependencies from the container.

这是一个非常简单的接口,仅强制执行两个方法: getName()buildForm() 。 前者将返回插件的名称,而后者将被传递给实体对象,并返回实现\Drupal\reusable_forms\Form\ReusableFormInterface的表单定义的渲染数组(我们将为实际设置的接口形式)。 您还将注意到,我们正在扩展其他两个接口。 这些为我们提供了一些额外的有用方法,并允许我们从容器中注入依赖项。

插件注释 (Plugin annotation)

As defined in the manager, let’s also set up our annotation class inside src/Annotation/ReusableForm.php:

根据管理器中的定义,让我们还在src/Annotation/ReusableForm.php设置我们的注释类:

<?php

namespace Drupal\reusable_forms\Annotation;

use Drupal\Component\Annotation\Plugin;

/**
 * Defines a reusable form plugin annotation object.
 *
 * @Annotation
 */
class ReusableForm extends Plugin {

  /**
   * The plugin ID.
   *
   * @var string
   */
  public $id;

  /**
   * The name of the form plugin.
   *
   * @var \Drupal\Core\Annotation\Translation
   *
   * @ingroup plugin_translatable
   */
  public $name;

  /**
   * The form class associated with this plugin
   *
   * It must implement \Drupal\reusable_forms\Form\ReusableFormInterface.
   *
   * @var string
   */
  public $form;
}

Here we simply extend the default Plugin annotation class and define three properties (id, name and form). These will be the three keys found in the annotation of our individual plugins (we’ll see an example in part two of this series).

在这里,我们只需扩展默认的Plugin注释类并定义三个属性(id,name和form)。 这些将是在我们各个插件的注释中找到的三个键(我们将在本系列的第二部分中看到一个示例)。

插件库 (Plugin base)

So far we have the core of what we need for our plugin type: a plugin manager that can discover new plugins using the annotation we defined and instantiate them with its default factory, i.e. the Container Factory.

到目前为止,我们拥有插件类型所需的核心:一个插件管理器,可以使用我们定义的注释发现新插件,并使用其默认工厂(即Container Factory)实例化它们。

Let us now lay the ground work for the plugins themselves by creating a base class all the plugins can/should/will extend. Inside the src/ folder of our module we can create a new file called ReusableFormPluginBase.php with the following abstract class inside:

现在,让我们通过创建所有插件可以/应该/将要扩展的基类,为插件本身奠定基础。 在我们模块的src/文件夹中,我们可以ReusableFormPluginBase.php以下抽象类创建一个名为ReusableFormPluginBase.php的新文件:

<?php

namespace Drupal\reusable_forms;

use Drupal\Component\Plugin\PluginBase;
use Drupal\Core\Form\FormBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;

abstract class ReusableFormPluginBase extends PluginBase implements ReusableFormPluginInterface {

  /**
   * The form builder.
   *
   * @var \Drupal\Core\Form\FormBuilder.
   */
  protected $formBuilder;

  /**
   * Constructs a ReusableFormPluginBase object.
   *
   * @param array $configuration
   * @param string $plugin_id
   * @param mixed $plugin_definition
   * @param FormBuilder $form_builder
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, FormBuilder $form_builder) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->formBuilder = $form_builder;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('form_builder')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getName() {
    return $this->pluginDefinition['name'];
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm($entity) {
    return $this->formBuilder->getForm($this->pluginDefinition['form'], $entity);
  }
}

There are a few things to note here. First, we are extending from the plugin base class provided by Drupal so that we get some useful functionality (the methods of PluginInspectionInterface are already implemented with sensible defaults). Second, we are implementing the interface we defined earlier. All of our plugins need to implement it so might as well take care of it here. Third, we are using dependency injection to load from the container the form_builder service we will need to build our forms. This is possible because our interface extends from the ContainerFactoryPluginInterface.

这里有几件事要注意。 首先,我们从Drupal提供的插件基类进行扩展,以便获得一些有用的功能( PluginInspectionInterface的方法已经使用合理的默认值实现了)。 其次,我们正在实现我们先前定义的接口。 我们所有的插件都需要实现它,因此最好在这里进行处理。 第三,我们使用依赖注入从容器中加载构建表单所需的form_builder服务。 这是可能的,因为我们的接口从ContainerFactoryPluginInterface扩展。

For more information about the service container and dependency injection in Drupal 8, check out one of my previous articles on Sitepoint.com.

有关Drupal 8中服务容器和依赖项注入的更多信息,请参阅我在Sitepoint.com上的前一篇文章。

As our interface dictates, we already take care of implementing the two methods right here in our base class. The getName() method will simply return the name of the plugin as defined in the plugin annotation. The buildForm() method, on the other hand, will use the form builder to build a form. For this it will use the class provided in the plugin annotation’s form key (which needs to be the fully qualified name of a class that implements our Form interface which we haven’t defined yet). In doing so, we also pass the $entity argument to the form (whatever that may be as long as it implements EntityInterface). This is so that the form that is being rendered on the node page becomes aware of the node it is being rendered with.

正如我们的界面所指示的,我们已经在基类中实现了这两种方法。 getName()方法将只返回插件注释中定义的插件名称。 另一方面, buildForm()方法将使用表单构建器来构建表单。 为此,它将使用插件注释的form键中提供的类(它必须是实现我们尚未定义的Form接口的类的完全限定名称)。 在此过程中,我们还将$entity参数传递给该表单(只要它实现EntityInterface ,就可以是任何形式)。 这样一来,正在节点页面上呈现的表单就可以知道正在与之一起呈现的节点。

表单界面 (Form Interface)

Our plugin type is pretty much finished. We can now provide some sensible defaults to the forms that will be used by these plugins. Inside src/Form/ReusableFormInterface.php we can have this simple interface:

我们的插件类型差不多完成了。 现在,我们可以为这些插件将使用的表单提供一些合理的默认值。 在src/Form/ReusableFormInterface.php我们可以有一个简单的界面:

<?php

namespace Drupal\reusable_forms\Form;

use Drupal\Core\Form\FormInterface;

interface ReusableFormInterface extends FormInterface {}

We are doing nothing here except for extending from the default Drupal FormInterface. We have it so that we can add whatever methods we need our forms to implement (currently none) and are able to identify forms that implement this interface as compatible with our plugins.

除了从默认的Drupal FormInterface扩展之外,我们在这里什么都不做。 我们拥有它,以便我们可以添加需要表单实现的任何方法(当前没有),并且能够标识实现此接口的表单与我们的插件兼容。

表格基础 (Form base)

Now that we have a form interface to implement, let’s also create a form base class that the rest of the forms can extend. Inside src/Form/ReusableFormBase.php we can have the following abstract class:

现在我们有了要实现的表单接口,我们还创建一个表单基础类,其余表单可以扩展。 在src/Form/ReusableFormBase.php我们可以具有以下抽象类:

<?php

namespace Drupal\reusable_forms\Form;

use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;

/**
 * Defines the ReusableFormBase abstract class
 */
abstract class ReusableFormBase extends FormBase implements ReusableFormInterface {

  /**
   * @var EntityInterface.
   */
  protected $entity;

  /**
   * {@inheritdoc}.
   */
  public function buildForm(array $form, FormStateInterface $form_state) {

    $build_info = $form_state->getBuildInfo();
    if ($build_info['args'] && $build_info['args'][0] instanceof EntityInterface) {
      $this->entity = $build_info['args'][0];
    }

    $form['first_name'] = array(
      '#type' => 'textfield',
      '#title' => $this->t('First name'),
    );

    $form['last_name'] = array(
      '#type' => 'textfield',
      '#title' => $this->t('Last name'),
    );

    $form['email'] = array(
      '#type' => 'email',
      '#title' => $this->t('Email'),
    );

    $form['actions']['#type'] = 'actions';
    $form['actions']['submit'] = array(
      '#type' => 'submit',
      '#value' => $this->t('Submit'),
      '#button_type' => 'primary',
    );

    return $form;
  }
}

As you can see, we are implementing the interface but we are also extending from the default Drupal FormBase class. And in this example we only have the buildForm method that returns a simple form with three fields and a submit button. You can do whatever you want here in terms of what you consider a good base form. Additionally though, at the beginning of this method, we are checking if an EntityInterface object has been passed as an argument when this form is built and setting it as a protected property. The classes extending this will be able to make use of this entity as long as they call the parent::buildForm() method first.

如您所见,我们正在实现接口,但是我们也从默认的Drupal FormBase类进行了扩展。 在此示例中,我们仅具有buildForm方法,该方法返回具有三个字段和一个提交按钮的简单表单。 您可以根据自己认为良好的基本形式在这里做任何您想做的事情。 另外,尽管如此,在此方法的开头,我们正在检查在构建此表单时是否EntityInterface对象作为参数传递并将其设置为受保护的属性。 只要扩展类首先调用parent::buildForm()方法,便可以使用该实体。

结论 (Conclusion)

In the first part of this article series we focused on setting up our custom plugin type and getting it ready for use. In doing so we quickly went through this process and saw how our plugins are expected to work with the actual form classes. In the next part we will work on making it possible to display them together with the nodes. This means adding extra configuration to the node type entities and displaying the forms using pseudo fields managed as part of the regular content view modes. Stay tuned!

在本系列文章的第一部分中,我们着重于设置自定义插件类型并使其准备就绪。 为此,我们快速完成了这一过程,并了解了我们的插件如何与实际的表单类一起工作。 在下一部分中,我们将努力使其与节点一起显示。 这意味着向节点类型实体添加额外的配置,并使用作为常规内容查看模式一部分管理的伪字段来显示表单。 敬请关注!

翻译自: https://www.sitepoint.com/drupal-8-custom-plugin-types/

drupal8 自定义实体

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值