drupal8 自定义实体

tech2022-09-02  124

drupal8 自定义实体

Data validation is a very important part of any application. Drupal 7 has a great Form API that can handle complex validation of submitted data, which can then be turned into entities. However, form level validation is problematic. For example, it becomes difficult to handle entity (data) validation programmatically. We have to either reimplement validation logic or mimic form submissions in code. This makes any data interaction dependent on the form system and this is a bad idea.

数据验证是任何应用程序中非常重要的部分。 Drupal 7具有出色的Form API,可以处理提交数据的复杂验证,然后可以将其转换为实体。 但是,表单级别验证存在问题。 例如,变得难以以编程方式处理实体(数据)验证。 我们必须重新实现验证逻辑或在代码中模拟表单提交。 这使得任何数据交互都依赖于表单系统,这是一个坏主意。

With the introduction of subsystems such as the REST API, Drupal 8 needed something better to handle this problem. The Symfony Validation component was chosen and Drupal 8 builds on top of it to tailor for the realties of the Typed Data and plugin based Entity system. So now, form submissions validate entities just like REST calls do or any other programmatic interaction with the entities easily can.

随着诸如REST API之类的子系统的引入,Drupal 8需要更好地解决这个问题。 选择了Symfony Validation组件 ,Drupal 8在此组件的基础上构建,以适应类型化数据和基于插件的实体系统的实际情况。 因此,现在,表单提交就像REST调用一样验证实体,或者轻松地与实体进行任何其他程序交互。

In this article, and its followup, we will explore the Drupal 8 Entity Validation API, see how it works, and how it can be extended. To better understand this, we will also take a look at the Typed Data API which underpins the entity system in Drupal 8. There will be some code examples that already exist in core but we will also write a bit of our own code which can be found in this git repository within the demo module.

在本文及其后续文章中,我们将探讨Drupal 8实体验证API,了解其工作方式以及扩展方式。 为了更好地理解这一点,我们还将看一下Typed Data API,它是Drupal 8中实体系统的基础。会有一些代码示例已经存在于核心中,但是我们还将编写一些自己的代码,这些代码可以在demo模块内的git存储库中找到。

类型数据 (Typed Data)

Typed Data is the Drupal 8 API that was built to provide a consistent way of interacting with data or metadata about the data itself. Why is this important for our topic? Because validation is defined and invoked on typed data objects.

Typed Data是Drupal 8 API,其构建目的是提供一种与数据或有关数据本身的元数据进行交互的一致方式。 为什么这对我们的主题很重要? 因为验证是在类型化的数据对象上定义和调用的。

Two important components of this API stand out: the data definition and the DataType plugins. The role of the former is to define data and how interaction works with it (including things like settings or validation constraints). The role of the latter is to provide a way to get and set values from that type of data. When they are instantiated, data type plugins make use of data definition instances passed on by the plugin manager. The latter can also infer which data definition needs to be used by a DataType plugin type.

该API的两个重要组成部分非常突出:数据定义和DataType插件。 前者的作用是定义数据以及如何与数据交互(包括设置或验证约束之类的东西)。 后者的作用是提供一种从此类数据中获取和设置值的方法。 实例化数据类型插件时,它们将使用插件管理器传递的数据定义实例。 后者还可以推断DataType插件类型需要使用哪个数据定义。

Let’s see an example:

让我们来看一个例子:

$definition = DataDefinition::create('string') ->addConstraint('Length', array('max' => 20));

We created a string data definition and applied the Length constraint to it. Constraints are a key part of the validation API and they define the type of validation that will run on the data. They are coupled with a validator that actually performs the task, but we will see more about constraints and validators in the second part of this series.

我们创建了一个string数据定义,并对其应用了Length约束。 约束是验证API的关键部分,它们定义了将在数据上运行的验证类型。 它们与实际执行任务的验证器结合在一起,但是在本系列的第二部分中,我们将看到有关约束和验证器的更多信息。

Next, we use the DataType plugin manager to create the actual data type plugin instance:

接下来,我们使用DataType插件管理器来创建实际的数据类型插件实例:

$string_typed_data = \Drupal::typedDataManager()->create($definition, 'my string');

