drupal变量术语字段
In the first installment of this series we started our journey towards creating some simple but powerful functionality. The goal we set was to have the possibility to load a form on each node page and to be able to choose which form type should be used on the different node bundles.
在本系列的第一部分中,我们开始了创建一些简单但功能强大的功能的旅程。 我们设定的目标是可以在每个节点页面上加载表单,并能够选择在不同的节点束中应使用哪种表单类型。
The first step is done. We created a custom plugin type called ReusableForm
already featured with a base plugin class that new plugins can extend. Additionally, we saw that each plugin will interact with a form class that is defined in their annotation. And like with the plugins, we also created a base class new forms can extend.
第一步完成。 我们创建了一个名为ReusableForm
的自定义插件类型,该插件类型已经具有新插件可以扩展的基本插件类。 此外,我们看到每个插件都将与在其批注中定义的表单类进行交互。 像使用插件一样,我们还创建了可以扩展新窗体的基类。
It follows to see how we can configure the core node types to use one of the plugins defined on the site and how to render the relevant form when viewing the node. But first, in order to have something to work with, let’s create our first ReusableForm
plugin that uses a very simple form.
接下来,我们将了解如何配置核心节点类型以使用站点上定义的插件之一,以及如何在查看节点时呈现相关表单。 但是首先,为了处理一些事情,让我们创建第一个ReusableForm
非常简单形式的ReusableForm
插件。
我们的第一个插件 (Our first plugin)
Inside the src/Form
folder of our module, create a class called BasicForm.php
(or whatever you want to call it). Inside, we can have this simple form definition:
在我们模块的src/Form
文件夹中,创建一个名为BasicForm.php
的类(或任何您想调用的类)。 在内部,我们可以有一个简单的表单定义:
<?php
namespace Drupal\reusable_forms\Form;
use Drupal\Core\Form\FormStateInterface;
/**
* Defines the BasicForm class.
*/
class BasicForm extends ReusableFormBase {
/**
* {@inheritdoc}.
*/
public function getFormId() {
return 'basic_form';
}
/**
* {@inheritdoc}.
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form = parent::buildForm($form, $form_state);
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Handle form submission.
}
}
This form class extends our form base and implements all the required methods. For more information on forms and how they work in Drupal 8, you can check out one of my previous articles on Sitepoint.com. But as you can see, we are calling the parent buildForm()
method so that the logic we defined in the base class takes place. The rest is implementation detail (and here we don’t do much). But you can perform whatever logic you want and handle the submissions in any way you need.
这个表单类扩展了我们的表单基础并实现了所有必需的方法。 有关表格及其在Drupal 8中的工作方式的更多信息,您可以在Sitepoint.com上查阅我以前的文章之一。 但是正如您所看到的,我们正在调用父buildForm()
方法,以便发生在基类中定义的逻辑。 剩下的就是实现细节(这里我们不做很多事情)。 但是您可以执行所需的任何逻辑,并以所需的任何方式处理提交。
This form can now be used with a plugin of our type and it will have 3 form elements inherited from the form base class.
现在可以将此表单与我们类型的插件一起使用,它将具有3个从表单基类继承的表单元素。
Next, let’s create our first plugin. Inside the src/Plugin/ReusableForm
folder of our module, we can have a file called BasicForm.php
with the following:
接下来,让我们创建第一个插件。 在我们模块的src/Plugin/ReusableForm
文件夹中,我们可以有一个名为BasicForm.php
的文件, BasicForm.php
包含以下内容:
<?php
namespace Drupal\reusable_forms\Plugin\ReusableForm;
use Drupal\reusable_forms\ReusableFormPluginBase;
/**
* Provides a basic form.
*
* @ReusableForm(
* id = "basic_form",
* name = @Translation("Basic Form"),
* form = "Drupal\reusable_forms\Form\BasicForm"
* )
*/
class BasicForm extends ReusableFormPluginBase {}
As you can see, all we need for our purposes is in the annotation: the id, name and form class to be used with this plugin. You can add your own methods, override existing ones and handle way more complex logic if you want, though. But for us this is ok as the ReusableFormPluginBase
class handles the form building already.
如您所见,我们所需的全部功能都在注释中:此插件将使用的id,名称和表单类。 不过,您可以添加自己的方法,覆盖现有方法并根据需要处理更复杂的逻辑。 但是对我们来说这是可以的,因为ReusableFormPluginBase
类已经处理了表单构建。
And that’s it. This is all it takes to now define new ReusableForm
plugins. We create a form class with all the fields and logic it needs and then reference it inside a simple plugin class.
就是这样。 这就是现在定义新的ReusableForm
插件所需的全部工作。 我们使用所需的所有字段和逻辑创建一个表单类,然后在一个简单的插件类中引用它。
节点类型配置 (Node type configuration)
Now that we have a ReusableForm plugin, we can proceed with configuring the node type entities to make use of it. For this, we’ll need to first turn our plugin manager into a service so that we can access it and load plugins.
现在我们有了ReusableForm插件,我们可以继续配置节点类型实体以使用它。 为此,我们需要首先将插件管理器变成服务,以便我们可以访问它并加载插件。
Inside the reusable_forms.services.yml
file of our module we can have this:
在我们模块的reusable_forms.services.yml
文件中,我们可以执行以下操作:
services:
plugin.manager.reusable_forms:
class: Drupal\reusable_forms\ReusableFormsManager
parent: default_plugin_manager
Now we’ll be able to access the plugin manager using the plugin.manager.reusable_forms
service id. And by using the parent
key in the definition, we specified that all its dependencies can be looked up from the parent. That’s cool!
现在,我们可以使用plugin.manager.reusable_forms
服务ID来访问插件管理器。 并且通过在定义中使用parent
键,我们指定可以从parent
键查找其所有依赖项。 这很酷!
Next, let’s turn to our .module
file and do a couple of things in there. First, we want to alter the node type edit form and add some extra information about our form plugins. This is so that when users edit a node bundle they can select which form plugin should be used with the nodes of that type:
接下来,让我们转到我们的.module
文件,并在其中执行一些操作。 首先,我们要更改节点类型编辑表单,并添加一些有关表单插件的额外信息。 这样,当用户编辑节点捆绑包时,他们可以选择该类型的节点应使用哪种表单插件:
/**
* Implements hook_form_BASE_FORM_ID_alter().
*/
function reusable_forms_form_node_type_form_alter(&$form, FormStateInterface $form_state) {
$form['reusable_forms'] = array(
'#type' => 'details',
'#title' => t('Reusable forms'),
'#group' => 'additional_settings',
);
// Load the current node type configuration entity.
$node_type = $form_state->getFormObject()->getEntity();
$form['reusable_forms']['reusable_forms_enabled'] = array(
'#type' => 'checkbox',
'#title' => t('Enable reusable forms'),
'#description' => t('Check this box if you would like a reusable form on this node type.'),
'#default_value' => $node_type->getThirdPartySetting('reusable_forms', 'enabled', 0),
);
$form_plugins = \Drupal::service('plugin.manager.reusable_forms')->getDefinitions();
$options = [];
foreach ($form_plugins as $name => $plugin) {
$options[$name] = $plugin['name'];
}
$form['reusable_forms']['reusable_forms_enabled'] = array(
'#type' => 'radios',
'#title' => t('Available forms'),
'#default_value' => $node_type->getThirdPartySetting('reusable_forms', 'plugin', 'basic_form'),
'#options' => $options,
'#description' => t('The available forms you can choose from for this node type.'),
'#states' => array(
'visible' => array(
':input[name="reusable_forms_enabled"]' => array('checked' => TRUE),
),
),
);
$form['#entity_builders'][] = 'reusable_forms_form_node_type_form_builder';
}
Implementing hook_form_BASE_FORM_ID_alter
will do the trick perfectly for this. Though we mustn’t forget to use the FormStateInterface
class at the top:
实现hook_form_BASE_FORM_ID_alter
可以完美地解决这一问题。 尽管我们不能忘记在顶部使用FormStateInterface
类:
use Drupal\Core\Form\FormStateInterface;
So what happens here? We are creating a new fieldset to group two form fields relevant for our purpose: a checkbox to enable the reusable forms and a select list to choose from the existing plugins. As options to the latter we are building an array of all the plugin names after we load our plugin manager and request from it all the available definitions. And using the #states
magic we make sure that this latter field is only visible if the checkbox to enable the forms is checked.
那么这里发生了什么? 我们正在创建一个新的字段集,以将与我们的目的相关的两个表单字段分组:一个复选框,用于启用可重复使用的表单;一个选择列表,用于从现有插件中进行选择。 作为后者的选项,我们在加载插件管理器并向其请求所有可用定义之后,将构建一个包含所有插件名称的数组。 使用#states
魔术,我们确保仅在选中了启用表单的复选框后,该字段才可见。
Right at the end we are adding an extra callback function to the #entity_builders
group that will be triggered when the entity is saved and that has the purpose of mapping values to an entity. So let’s see that function now:
在最后,我们#entity_builders
组添加一个额外的回调函数,该函数将在保存实体时触发,其目的是将值映射到实体。 现在让我们来看一下该函数:
/**
* Entity form builder for the node type form to map some values to third party
* settings
*/
function reusable_forms_form_node_type_form_builder($entity_type, NodeTypeInterface $type, &$form, FormStateInterface $form_state) {
if ($form_state->getValue('reusable_forms_enabled') === 1) {
$type->setThirdPartySetting('reusable_forms', 'enabled', 1);
$type->setThirdPartySetting('reusable_forms', 'plugin', $form_state->getValue('reusable_forms_enabled'));
return;
}
$type->unsetThirdPartySetting('reusable_forms', 'enabled');
$type->unsetThirdPartySetting('reusable_forms', 'plugin');
}
And again, let’s make use of the NodeTypeInterface
at the top:
再一次,让我们在顶部使用NodeTypeInterface
:
use Drupal\node\NodeTypeInterface;
In this function we are doing something simple but awesome. If the admin has enabled the use of reusable forms on this bundle, we make use of the configuration entity’s third party settings space to store our data. Otherwise we simply unset it if it exists.
在此功能中,我们正在做一些简单但很棒的事情。 如果管理员已启用此捆绑软件上的可重用表单,则我们将使用配置实体的第三方设置空间来存储我们的数据。 否则,只要存在它,我们就将其取消设置。
I purposefully ignored this last point when talking about the form alter we implemented before. Now you can see how the form element default values are populated by making a request to the third party settings object on the node type configuration entity.
在谈论我们之前实现的表单更改时,我故意忽略了最后一点。 现在,您可以查看对节点类型配置实体上的第三方设置对象的请求,如何填充表单元素的默认值。
配置架构 (Configuration schema)
Before moving on to actually displaying the forms, we need to also add the schema definition for the new configuration we are storing. Inside the config/schema/reusable_forms.schema.yml
file of our module we can add the following:
在继续实际显示表单之前,我们还需要为要存储的新配置添加架构定义 。 在我们模块的config/schema/reusable_forms.schema.yml
文件中,我们可以添加以下内容:
node.type.*.third_party.reusable_forms:
type: mapping
label: 'Reusable Forms'
mapping:
reusable_forms_enabled:
type: boolean
label: 'Whether to enable the reusable forms on this node type'
reusable_forms_enabled:
type: sequence
label: 'Available forms'
sequence:
type: string
label: 'Plugin name'
节点视图 (Node view)
Now that we are storing the user preferences on the node type config entity, let’s see how we can render our chosen form plugins on the node pages of the enabled types.
现在,我们将用户首选项存储在节点类型配置实体上,让我们看看如何在启用的类型的节点页面上呈现我们选择的表单插件。
The first thing we’re going to do is define a content entity pseudo field that will be configurable from the node Manage display interface. Still inside our .module
file we can have this:
我们要做的第一件事是定义一个内容实体伪字段 ,该字段可从节点“ 管理”显示界面进行配置。 仍然在我们的.module
文件中,我们可以这样:
/**
* Implements hook_entity_extra_field_info().
*/
function reusable_forms_entity_extra_field_info() {
$extra = array();
$bundles = NodeType::loadMultiple();
$bundles = array_filter($bundles, function($bundle){
return $bundle->getThirdPartySetting('reusable_forms', 'enabled') === 1;
});
foreach ($bundles as $bundle) {
$extra['node'][$bundle->Id()]['display']['reusable_form'] = array(
'label' => t('Reusable form'),
'description' => t('Reusable form'),
'weight' => 100,
'visible' => TRUE,
);
}
return $extra;
}
And again we have to use NodeType
at the top:
同样,我们必须在顶部使用NodeType
:
use Drupal\node\Entity\NodeType;
What happens here is simple. We load all the node bundles and filter out the ones for which the reusable forms are not enabled. Then for each one we define an extra display
component in a very self-explanatory way.
这里发生的事情很简单。 我们加载所有节点束,并过滤掉未启用可重用格式的那些束。 然后,我们以一种不言自明的方式为每个对象定义一个额外的display
组件。
By clearing the cache will be able to already see the new pseudo field in place on the configuration page – though it won’t yet do anything. Which brings us to the last piece of the puzzle, rendering the relevant form.
通过清除缓存,虽然它不会做任何事情,但已经可以在配置页上看到新的伪字段。 这将我们带到了难题的最后一部分,呈现了相关形式。
To make the pseudo field we just defined useful, we need to implement hook_entity_view
(or a variant of it) and render its content:
为了使我们刚刚定义的伪字段有用,我们需要实现hook_entity_view
(或它的一个变体)并呈现其内容:
/**
* Implements hook_ENTITY_TYPE_view().
*/
function reusable_forms_node_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode, $langcode) {
if ($display->getComponent('reusable_form')) {
$plugin_manager = \Drupal::service('plugin.manager.reusable_forms');
$node_type = NodeType::load($entity->bundle());
$plugin = $node_type->getThirdPartySetting('reusable_forms', 'plugin');
if (!$plugin) {
return;
}
$build['reusable_form'] = $plugin_manager->createInstance($plugin)->buildForm($entity);
}
}
And let’s not forget the use statements at the top:
并且不要忘记顶部的use语句:
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
First, we check if the reusable_form
component exists on this node’s display (whether it is made visible in the UI). If it is, we add the reusable_form
key to the render array that is building this node view.
首先,我们检查该节点的显示器上是否存在reusable_form
组件(是否在UI中使其可见)。 如果是,我们将reusable_form
键添加到构建此节点视图的渲染数组中。
We start by loading the plugin manager and the node type configuration object of the current node entity. Then we use the former to instantiate a plugin with the ID defined in the third party settings of the latter. And in doing so, as you remember, we are passing as argument the current entity object so that the form being built can be aware of the entity it is showing up with (though we are not really taking advantage of this in our example).
我们首先加载插件管理器和当前节点实体的节点类型配置对象。 然后,我们使用前者实例化具有在后者的第三方设置中定义的ID的插件。 这样做,正如您所记得的,我们将当前实体对象作为参数传递,以便所构建的表单可以知道它所显示的实体(尽管在示例中我们并未真正利用这一点)。
And that’s it. What’s left is to install the module from scratch (since we added the config schema), edit a node type and select our default plugin (or another one if you created it). Then you can configure the display of this node type to show the reusable form which will then do so when viewing a node of that type. Neat.
就是这样。 剩下的就是从头开始安装模块(因为我们添加了配置模式),编辑节点类型并选择默认插件(如果创建了插件,则选择另一个插件)。 然后,您可以配置此节点类型的显示,以显示可重用的表单,然后在查看该类型的节点时将这样做。 整齐。
结论 (Conclusion)
As we’ve seen in this article series, Drupal 8 provides us with the tools to do some powerful stuff. With the plugin system we set up the basis for our reusable functionality. And then we hooked into various points of Drupal core to make use of this logic. We’ve seen how any configuration entity that implements the ThirdPartySettingInterface
can be extended to include new data. And lastly, we’ve displayed relevant data when viewing content entities with the help of pseudo fields.
正如我们在本系列文章中所看到的,Drupal 8为我们提供了执行某些强大功能的工具。 通过插件系统,我们为可重用功能奠定了基础。 然后,我们探讨了Drupal核心的各个方面,以利用此逻辑。 我们已经看到了实现ThirdPartySettingInterface
任何配置实体如何可以扩展为包括新数据。 最后,我们在使用伪字段查看内容实体时显示了相关数据。
But this is just the beginning. You can take what we did here and extend it to your needs. For instance, you can create a new content entity type that will model form submissions. The possibilities are endless.
但这仅仅是开始。 您可以采用我们在这里所做的事情,并将其扩展到您的需求。 例如,您可以创建一个新的内容实体类型,以对表单提交进行建模。 可能性是无止境。
翻译自: https://www.sitepoint.com/drupal-8-third-party-settings-and-pseudo-fields/
drupal变量术语字段