View source
<?php
namespace Drupal\select2\Element;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Entity\Element\EntityAutocomplete;
use Drupal\Core\Form\FormState;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\OptGroup;
use Drupal\Core\Render\Element\Select;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
use Drupal\select2\Select2Trait;
class Select2 extends Select {
use Select2Trait;
public function getInfo() {
$info = parent::getInfo();
$class = get_class($this);
$info['#target_type'] = NULL;
$info['#selection_handler'] = 'default';
$info['#selection_settings'] = [];
$info['#autocomplete'] = FALSE;
$info['#autocreate'] = [];
$info['#empty_value'] = '';
$info['#cardinality'] = 0;
$info['#pre_render'][] = [
$class,
'preRenderAutocomplete',
];
$info['#pre_render'][] = [
$class,
'preRenderOverwrites',
];
$info['#element_validate'][] = [
$class,
'validateEntityAutocomplete',
];
$info['#select2'] = [];
return $info;
}
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
if ($input !== FALSE && is_array($input)) {
$input = array_map(function ($item) {
return isset($item['target_id']) ? $item['target_id'] : $item;
}, $input);
return array_combine($input, $input);
}
return parent::valueCallback($element, $input, $form_state);
}
public static function validateEntityAutocomplete(array &$element, FormStateInterface $form_state, array &$complete_form) {
if ($element['#target_type'] && !$element['#autocreate']) {
$value_callable = isset($element['#autocomplete_options_callback']) ? $element['#autocomplete_options_callback'] : NULL;
if (!$value_callable || !is_callable($value_callable)) {
$value_callable = '\\Drupal\\select2\\Element\\Select2::getValidSelectedOptions';
}
$value = [];
$input_values = call_user_func_array($value_callable, [
$element,
$form_state,
]);
foreach ($input_values as $id => $input) {
$value[] = [
'target_id' => $id,
];
}
$form_state
->setValueForElement($element, $value);
}
}
public static function processSelect(&$element, FormStateInterface $form_state, &$complete_form) {
if ($element['#autocomplete']) {
$value_callable = isset($element['#autocomplete_options_callback']) ? $element['#autocomplete_options_callback'] : NULL;
if (!$value_callable || !is_callable($value_callable)) {
$value_callable = '\\Drupal\\select2\\Element\\Select2::getValidSelectedOptions';
}
$element['#options'] = call_user_func_array($value_callable, [
$element,
$form_state,
]);
}
if ($element['#autocreate'] && $element['#target_type']) {
unset($element['#needs_validation']);
$values = is_array($element['#value']) ? $element['#value'] : [
$element['#value'],
];
foreach ($values as $key => $value) {
if (is_string($key) && substr($key, 0, 4) === "\$ID:") {
$element['#options'][$key] = substr($value, 0, 4) === "\$ID:" ? substr($value, 4) : $value;
}
elseif (!$element['#multiple'] && substr($value, 0, 4) === "\$ID:") {
$element['#options'][$value] = substr($value, 4);
}
}
}
if (!$element['#multiple'] && !isset($element['#options'][$element['#empty_value']])) {
$empty_option = [
$element['#empty_value'] => '',
];
$element['#options'] = $empty_option + $element['#options'];
}
$element['#type'] = 'select';
return $element;
}
protected static function getValidSelectedOptions(array $element, FormStateInterface $form_state) {
$handler_settings = $element['#selection_settings'] + [
'target_type' => $element['#target_type'],
'handler' => $element['#selection_handler'],
];
$value = is_array($element['#value']) ? $element['#value'] : [
$element['#value'],
];
return $value ? static::getValidReferenceableEntities($value, $handler_settings) : [];
}
public static function preRenderSelect($element) {
$element = parent::preRenderSelect($element);
$required = isset($element['#states']['required']) ? TRUE : $element['#required'];
$multiple = $element['#multiple'];
if ($multiple) {
$element['#attributes']['multiple'] = 'multiple';
$element['#attributes']['name'] = $element['#name'] . '[]';
}
$current_language = \Drupal::languageManager()
->getCurrentLanguage();
$current_theme = \Drupal::theme()
->getActiveTheme()
->getName();
$select2_theme_exists = \Drupal::service('library.discovery')
->getLibraryByName($current_theme, 'select2.theme');
$placeholder_text = $required ? new TranslatableMarkup('- Select -') : new TranslatableMarkup('- None -');
$placeholder = [
'id' => '',
'text' => $placeholder_text,
];
if (!empty($element['#empty_value'])) {
$placeholder['id'] = $element['#empty_value'];
}
if (!empty($element['#placeholder'])) {
$placeholder['text'] = $element['#placeholder'];
}
elseif (!empty($element['#empty_option'])) {
$placeholder['text'] = $element['#empty_option'];
}
$settings = [
'multiple' => $multiple,
'placeholder' => $placeholder,
'allowClear' => !$multiple && !$required,
'dir' => $current_language
->getDirection(),
'language' => $current_language
->getId(),
'tags' => (bool) $element['#autocreate'],
'theme' => $select2_theme_exists ? $current_theme : 'default',
'maximumSelectionLength' => $multiple ? $element['#cardinality'] : 0,
'tokenSeparators' => $element['#autocreate'] ? [
',',
] : [],
'selectOnClose' => $element['#autocomplete'],
'width' => '100%',
];
$element['#attributes']['class'][] = 'select2-widget';
$element['#attributes']['data-select2-config'] = $settings;
$element['#attached']['library'][] = 'select2/select2';
$element['#attached']['library'][] = 'select2/select2.i18n.' . $current_language
->getId();
if ($select2_theme_exists) {
$element['#attached']['library'][] = $current_theme . '/select2.theme';
}
return $element;
}
public static function preRenderAutocomplete($element) {
if (!$element['#autocomplete']) {
return $element;
}
$value_callable = isset($element['#autocomplete_route_callback']) ? $element['#autocomplete_route_callback'] : NULL;
if (!$value_callable || !is_callable($value_callable)) {
$value_callable = '\\Drupal\\select2\\Element\\Select2::setAutocompleteRouteParameters';
}
$element = call_user_func_array($value_callable, [
&$element,
]);
$options = OptGroup::flattenOptions($element['#options']);
$values = isset($element['#value']) ? $element['#value'] : $element['#default_value'];
$values = is_array($values) ? $values : [
$values,
];
$element['#options'] = [];
foreach ($values as $value) {
if (isset($options[$value])) {
$element['#options'][$value] = $options[$value];
}
}
$access_manager = \Drupal::service('access_manager');
$access = $access_manager
->checkNamedRoute($element['#autocomplete_route_name'], $element['#autocomplete_route_parameters'], \Drupal::currentUser(), TRUE);
if ($access && $access
->isAllowed()) {
$url = Url::fromRoute($element['#autocomplete_route_name'], $element['#autocomplete_route_parameters'])
->toString(TRUE);
$element['#attributes']['data-select2-config'] += [
'minimumInputLength' => 1,
'ajax' => [
'delay' => 250,
'url' => $url
->getGeneratedUrl(),
],
];
}
return $element;
}
protected static function setAutocompleteRouteParameters(array &$element) {
$complete_form = [];
$element = EntityAutocomplete::processEntityAutocomplete($element, new FormState(), $complete_form);
$element['#autocomplete_route_name'] = 'select2.entity_autocomplete';
return $element;
}
public static function preRenderOverwrites($element) {
if (!$element['#multiple']) {
$empty_option = [
$element['#empty_value'] => '',
];
$element['#options'] = $empty_option + $element['#options'];
}
foreach ($element["#select2"] as $key => $value) {
$element['#attributes']['data-select2-config'][$key] = $value;
}
$element['#attributes']['data-select2-config'] = Json::encode($element['#attributes']['data-select2-config']);
return $element;
}
}