We loaded the manager service statically here for brevity but you should use dependency injection in your project if you are in a class context. The create() method on the TypedDataManager takes the data definition as the first parameter, the actual value as its second and returns a DataType plugin instance of the type that matches the definition: in our case StringData. For complex data, DataType plugins can specify in their annotations which data definition class they need to use. And for more information about plugins in Drupal 8, make sure you check out my previous articles on the topic.

为了简洁起见,我们在此处静态加载了Manager服务,但是如果您处于类上下文中,则应在项目中使用依赖项注入。 TypedDataManager上的create()方法将数据定义作为第一个参数,将实际值作为第二个参数,并返回与定义匹配的类型的DataType插件实例:在我们的示例中为StringData 。 对于复杂数据, DataType插件可以在其批注中指定它们需要使用的数据定义类。 有关Drupal 8中插件的更多信息,请确保查看我以前关于该主题的文章。

One of the methods on this plugin instance is validate(), which triggers data validation against all the constraints that have been applied to the definition and returns an instance of Symfony’s ConstraintViolationList. By iterating over it or using methods like count() or get() we can check if the data is valid and which constraints have failed if not.

这个插件实例的方法之一是validate() ,它针对已应用到定义的所有约束触发数据验证,并返回Symfony的ConstraintViolationList实例。 通过对其进行迭代或使用诸如count()或get()我们可以检查数据是否有效以及哪些约束失败了。

In our example, the violation list should have no validation errors because the string we used is under 20 characters. Had we used a longer string when creating the plugin, we would have had one violation represented by a Symfony ConstraintViolationInterface instance with a message, offending value and even a property path.

在我们的示例中,违规列表应该没有验证错误,因为我们使用的字符串小于20个字符。 如果我们在创建插件时使用了更长的字符串,我们将遇到一个Symfony ConstraintViolationInterface实例所代表的违规行为,其中包含一条消息,令人反感的值甚至是一个属性路径。

类型化数据和内容实体 (Typed Data and Content Entities)

Now that we know a bit about the Typed Data API, let’s see how this applies to content entities.

现在我们对类型数据API有所了解,让我们看看这如何应用于内容实体。

Entity data in Drupal 7 is split between entity properties (usually the columns on the entity table) and Field API fields that are configured through the UI. In Drupal 8, they have been brought under the same umbrella so the old properties become fields as well. However, a difference still remains in that some fields (mainly the old entity properties) are defined as base fields while the rest are configurable fields.

Drupal 7中的实体数据在实体属性(通常是实体表中的列)和通过UI配置的Field API字段之间划分。 在Drupal 8中,它们被置于同一保护伞下,因此旧的属性也变成了领域。 但是,仍然存在差异,因为某些字段(主要是旧的实体属性)被定义为基础字段,而其余字段是可配置的字段。

The data definitions for these fields are BaseFieldDefinition and FieldConfig, but they are both implementors of the same FieldDefinitionInterface (which is a complex extender of the DataDefinitionInterface – the interface directly implemented by DataDefinition we saw earlier).

这些字段的数据定义是BaseFieldDefinition和FieldConfig ,但它们都是同一个FieldDefinitionInterface实现者(这是DataDefinitionInterface的复杂扩展程序,即我们之前看到的DataDefinition直接实现的接口)。

Each individual field holds data in a special FieldItemListInterface implementation and is always a list of individual FieldItem plugins (even if there is only one real value in the field). Each of these plugins extends a DataType plugin and uses a type of DataDefinitionInterface implementation itself (usually FieldItemDataDefinition). Things are quite complex at this level so it’s well worth taking a closer look at the makeup of entity fields to better understand this architecture.

每个单独的字段都在特殊的FieldItemListInterface实现中保存数据,并且始终是单个FieldItem插件的列表(即使该字段中只有一个实数值)。 这些插件中的每一个都扩展了一个DataType插件,并使用一种类型的DataDefinitionInterface实现本身(通常是FieldItemDataDefinition )。 在此级别上,事情非常复杂,因此值得仔细研究实体字段的构成以更好地了解此体系结构。

添加实体和字段约束 (Adding Entity and Field Constraints)

