class EntityAutocomplete in Zircon Profile 8
Same name and namespace in other branches
- 8.0 core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php \Drupal\Core\Entity\Element\EntityAutocomplete
Provides an entity autocomplete form element.
The #default_value accepted by this element is either an entity object or an array of entity objects.
Plugin annotation
@FormElement("entity_autocomplete");
Hierarchy
- class \Drupal\Component\Plugin\PluginBase implements DerivativeInspectionInterface, PluginInspectionInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, StringTranslationTrait
- class \Drupal\Core\Render\Element\RenderElement implements ElementInterface
- class \Drupal\Core\Render\Element\FormElement implements FormElementInterface
- class \Drupal\Core\Render\Element\Textfield
- class \Drupal\Core\Entity\Element\EntityAutocomplete
- class \Drupal\Core\Render\Element\Textfield
- class \Drupal\Core\Render\Element\FormElement implements FormElementInterface
- class \Drupal\Core\Render\Element\RenderElement implements ElementInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, StringTranslationTrait
Expanded class hierarchy of EntityAutocomplete
4 files declare their use of EntityAutocomplete
- EntityAutocompleteElementFormTest.php in core/
modules/ system/ src/ Tests/ Entity/ Element/ EntityAutocompleteElementFormTest.php - Contains \Drupal\system\Tests\Entity\Element\EntityAutocompleteElementFormTest.
- LinkWidget.php in core/
modules/ link/ src/ Plugin/ Field/ FieldWidget/ LinkWidget.php - Contains \Drupal\link\Plugin\Field\FieldWidget\LinkWidget.
- Name.php in core/
modules/ user/ src/ Plugin/ views/ filter/ Name.php - Contains \Drupal\user\Plugin\views\filter\Name.
- TaxonomyIndexTid.php in core/
modules/ taxonomy/ src/ Plugin/ views/ filter/ TaxonomyIndexTid.php - Contains \Drupal\taxonomy\Plugin\views\filter\TaxonomyIndexTid.
7 #type uses of EntityAutocomplete
- AssignOwnerNode::buildConfigurationForm in core/
modules/ node/ src/ Plugin/ Action/ AssignOwnerNode.php - Form constructor.
- CommentForm::form in core/
modules/ comment/ src/ CommentForm.php - Gets the actual form array to be built.
- ContentTranslationHandler::entityFormAlter in core/
modules/ content_translation/ src/ ContentTranslationHandler.php - Performs the needed alterations to the entity form.
- EntityAutocompleteElementFormTest::buildForm in core/
modules/ system/ src/ Tests/ Entity/ Element/ EntityAutocompleteElementFormTest.php - Form constructor.
- EntityReferenceAutocompleteWidget::formElement in core/
lib/ Drupal/ Core/ Field/ Plugin/ Field/ FieldWidget/ EntityReferenceAutocompleteWidget.php - Returns the form for a single field widget.
File
- core/
lib/ Drupal/ Core/ Entity/ Element/ EntityAutocomplete.php, line 26 - Contains \Drupal\Core\Entity\Element\EntityAutocomplete.
Namespace
Drupal\Core\Entity\ElementView source
class EntityAutocomplete extends Textfield {
/**
* {@inheritdoc}
*/
public function getInfo() {
$info = parent::getInfo();
$class = get_class($this);
// Apply default form element properties.
$info['#target_type'] = NULL;
$info['#selection_handler'] = 'default';
$info['#selection_settings'] = array();
$info['#tags'] = FALSE;
$info['#autocreate'] = NULL;
// This should only be set to FALSE if proper validation by the selection
// handler is performed at another level on the extracted form values.
$info['#validate_reference'] = TRUE;
// IMPORTANT! This should only be set to FALSE if the #default_value
// property is processed at another level (e.g. by a Field API widget) and
// it's value is properly checked for access.
$info['#process_default_value'] = TRUE;
$info['#element_validate'] = array(
array(
$class,
'validateEntityAutocomplete',
),
);
array_unshift($info['#process'], array(
$class,
'processEntityAutocomplete',
));
return $info;
}
/**
* {@inheritdoc}
*/
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
// Process the #default_value property.
if ($input === FALSE && isset($element['#default_value']) && $element['#process_default_value']) {
if (is_array($element['#default_value']) && $element['#tags'] !== TRUE) {
throw new \InvalidArgumentException('The #default_value property is an array but the form element does not allow multiple values.');
}
elseif (!is_array($element['#default_value'])) {
// Convert the default value into an array for easier processing in
// static::getEntityLabels().
$element['#default_value'] = array(
$element['#default_value'],
);
}
if ($element['#default_value'] && !reset($element['#default_value']) instanceof EntityInterface) {
throw new \InvalidArgumentException('The #default_value property has to be an entity object or an array of entity objects.');
}
// Extract the labels from the passed-in entity objects, taking access
// checks into account.
return static::getEntityLabels($element['#default_value']);
}
}
/**
* Adds entity autocomplete functionality to a form element.
*
* @param array $element
* The form element to process. Properties used:
* - #target_type: The ID of the target entity type.
* - #selection_handler: The plugin ID of the entity reference selection
* handler.
* - #selection_settings: An array of settings that will be passed to the
* selection handler.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param array $complete_form
* The complete form structure.
*
* @return array
* The form element.
*
* @throws \InvalidArgumentException
* Exception thrown when the #target_type or #autocreate['bundle'] are
* missing.
*/
public static function processEntityAutocomplete(array &$element, FormStateInterface $form_state, array &$complete_form) {
// Nothing to do if there is no target entity type.
if (empty($element['#target_type'])) {
throw new \InvalidArgumentException('Missing required #target_type parameter.');
}
// Provide default values and sanity checks for the #autocreate parameter.
if ($element['#autocreate']) {
if (!isset($element['#autocreate']['bundle'])) {
throw new \InvalidArgumentException("Missing required #autocreate['bundle'] parameter.");
}
// Default the autocreate user ID to the current user.
$element['#autocreate']['uid'] = isset($element['#autocreate']['uid']) ? $element['#autocreate']['uid'] : \Drupal::currentUser()
->id();
}
// Store the selection settings in the key/value store and pass a hashed key
// in the route parameters.
$selection_settings = isset($element['#selection_settings']) ? $element['#selection_settings'] : [];
$data = serialize($selection_settings) . $element['#target_type'] . $element['#selection_handler'];
$selection_settings_key = Crypt::hmacBase64($data, Settings::getHashSalt());
$key_value_storage = \Drupal::keyValue('entity_autocomplete');
if (!$key_value_storage
->has($selection_settings_key)) {
$key_value_storage
->set($selection_settings_key, $selection_settings);
}
$element['#autocomplete_route_name'] = 'system.entity_autocomplete';
$element['#autocomplete_route_parameters'] = array(
'target_type' => $element['#target_type'],
'selection_handler' => $element['#selection_handler'],
'selection_settings_key' => $selection_settings_key,
);
return $element;
}
/**
* Form element validation handler for entity_autocomplete elements.
*/
public static function validateEntityAutocomplete(array &$element, FormStateInterface $form_state, array &$complete_form) {
$value = NULL;
if (!empty($element['#value'])) {
$options = array(
'target_type' => $element['#target_type'],
'handler' => $element['#selection_handler'],
'handler_settings' => $element['#selection_settings'],
);
$handler = \Drupal::service('plugin.manager.entity_reference_selection')
->getInstance($options);
$autocreate = (bool) $element['#autocreate'] && $handler instanceof SelectionWithAutocreateInterface;
$input_values = $element['#tags'] ? Tags::explode($element['#value']) : array(
$element['#value'],
);
foreach ($input_values as $input) {
$match = static::extractEntityIdFromAutocompleteInput($input);
if ($match === NULL) {
// Try to get a match from the input string when the user didn't use
// the autocomplete but filled in a value manually.
$match = static::matchEntityByTitle($handler, $input, $element, $form_state, !$autocreate);
}
if ($match !== NULL) {
$value[] = array(
'target_id' => $match,
);
}
elseif ($autocreate) {
// Auto-create item. See an example of how this is handled in
// \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::presave().
$value[] = array(
'entity' => $handler
->createNewEntity($element['#target_type'], $element['#autocreate']['bundle'], $input, $element['#autocreate']['uid']),
);
}
}
// Check that the referenced entities are valid, if needed.
if ($element['#validate_reference'] && !empty($value)) {
// Validate existing entities.
$ids = array_reduce($value, function ($return, $item) {
if (isset($item['target_id'])) {
$return[] = $item['target_id'];
}
return $return;
});
if ($ids) {
$valid_ids = $handler
->validateReferenceableEntities($ids);
if ($invalid_ids = array_diff($ids, $valid_ids)) {
foreach ($invalid_ids as $invalid_id) {
$form_state
->setError($element, t('The referenced entity (%type: %id) does not exist.', array(
'%type' => $element['#target_type'],
'%id' => $invalid_id,
)));
}
}
}
// Validate newly created entities.
$new_entities = array_reduce($value, function ($return, $item) {
if (isset($item['entity'])) {
$return[] = $item['entity'];
}
return $return;
});
if ($new_entities) {
if ($autocreate) {
$valid_new_entities = $handler
->validateReferenceableNewEntities($new_entities);
$invalid_new_entities = array_diff_key($new_entities, $valid_new_entities);
}
else {
// If the selection handler does not support referencing newly
// created entities, all of them should be invalidated.
$invalid_new_entities = $new_entities;
}
foreach ($invalid_new_entities as $entity) {
$form_state
->setError($element, t('This entity (%type: %label) cannot be referenced.', array(
'%type' => $element['#target_type'],
'%label' => $entity
->label(),
)));
}
}
}
// Use only the last value if the form element does not support multiple
// matches (tags).
if (!$element['#tags'] && !empty($value)) {
$last_value = $value[count($value) - 1];
$value = isset($last_value['target_id']) ? $last_value['target_id'] : $last_value;
}
}
$form_state
->setValueForElement($element, $value);
}
/**
* Finds an entity from an autocomplete input without an explicit ID.
*
* The method will return an entity ID if one single entity unambuguously
* matches the incoming input, and sill assign form errors otherwise.
*
* @param string $input
* Single string from autocomplete element.
* @param array $element
* The form element to set a form error.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current form state.
* @param bool $strict
* Whether to trigger a form error if an element from $input (eg. an entity)
* is not found.
*
* @return integer|null
* Value of a matching entity ID, or NULL if none.
*/
protected static function matchEntityByTitle($handler, $input, &$element, FormStateInterface $form_state, $strict) {
$entities_by_bundle = $handler
->getReferenceableEntities($input, '=', 6);
$entities = array_reduce($entities_by_bundle, function ($flattened, $bundle_entities) {
return $flattened + $bundle_entities;
}, []);
$params = array(
'%value' => $input,
'@value' => $input,
);
if (empty($entities)) {
if ($strict) {
// Error if there are no entities available for a required field.
$form_state
->setError($element, t('There are no entities matching "%value".', $params));
}
}
elseif (count($entities) > 5) {
$params['@id'] = key($entities);
// Error if there are more than 5 matching entities.
$form_state
->setError($element, t('Many entities are called %value. Specify the one you want by appending the id in parentheses, like "@value (@id)".', $params));
}
elseif (count($entities) > 1) {
// More helpful error if there are only a few matching entities.
$multiples = array();
foreach ($entities as $id => $name) {
$multiples[] = $name . ' (' . $id . ')';
}
$params['@id'] = $id;
$form_state
->setError($element, t('Multiple entities match this reference; "%multiple". Specify the one you want by appending the id in parentheses, like "@value (@id)".', array(
'%multiple' => implode('", "', $multiples),
)));
}
else {
// Take the one and only matching entity.
return key($entities);
}
}
/**
* Converts an array of entity objects into a string of entity labels.
*
* This method is also responsible for checking the 'view' access on the
* passed-in entities.
*
* @param \Drupal\Core\Entity\EntityInterface[] $entities
* An array of entity objects.
*
* @return string
* A string of entity labels separated by commas.
*/
public static function getEntityLabels(array $entities) {
$entity_labels = array();
foreach ($entities as $entity) {
$label = $entity
->access('view') ? $entity
->label() : t('- Restricted access -');
// Take into account "autocreated" entities.
if (!$entity
->isNew()) {
$label .= ' (' . $entity
->id() . ')';
}
// Labels containing commas or quotes must be wrapped in quotes.
$entity_labels[] = Tags::encode($label);
}
return implode(', ', $entity_labels);
}
/**
* Extracts the entity ID from the autocompletion result.
*
* @param string $input
* The input coming from the autocompletion result.
*
* @return mixed|null
* An entity ID or NULL if the input does not contain one.
*/
public static function extractEntityIdFromAutocompleteInput($input) {
$match = NULL;
// Take "label (entity id)', match the ID from parenthesis when it's a
// number.
if (preg_match("/.+\\((\\d+)\\)/", $input, $matches)) {
$match = $matches[1];
}
elseif (preg_match("/.+\\(([\\w.]+)\\)/", $input, $matches)) {
$match = $matches[1];
}
return $match;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
DependencySerializationTrait:: |
protected | property | An array of service IDs keyed by property name used for serialization. | |
DependencySerializationTrait:: |
public | function | 1 | |
DependencySerializationTrait:: |
public | function | 2 | |
EntityAutocomplete:: |
public static | function | Extracts the entity ID from the autocompletion result. | |
EntityAutocomplete:: |
public static | function | Converts an array of entity objects into a string of entity labels. | |
EntityAutocomplete:: |
public | function |
Returns the element properties for this element. Overrides Textfield:: |
|
EntityAutocomplete:: |
protected static | function | Finds an entity from an autocomplete input without an explicit ID. | |
EntityAutocomplete:: |
public static | function | Adds entity autocomplete functionality to a form element. | |
EntityAutocomplete:: |
public static | function | Form element validation handler for entity_autocomplete elements. | |
EntityAutocomplete:: |
public static | function |
Determines how user input is mapped to an element's #value property. Overrides Textfield:: |
|
FormElement:: |
public static | function | Adds autocomplete functionality to elements. | |
FormElement:: |
public static | function | #process callback for #pattern form element property. | |
FormElement:: |
public static | function | #element_validate callback for #pattern form element property. | |
PluginBase:: |
protected | property | Configuration information passed into the plugin. | 2 |
PluginBase:: |
protected | property | The plugin implementation definition. | |
PluginBase:: |
protected | property | The plugin_id. | |
PluginBase:: |
constant | A string which is used to separate base plugin IDs from the derivative ID. | ||
PluginBase:: |
public | function |
Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the definition of the plugin implementation. Overrides PluginInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface:: |
|
PluginBase:: |
public | function | Constructs a Drupal\Component\Plugin\PluginBase object. | 69 |
RenderElement:: |
public static | function | Adds Ajax information about an element to communicate with JavaScript. | |
RenderElement:: |
public static | function | Adds members of this group as actual elements for rendering. | |
RenderElement:: |
public static | function | Form element processing handler for the #ajax form property. | |
RenderElement:: |
public static | function | Arranges elements into groups. | |
RenderElement:: |
public static | function |
Sets a form element's class attribute. Overrides ElementInterface:: |
|
StringTranslationTrait:: |
protected | property | The string translation service. | |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. | |
Textfield:: |
public static | function | Prepares a #type 'textfield' render element for input.html.twig. |