pluginreference-2.0.0/src/Element/PluginAutocomplete.php
src/Element/PluginAutocomplete.php
<?php
namespace Drupal\pluginreference\Element;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Component\Utility\Crypt;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\PluginBase;
use Drupal\Core\Render\Element\Textfield;
use Drupal\Core\Site\Settings;
/**
* Provides a plugin autocomplete form element.
*
* The #default_value accepted by this element is a plugin definition array.
*
* @FormElement("plugin_autocomplete")
*/
class PluginAutocomplete 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'] = [];
$info['#validate_reference'] = TRUE;
$info['#element_validate'] = [
[
$class,
'validatePluginAutocomplete',
],
];
array_unshift($info['#process'], [
$class,
'processPluginAutocomplete',
]);
return $info;
}
/**
* {@inheritdoc}
*/
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
// Process the #default_value property.
if ($input === FALSE && isset($element['#default_value'])) {
if (is_array($element['#default_value']) && isset($element['#default_value']['label'])) {
$name = $element['#default_value']['label'] ?? $element['#default_value']['admin_label'] ?? $element['#default_value']['id'];
return $name . ' (' . $element['#default_value']['id'] . ')';
}
elseif ($element['#default_value'] instanceof PluginBase) {
$plugin_definition = $element['#default_value']->getPluginDefinition();
$name = $plugin_definition['label'] ?? $plugin_definition['admin_label'] ?? $element['#default_value']->getPluginId();
return $name . ' (' . $element['#default_value']->getPluginId() . ')';
}
elseif (is_string($element['#default_value'])) {
$plugin_manager = \Drupal::service('plugin_reference.plugin_type_helper')->getPluginManager($element['#target_type']);
if ($plugin_manager instanceof PluginManagerInterface && $plugin_manager->hasDefinition($element['#default_value'])) {
$plugin_definition = $plugin_manager->getDefinition($element['#default_value']);
$name = $plugin_definition['label'] ?? $plugin_definition['admin_label'] ?? $plugin_definition['id'];
return $name . ' (' . $plugin_definition['id'] . ')';
}
}
}
return NULL;
}
/**
* Adds plugin 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.
* @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 is missing.
*/
public static function processPluginAutocomplete(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.');
}
// Store the selection settings in the key/value store and pass a hashed key
// in the route parameters.
$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('plugin_autocomplete');
if (!$key_value_storage->has($selection_settings_key)) {
$key_value_storage->set($selection_settings_key, $selection_settings);
}
$element['#autocomplete_route_name'] = 'pluginreference.plugin_autocomplete';
$element['#autocomplete_route_parameters'] = [
'target_type' => $element['#target_type'],
'selection_handler' => $element['#selection_handler'],
'selection_settings_key' => $selection_settings_key,
];
return $element;
}
/**
* Form element validation handler for plugin_autocomplete elements.
*/
public static function validatePluginAutocomplete(array &$element, FormStateInterface $form_state, array &$complete_form): void {
$value = NULL;
if (!empty($element['#value']) && $match = static::extractPluginIdFromAutocompleteInput($element['#value'])) {
$options = $element['#selection_settings'] + [
'target_type' => $element['#target_type'],
'handler' => $element['#selection_handler'],
];
$handler = \Drupal::service('plugin.manager.plugin_reference_selection')->getInstance($options);
if ($element['#validate_reference'] === TRUE && $handler->validateReferenceablePlugins([$match])) {
$value = $match;
}
elseif ($element['#validate_reference'] === FALSE) {
$value = $match;
}
}
$form_state->setValueForElement($element, $value);
}
/**
* Helper function to retrieve the plugin ID from the element value.
*/
public static function extractPluginIdFromAutocompleteInput(string $input): ?string {
$match = NULL;
// Take "label (plugin id)', match the ID from parenthesis when it's a
// number.
if (preg_match("/.+\s\((\d+)\)/", $input, $matches)) {
$match = $matches[1];
}
// Match the ID when it's a string .
elseif (preg_match("/.+\s\(([\w.]+)\)/", $input, $matches)) {
$match = $matches[1];
}
return $match;
}
}