Constraints in Drupal 8 are also plugins which usually hold a small amount of information about how data is actually being validated, what error message should be used in case of failure and any additional options the validator needs. The validator class (which is referenced by the constraint) is responsible for checking the data. We’ve seen one example, Length, which is in fact the LengthConstraint class validated directly by Symfony’s LengthValidator class. We will see in the second part how to create our own constraint and validator. For now, though, let’s see how we can add existing constraints to content entities and fields.

Drupal 8中的约束也是插件,通常包含少量信息,这些信息涉及如何实际验证数据,发生故障时应使用什么错误消息以及验证器需要的其他任何选项。 验证器类(由约束引用)负责检查数据。 我们已经看到了一个例子, Length ,这实际上是在LengthConstraint通过的Symfony的直接验证类LengthValidator类。 我们将在第二部分中看到如何创建自己的约束和验证器。 不过,现在让我们看看如何将现有约束添加到内容实体和字段。

实体级别约束 (Entity level constraints)

Entity level constraints are added in the annotation of the entity class itself. For example, this is how the Comment entity has defined a constraint for its name/author fields combination:

实体级别约束被添加到实体类本身的注释中。 例如,这是Comment实体为其名称/作者字段组合定义约束的方式:

... constraints = { "CommentName" = {} } ...

In this example, CommentName is the plugin ID of the constraint that will be validated against when saving a comment entity. The opening and closing braces means that the plugin takes no options.

在此示例中, CommentName是将在保存注释实体时对其进行验证的约束的插件ID。 左括号和右括号表示该插件不包含任何选项。

If we wanted to add to or remove a constraint from an existing entity, we’d have to implement hook_entity_type_alter():

如果我们想添加或删除现有实体中的约束,则必须实现hook_entity_type_alter() :

function demo_entity_type_alter(array &$entity_types) { /** @var \Drupal\Core\Entity\ContentEntityType $node */ $node = $entity_types['node']; $node->addConstraint('ConstraintPluginName', ['array', 'of', 'options']); }

In this example, we are adding a fictitious constraint to the Node entity and passing an array of options to it.

在此示例中,我们向节点实体添加了一个虚拟约束,并将一系列选项传递给它。

现场水平约束 (Field level constraints)

There is more than one way to add field level constraints depending on whether the content entity type is defined in our module and whether the type of field we are talking about is a base field or configurable.

有多种添加字段级别约束的方法,具体取决于是否在模块中定义了内容实体类型,以及我们所讨论的字段类型是基础字段还是可配置字段。

If we are defining our own entity type, one of the methods on the actual entity class that we have to implement is baseFieldDefinitions(). This is where we return an array of BaseFieldDefinition field definitions and we can easily add our constraints there. For example, this is how the Node ID base field is being defined:

如果要定义自己的实体类型,则必须在实际实体类上实现的方法之一是baseFieldDefinitions() 。 这是我们返回BaseFieldDefinition字段定义的数组的BaseFieldDefinition ,我们可以轻松地在其中添加约束。 例如,这是定义节点ID基础字段的方式:

$fields['nid'] = BaseFieldDefinition::create('integer') ->setLabel(t('Node ID')) ->setDescription(t('The node ID.')) ->setReadOnly(TRUE) ->setSetting('unsigned', TRUE);

Similarly to how we added constraints to the DataDefinition instance earlier, the Node entity could also add constraints to the Node ID field, more specifically:

与我们之前向DataDefinition实例添加约束的方式类似,Node实体也可以向Node ID字段添加约束,更具体地说:

to the BaseFieldDefiniton itself, which is, as we saw, the definition for the FieldItemListInterface implementation (the list)

到BaseFieldDefiniton本身,如我们所见,它是FieldItemListInterface实现的定义(列表)

to the individual FieldItemDataDefinition items, which are, as we saw, a type of complex data definition for the FieldItemInterface implementations (the items)

到单个FieldItemDataDefinition项目,如我们所见,这是FieldItemInterface实现(项目)的一种复杂数据定义类型

If we want to add a constraint to a Node base field (or of any other content entity type not defined by our module), we have to implement hook_entity_base_field_info_alter() and add our constraint there:

