Commit d9f7b3a3 authored by alexpott's avatar alexpott

Issue #1950632 by effulgentsia, Wim Leers, amateescu, yched: Create a...

Issue #1950632 by effulgentsia, Wim Leers, amateescu, yched: Create a FieldDefinitionInterface, decoupled from  and  config entities, and use it for formatters and widgets.
parent 096b8c5a
<?php
/**
* @file
* Contains \Drupal\Core\Entity\Field\FieldDefinitionInterface.
*/
namespace Drupal\Core\Entity\Field;
/**
* Defines an interface for entity field definitions.
*
* An entity field is a data object that holds the values of a particular field
* for a particular entity (see \Drupal\Core\Entity\Field\FieldInterface). For
* example, $node_1->body and $node_2->body contain different data and therefore
* are different field objects.
*
* In contrast, an entity field *definition* is an object that returns
* information *about* a field (e.g., its type and settings) rather than its
* values. As such, if all the information about $node_1->body and $node_2->body
* is the same, then the same field definition object can be used to describe
* both.
*
* It is up to the class implementing this interface to manage where the
* information comes from. For example, field.module provides an implementation
* based on two levels of configuration. It allows the site administrator to add
* custom fields to any entity type and bundle via the "field_entity" and
* "field_instance" configuration entities. The former for storing configuration
* that is independent of which entity type and bundle the field is added to,
* and the latter for storing configuration that is specific to the entity type
* and bundle. The class that implements "field_instance" configuration entities
* also implements this interface, returning information from either itself, or
* from the corresponding "field_entity" configuration, as appropriate.
*
* However, entity base fields, such as $node->title, are not managed by
* field.module and its "field_entity"/"field_instance" configuration entities.
* Therefore, their definitions are provided by different objects that implement
* this interface.
* @todo That is still in progress: https://drupal.org/node/1949932. Update this
* documentation with details when that's implemented.
*
* Field definitions may fully define a concrete data object (e.g.,
* $node_1->body), or may provide a best-guess definition for a data object that
* might come into existence later. For example, $node_1->body and $node_2->body
* may have different definitions (e.g., if the node types are different). When
* adding the "body" field to a View that can return nodes of different types,
* the View can get a field definition that represents the "body" field
* abstractly, and present Views configuration options to the administrator
* based on that abstract definition, even though that abstract definition can
* differ from the concrete definition of any particular node's body field.
*/
interface FieldDefinitionInterface {
/**
* Returns the machine name of the field.
*
* This defines how the field data is accessed from the entity. For example,
* if the field name is "foo", then $entity->foo returns its data.
*
* @return string
* The field name.
*/
public function getFieldName();
/**
* Returns the field type.
*
* @return string
* The field type.
*
* @todo Provide more information about field types after
* https://drupal.org/node/1969728 is implemented.
*/
public function getFieldType();
/**
* Returns the field settings.
*
* Each field type defines the settings that are meaningful for that type.
* For example, a text field can define a 'max_length' setting, and an image
* field can define a 'alt_field_required' setting.
*
* @return array
* An array of key/value pairs.
*/
public function getFieldSettings();
/**
* Returns the value of a given field setting.
*
* @param string $setting_name
* The setting name.
*
* @return mixed
* The setting value.
*/
public function getFieldSetting($setting_name);
/**
* Returns the names of the field's subproperties.
*
* A field is a list of items, and each item can contain one or more
* properties. All items for a given field contain the same property names,
* but the values can be different for each item.
*
* For example, an email field might just contain a single 'value' property,
* while a link field might contain 'title' and 'url' properties, and a text
* field might contain 'value', 'summary', and 'format' properties.
*
* @return array
* The property names.
*/
public function getFieldPropertyNames();
/**
* Returns whether the field is translatable.
*
* @return bool
* TRUE if the field is translatable.
*/
public function isFieldTranslatable();
/**
* Returns the human-readable label for the field.
*
* @return string
* The field label.
*/
public function getFieldLabel();
/**
* Returns the human-readable description for the field.
*
* This is displayed in addition to the label in places where additional
* descriptive information is helpful. For example, as help text below the
* form element in entity edit forms.
*
* @return string
* The field description.
*/
public function getFieldDescription();
/**
* Returns the maximum number of items allowed for the field.
*
* Possible values are positive integers or FIELD_CARDINALITY_UNLIMITED.
*
* @return integer
* The field cardinality.
*/
public function getFieldCardinality();
/**
* Returns whether at least one non-empty item is required for this field.
*
* Currently, required-ness is only enforced at the Form API level in entity
* edit forms, not during direct API saves.
*
* @var bool
* TRUE if the field is required.
*/
public function isFieldRequired();
}
......@@ -83,7 +83,7 @@ function datetime_theme() {
/**
* Implements hook_field_is_empty().
*/
function datetime_field_is_empty($item, $field) {
function datetime_field_is_empty($item, $field_type) {
if (empty($item['value'])) {
return TRUE;
}
......
......@@ -53,7 +53,7 @@ public function viewElements(EntityInterface $entity, $langcode, array $items) {
// The formatted output will be in local time.
$date->setTimeZone(timezone_open(drupal_get_user_timezone()));
if ($this->field['settings']['datetime_type'] == 'date') {
if ($this->getFieldSetting('datetime_type') == 'date') {
// A date without time will pick up the current time, use the default.
datetime_date_default_time($date);
}
......
......@@ -28,7 +28,7 @@
class DateTimePlainFormatter extends FormatterBase {
/**
* Implements Drupal\field\Plugin\Type\Formatter\FormatterInterface::viewElements().
* {@inheritdoc}
*/
public function viewElements(EntityInterface $entity, $langcode, array $items) {
......@@ -43,7 +43,7 @@ public function viewElements(EntityInterface $entity, $langcode, array $items) {
$date = $item['date'];
$date->setTimeZone(timezone_open(drupal_get_user_timezone()));
$format = DATETIME_DATETIME_STORAGE_FORMAT;
if ($this->field['settings']['datetime_type'] == 'date') {
if ($this->getFieldSetting('datetime_type') == 'date') {
// A date without time will pick up the current time, use the default.
datetime_date_default_time($date);
$format = DATETIME_DATE_STORAGE_FORMAT;
......
......@@ -11,8 +11,9 @@
use Drupal\field\Plugin\Type\Widget\WidgetBase;
use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\Field\FieldDefinitionInterface;
use Drupal\field\Plugin\PluginSettingsBase;
use Drupal\field\Plugin\Core\Entity\FieldInstance;
use Drupal\field\FieldInstanceInterface;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\datetime\DateHelper;
......@@ -38,10 +39,14 @@ class DateTimeDatelistWidget extends WidgetBase {
/**
* {@inheritdoc}
*/
public function __construct($plugin_id, array $plugin_definition, FieldInstance $instance, array $settings) {
public function __construct($plugin_id, array $plugin_definition, FieldDefinitionInterface $field_definition, array $settings) {
// Identify the function used to set the default value.
$instance['default_value_function'] = $this->defaultValueFunction();
parent::__construct($plugin_id, $plugin_definition, $instance, $settings);
// @todo Make this work for both configurable and nonconfigurable fields:
// https://drupal.org/node/1989468.
if ($field_definition instanceof FieldInstanceInterface) {
$field_definition->default_value_function = $this->defaultValueFunction();
}
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings);
}
/**
......@@ -55,13 +60,9 @@ public function defaultValueFunction() {
}
/**
* Implements \Drupal\field\Plugin\Type\Widget\WidgetInterface::formElement().
* {@inheritdoc}
*/
public function formElement(array $items, $delta, array $element, $langcode, array &$form, array &$form_state) {
$field = $this->field;
$instance = $this->instance;
$date_order = $this->getSetting('date_order');
$time_type = $this->getSetting('time_type');
$increment = $this->getSetting('increment');
......@@ -78,7 +79,7 @@ public function formElement(array $items, $delta, array $element, $langcode, arr
$element['#element_validate'][] = 'datetime_datelist_widget_validate';
// Identify the type of date and time elements to use.
switch ($field['settings']['datetime_type']) {
switch ($this->getFieldSetting('datetime_type')) {
case 'date':
$storage_format = DATETIME_DATE_STORAGE_FORMAT;
$type_type = 'none';
......@@ -126,7 +127,7 @@ public function formElement(array $items, $delta, array $element, $langcode, arr
);
// Set the storage and widget options so the validation can use them. The
// validator will not have access to field or instance settings.
// validator will not have access to the field definition.
$element['value']['#date_storage_format'] = $storage_format;
if (!empty($items[$delta]['date'])) {
......@@ -134,7 +135,7 @@ public function formElement(array $items, $delta, array $element, $langcode, arr
// The date was created and verified during field_load(), so it is safe to
// use without further inspection.
$date->setTimezone( new \DateTimeZone($element['value']['#date_timezone']));
if ($field['settings']['datetime_type'] == 'date') {
if ($this->getFieldSetting('datetime_type') == 'date') {
// A date without time will pick up the current time, use the default
// time.
datetime_date_default_time($date);
......@@ -145,22 +146,11 @@ public function formElement(array $items, $delta, array $element, $langcode, arr
}
/**
*
*
* @param array $form
* The form definition as an array.
* @param array $form_state
* The current state of the form as an array.
*
* @return array
*
* {@inheritdoc}
*/
function settingsForm(array $form, array &$form_state) {
$element = parent::settingsForm($form, $form_state);
$field = $this->field;
$instance = $this->instance;
$element['date_order'] = array(
'#type' => 'select',
'#title' => t('Date part order'),
......@@ -168,7 +158,7 @@ function settingsForm(array $form, array &$form_state) {
'#options' => array('MDY' => t('Month/Day/Year'), 'DMY' => t('Day/Month/Year'), 'YMD' => t('Year/Month/Day')),
);
if ($field['settings']['datetime_type'] == 'datetime') {
if ($this->getFieldSetting('datetime_type') == 'datetime') {
$element['time_type'] = array(
'#type' => 'select',
'#title' => t('Time type'),
......
......@@ -11,8 +11,9 @@
use Drupal\field\Plugin\Type\Widget\WidgetBase;
use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\Field\FieldDefinitionInterface;
use Drupal\field\Plugin\PluginSettingsBase;
use Drupal\field\Plugin\Core\Entity\FieldInstance;
use Drupal\field\FieldInstanceInterface;
use Drupal\Core\Datetime\DrupalDateTime;
/**
......@@ -32,10 +33,14 @@ class DateTimeDefaultWidget extends WidgetBase {
/**
* {@inheritdoc}
*/
public function __construct($plugin_id, array $plugin_definition, FieldInstance $instance, array $settings) {
public function __construct($plugin_id, array $plugin_definition, FieldDefinitionInterface $field_definition, array $settings) {
// Identify the function used to set the default value.
$instance['default_value_function'] = $this->defaultValueFunction();
parent::__construct($plugin_id, $plugin_definition, $instance, $settings);
// @todo Make this work for both configurable and nonconfigurable fields:
// https://drupal.org/node/1989468.
if ($field_definition instanceof FieldInstanceInterface) {
$field_definition->default_value_function = $this->defaultValueFunction();
}
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings);
}
/**
......@@ -49,13 +54,9 @@ public function defaultValueFunction() {
}
/**
* Implements \Drupal\field\Plugin\Type\Widget\WidgetInterface::formElement().
*
* {@inheritdoc}
*/
public function formElement(array $items, $delta, array $element, $langcode, array &$form, array &$form_state) {
$field = $this->field;
$instance = $this->instance;
$format_type = datetime_default_format_type();
// We are nesting some sub-elements inside the parent, so we need a wrapper.
......@@ -69,7 +70,7 @@ public function formElement(array $items, $delta, array $element, $langcode, arr
$element['#element_validate'][] = 'datetime_datetime_widget_validate';
// Identify the type of date and time elements to use.
switch ($field['settings']['datetime_type']) {
switch ($this->getFieldSetting('datetime_type')) {
case 'date':
$date_type = 'date';
$time_type = 'none';
......@@ -104,7 +105,7 @@ public function formElement(array $items, $delta, array $element, $langcode, arr
);
// Set the storage and widget options so the validation can use them. The
// validator will not have access to field or instance settings.
// validator will not have access to the field definition.
$element['value']['#date_element_format'] = $element_format;
$element['value']['#date_storage_format'] = $storage_format;
......@@ -113,7 +114,7 @@ public function formElement(array $items, $delta, array $element, $langcode, arr
// The date was created and verified during field_load(), so it is safe to
// use without further inspection.
$date->setTimezone(new \DateTimeZone($element['value']['#date_timezone']));
if ($field['settings']['datetime_type'] == 'date') {
if ($this->getFieldSetting('datetime_type') == 'date') {
// A date without time will pick up the current time, use the default
// time.
datetime_date_default_time($date);
......
......@@ -41,7 +41,7 @@ public function access(Route $route, Request $request) {
* Implements EntityFieldAccessCheckInterface::accessEditEntityField().
*/
public function accessEditEntityField(EntityInterface $entity, $field_name) {
return $entity->access('update') && field_access('edit', $field_name, $entity->entityType(), $entity);
return $entity->access('update') && ($field = field_info_field($field_name)) && field_access('edit', $field, $entity->entityType(), $entity);
}
/**
......
......@@ -45,7 +45,7 @@ function email_field_info_alter(&$info) {
/**
* Implements hook_field_is_empty().
*/
function email_field_is_empty($item, $field) {
function email_field_is_empty($item, $field_type) {
return !isset($item['value']) || $item['value'] === '';
}
......
......@@ -54,7 +54,7 @@ public function getFormatter($field_name) {
if ($configuration = $this->getComponent($field_name)) {
$instance = field_info_instance($this->targetEntityType, $field_name, $this->bundle);
$formatter = $this->pluginManager->getInstance(array(
'instance' => $instance,
'field_definition' => $instance,
'view_mode' => $this->originalMode,
// No need to prepare, defaults have been merged in setComponent().
'prepare' => FALSE,
......
......@@ -54,7 +54,7 @@ public function getWidget($field_name) {
if ($configuration = $this->getComponent($field_name)) {
$instance = field_info_instance($this->targetEntityType, $field_name, $this->bundle);
$widget = $this->pluginManager->getInstance(array(
'instance' => $instance,
'field_definition' => $instance,
'form_mode' => $this->originalMode,
// No need to prepare, defaults have been merged in setComponent().
'prepare' => FALSE,
......
......@@ -8,6 +8,7 @@
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Database\Query\AlterableInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\Field\FieldDefinitionInterface;
/**
* Implements hook_field_info().
......@@ -80,10 +81,9 @@ function entity_reference_field_widget_info_alter(&$info) {
*
* @return \Drupal\entity_reference\Plugin\Type\Selection\SelectionInterface
*/
function entity_reference_get_selection_handler($field, $instance, EntityInterface $entity = NULL) {
function entity_reference_get_selection_handler(FieldDefinitionInterface $field_definition, EntityInterface $entity = NULL) {
$options = array(
'field' => $field,
'instance' => $instance,
'field_definition' => $field_definition,
'entity' => $entity,
);
return Drupal::service('plugin.manager.entity_reference.selection')->getInstance($options);
......@@ -92,7 +92,7 @@ function entity_reference_get_selection_handler($field, $instance, EntityInterfa
/**
* Implements hook_field_is_empty().
*/
function entity_reference_field_is_empty($item, $field) {
function entity_reference_field_is_empty($item, $field_type) {
if (empty($item['target_id']) && !empty($item['entity']) && $item['entity']->isNew()) {
// Allow auto-create entities.
return FALSE;
......@@ -126,7 +126,7 @@ function entity_reference_field_validate(EntityInterface $entity = NULL, $field,
}
if ($ids) {
$valid_ids = entity_reference_get_selection_handler($field, $instance, $entity)->validateReferencableEntities(array_keys($ids));
$valid_ids = entity_reference_get_selection_handler($instance, $entity)->validateReferencableEntities(array_keys($ids));
$invalid_entities = array_diff_key($ids, array_flip($valid_ids));
if ($invalid_entities) {
......@@ -267,7 +267,7 @@ function entity_reference_field_instance_settings_form($field, $instance, $form_
'#attributes' => array('class' => array('entity_reference-settings')),
);
$handler = entity_reference_get_selection_handler($field, $instance);
$handler = entity_reference_get_selection_handler($instance);
$form['handler']['handler_settings'] += $handler->settingsForm($field, $instance);
return $form;
......@@ -363,13 +363,13 @@ function entity_reference_settings_ajax_submit($form, &$form_state) {
/**
* Implements hook_options_list().
*/
function entity_reference_options_list($field, $instance, $entity_type = NULL, $entity = NULL) {
if (!$options = entity_reference_get_selection_handler($field, $instance, $entity)->getReferencableEntities()) {
function entity_reference_options_list(FieldDefinitionInterface $field_definition, EntityInterface $entity) {
if (!$options = entity_reference_get_selection_handler($field_definition, $entity)->getReferencableEntities()) {
return array();
}
// Rebuild the array by changing the bundle key into the bundle label.
$target_type = $field['settings']['target_type'];
$target_type = $field_definition->getFieldSetting('target_type');
$bundles = entity_get_bundles($target_type);
$return = array();
......
......@@ -71,7 +71,7 @@ public function getMatches($field, $instance, $entity_type, $entity_id = '', $pr
throw new AccessDeniedHttpException();
}
}
$handler = entity_reference_get_selection_handler($field, $instance, $entity);
$handler = entity_reference_get_selection_handler($instance, $entity);
if (isset($string)) {
// Get an array of matching entities.
......
......@@ -46,7 +46,7 @@ public function getDerivativeDefinitions(array $base_plugin_definition) {
foreach (entity_get_info() as $entity_type => $info) {
if (!in_array($entity_type, $supported_entities)) {
$this->derivatives[$entity_type] = $base_plugin_definition;
$this->derivatives[$entity_type]['label'] = t('@enitty_type selection', array('@entity_type' => $info['label']));
$this->derivatives[$entity_type]['label'] = t('@entity_type selection', array('@entity_type' => $info['label']));
}
}
return $this->derivatives;
......
......@@ -14,14 +14,6 @@
*/
class SelectionBroken implements SelectionInterface {
/**
* Constructs a SelectionBroken object.
*/
public function __construct($field, $instance = NULL) {
$this->field = $field;
$this->instance = $instance;
}
/**
* Implements SelectionInterface::settingsForm().
*/
......
......@@ -42,7 +42,7 @@ public function createInstance($plugin_id, array $configuration = array()) {
return parent::createInstance($plugin_id, $configuration);
}
catch (PluginException $e) {
return new SelectionBroken($configuration['field'], $configuration['instance']);
return new SelectionBroken($configuration['field_definition']);
}
}
......@@ -50,8 +50,8 @@ public function createInstance($plugin_id, array $configuration = array()) {
* Overrides \Drupal\Component\Plugin\PluginManagerBase::getInstance().
*/
public function getInstance(array $options) {
$selection_handler = $options['instance']['settings']['handler'];
$target_entity_type = $options['field']['settings']['target_type'];
$selection_handler = $options['field_definition']->getFieldSetting('handler');
$target_entity_type = $options['field_definition']->getFieldSetting('target_type');
// Get all available selection plugins for this entity type.
$selection_handler_groups = $this->getSelectionGroups($target_entity_type);
......
......@@ -12,6 +12,7 @@
use Drupal\Core\Database\Query\AlterableInterface;
use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\Field\FieldDefinitionInterface;
use Drupal\Component\Utility\NestedArray;
use Drupal\entity_reference\Plugin\Type\Selection\SelectionInterface;
......@@ -30,18 +31,11 @@
class SelectionBase implements SelectionInterface {
/**
* The field array.
* The field definition.
*
* @var array
* @var \Drupal\Core\Entity\Field\FieldDefinitionInterface
*/
protected $field;
/**
* The instance array.
*
* @var array
*/
protected $instance;
protected $fieldDefinition;
/**
* The entity object, or NULL
......@@ -53,9 +47,8 @@ class SelectionBase implements SelectionInterface {
/**
* Constructs a SelectionBase object.
*/
public function __construct($field, $instance, EntityInterface $entity = NULL) {
$this->field = $field;
$this->instance = $instance;
public function __construct(FieldDefinitionInterface $field_definition, EntityInterface $entity = NULL) {
$this->fieldDefinition = $field_definition;
$this->entity = $entity;
}
......@@ -165,7 +158,7 @@ public static function settingsForm(&$field, &$instance) {
* Implements SelectionInterface::getReferencableEntities().
*/
public function getReferencableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
$target_type = $this->field['settings']['target_type'];
$target_type = $this->fieldDefinition->getFieldSetting('target_type');
$query = $this->buildEntityQuery($match, $match_operator);
if ($limit > 0) {
......@@ -204,7 +197,7 @@ public function countReferencableEntities($match = NULL, $match_operator = 'CONT
public function validateReferencableEntities(array $ids) {
$result = array();
if ($ids) {
$target_type = $this->field['settings']['target_type'];
$target_type = $this->fieldDefinition->getFieldSetting('target_type');
$entity_info = entity_get_info($target_type);
$query = $this->buildEntityQuery();
$result = $query
......@@ -264,7 +257,7 @@ public function validateAutocompleteInput($input, &$element, &$form_state, $form
* it.
*/
public function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') {
$target_type = $this->field['settings']['target_type'];
$target_type = $this->fieldDefinition->getFieldSetting('target_type');
$entity_info = entity_get_info($target_type);
$query = \Drupal::entityQuery($target_type);
......@@ -277,17 +270,18 @@ public function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') {
}
// Add entity-access tag.
$query->addTag($this->field['settings']['target_type'] . '_access');
$query->addTag($this->fieldDefinition->getFieldSetting('target_type') . '_access');
// Add the Selection handler for
// entity_reference_query_entity_reference_alter().
$query->addTag('entity_reference');
$query->addMetaData('field', $this->field);
$query->addMetaData('field_definition', $this->fieldDefinition);
$query->addMetaData('entity_reference_selection_handler', $this);
// Add the sort option.
if (!empty($this->instance['settings']['handler_settings']['sort'])) {
$sort_settings = $this->instance['settings']['handler_settings']['sort'];
$handler_settings = $this->fieldDefinition->getFieldSetting('handler_settings');
if (!empty($handler_settings['sort'])) {
$sort_settings = $handler_settings['sort'];
if ($sort_settings['field'] != '_none') {
$query->sort($sort_settings['field'], $sort_settings['direction']);
}
......
......@@ -36,7 +36,7 @@ class EntityReferenceEntityFormatter extends EntityReferenceFormatterBase {
* {@inheritdoc}
*/
public function settingsForm(array $form, array &$form_state) {
$view_modes = entity_get_view_modes($this->field['settings']['target_type']);
$view_modes = entity_get_view_modes($this->getFieldSetting('target_type'));
$options = array();
foreach ($view_modes as $view_mode => $view_mode_settings) {
$options[$view_mode] = $view_mode_settings['label'];
......@@ -65,7 +65,7 @@ public function settingsForm(array $form, array &$form_state) {
public function settingsSummary() {
$summary = array();
$view_modes = entity_get_view_modes($this->field['settings']['target_type']);
$view_modes = entity_get_view_modes($this->getFieldSetting('target_type'));
$view_mode = $this->getSetting('view_mode');
$summary[] = t('Rendered as @mode', array('@mode' => isset($view_modes[$view_mode]['label']) ? $view_modes[$view_mode]['label'] : $view_mode));
$summary[] = $this->getSetting('links') ? t('Display links') : t('Do not display links');
......@@ -83,7 +83,7 @@ public function viewElements(EntityInterface $entity, $langcode, array $items) {
$view_mode = $this->getSetting('view_mode');
$links = $this->getSetting('links');
$target_type = $this->field['settings']['target_type'];
$target_type = $this->getFieldSetting('target_type');
$elements = array();
......
......@@ -34,14 +34,14 @@
class AutocompleteTagsWidget extends AutocompleteWidgetBase {
/**
* Overrides \Drupal\entity_reference\Plugin\field\widget\AutocompleteWidgetBase::elementValidate()
* {@inheritdoc}