如果我们想要一个约束添加到一个节点基地场(或不是由我们的模块定义的任何其他内容实体类型),我们必须实现hook_entity_base_field_info_alter()并添加我们的约束有:

function demo_entity_base_field_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type) { if ($entity_type->id() === 'node') { /** @var \Drupal\Core\Field\BaseFieldDefinition $title */ $title = $fields['title']; $title->addPropertyConstraints('value', ['Length' => ['max' => 5]]); } }

In the example above, we are adding a constraint to the Node title field to make sure that no title can be longer than 5 characters. To notice is that we are using the addPropertyConstraint() method instead of the addConstraint() one we saw earlier. This is because we are not targeting the definition for the list of items but the individual item definitions themselves (FieldItemDataDefinition).

在上面的示例中,我们向“节点标题”字段添加了一个约束,以确保标题不能超过5个字符。 注意,我们使用的是addPropertyConstraint()方法,而不是前面看到的addConstraint()方法。 这是因为我们不是针对项目列表的定义,而是针对单个项目定义本身( FieldItemDataDefinition )。

By using the addConstraint() method, we are adding a constraint to the entire list of items. This means that when the constraint gets validated, the validator receives the entire list not just the value of the individual item.

通过使用addConstraint()方法,我们将约束添加到整个项目列表。 这意味着当约束得到验证时,验证器将接收整个列表,而不仅仅是单个项目的值。

If we want to add a constraint to a configurable field, the process is quite similar. The difference is that we need to implement hook_entity_bundle_field_info_alter() and work with FieldConfig instances instead of BaseFieldDefinition.

如果我们想向可配置字段添加约束,则过程非常相似。 不同之处在于,我们需要实现hook_entity_bundle_field_info_alter()并使用FieldConfig实例而不是BaseFieldDefinition 。

If we want to inspect the constraints that are already set on the field, we can do something like this:

如果要检查已经在现场设置的约束,可以执行以下操作:

$title = $fields['title']; $constraints = $title->getConstraints(); $property_constraints = $title->getItemDefinition()->getConstraints();

In this example, $title is either an instance of BaseFieldDefinition or FieldConfig. The $constraints array is, as expected, a list of constraints applied to the entire list of field items while the $property_constraints is an array of constraints applied to the individual field items themselves. We can notice, though, that if we run this code after we’ve applied the Length constraint, we will find inside $property_constraints a ComplexData constraint which wraps over the individual constraints applied to the field values. This is because there can be multiple individual data definitions for a single field item and Drupal by default groups them like so.

在此示例中, $title是BaseFieldDefinition或FieldConfig的实例。 正如预期的那样, $constraints数组是应用于整个字段项列表的约束列表,而$property_constraints是应用于单个字段项本身的约束数组。 但是,我们可以注意到,如果在应用了Length约束之后运行此代码,我们将在$property_constraints内部发现一个ComplexData约束,该约束将包裹应用于字段值的各个约束。 这是因为单个字段项目可以有多个单独的数据定义,而Drupal默认将它们分组。

结论 (Conclusion)

In this article, we’ve started looking at the Entity Validation API in Drupal 8. To this end, we also had to get a sense of the Typed Data API which is used as a foundation for the entity system. Once that became a bit clearer, we’ve seen how constraints can be added to various types of data definitions, including those used by entities and fields.

在本文中,我们已经开始研究Drupal 8中的Entity Validation API。为此,我们还必须对用作实体系统基础的Typed Data API有所了解。 一旦变得更加清晰,我们就可以看到如何将约束添加到各种类型的数据定义,包括实体和字段使用的约束。

In the next part, we will look at how the actual validation works and how handling the violations that may occur should be done. Additionally, we will create our own constraint and validator and apply our knowledge from this part to various data definition types. Fun!

在下一部分中,我们将研究实际的验证工作原理以及应该如何处理可能发生的违规行为。 另外,我们将创建自己的约束和验证器,并将我们在这部分中的知识应用于各种数据定义类型。 好玩!

翻译自: https://www.sitepoint.com/drupal-8-entity-validation-and-typed-data-explained/

drupal8 自定义实体

相关资源:jdk-8u281-windows-x64.exe
最新回复(0)