View source
<?php
namespace Drupal\select2\Plugin\Field\FieldWidget;
use Drupal\Core\Entity\EntityReferenceSelection\SelectionWithAutocreateInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\OptGroup;
use Drupal\select2\Select2Trait;
use Drupal\user\EntityOwnerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class Select2EntityReferenceWidget extends Select2Widget {
use Select2Trait;
protected $entityTypeManager;
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
$widget = parent::create($container, $configuration, $plugin_id, $plugin_definition);
$widget
->setEntityTypeManager($container
->get('entity_type.manager'));
return $widget;
}
protected function setEntityTypeManager(EntityTypeManagerInterface $entityTypeManager) {
$this->entityTypeManager = $entityTypeManager;
}
public static function defaultSettings() {
return [
'autocomplete' => FALSE,
'match_operator' => 'CONTAINS',
'match_limit' => 10,
] + parent::defaultSettings();
}
public function settingsForm(array $form, FormStateInterface $form_state) {
$element = parent::settingsForm($form, $form_state);
$element['autocomplete'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Autocomplete'),
'#default_value' => $this
->getSetting('autocomplete'),
'#description' => $this
->t('Options will be lazy loaded. This is recommended for lists with a lot of values.'),
];
$element['match_operator'] = [
'#type' => 'radios',
'#title' => $this
->t('Autocomplete matching'),
'#default_value' => $this
->getSetting('match_operator'),
'#options' => $this
->getMatchOperatorOptions(),
'#description' => $this
->t('Select the method used to collect autocomplete suggestions. Note that <em>Contains</em> can cause performance issues on sites with thousands of entities.'),
'#states' => [
'visible' => [
':input[name$="[settings_edit_form][settings][autocomplete]"]' => [
'checked' => TRUE,
],
],
],
];
$element['match_limit'] = [
'#type' => 'number',
'#title' => $this
->t('Number of results'),
'#default_value' => $this
->getSetting('match_limit'),
'#min' => 0,
'#description' => $this
->t('The number of suggestions that will be listed. Use <em>0</em> to remove the limit.'),
'#states' => [
'visible' => [
':input[name$="[settings_edit_form][settings][autocomplete]"]' => [
'checked' => TRUE,
],
],
],
];
return $element;
}
protected function getOptions(FieldableEntityInterface $entity) {
if (!isset($this->options) && $this
->getSetting('autocomplete')) {
$selected_options = [];
foreach ($entity
->get($this->fieldDefinition
->getName()) as $item) {
if ($item->{$this->column} !== NULL) {
$selected_options[] = $item->{$this->column};
}
}
if (!$selected_options) {
return $this->options = [];
}
$handler_settings = $this
->getSelectionSettings() + [
'target_type' => $this
->getFieldSetting('target_type'),
'handler' => $this
->getFieldSetting('handler'),
];
return $this->options = static::getValidReferenceableEntities($selected_options, $handler_settings);
}
return parent::getOptions($entity);
}
public function settingsSummary() {
$summary = parent::settingsSummary();
$autocomplete = $this
->getSetting('autocomplete');
$operators = $this
->getMatchOperatorOptions();
$summary[] = $this
->t('Autocomplete: @autocomplete', [
'@autocomplete' => $autocomplete ? $this
->t('On') : $this
->t('Off'),
]);
if ($autocomplete) {
$summary[] = $this
->t('Autocomplete matching: @match_operator', [
'@match_operator' => $operators[$this
->getSetting('match_operator')],
]);
$size = $this
->getSetting('match_limit') ?: $this
->t('unlimited');
$summary[] = $this
->t('Autocomplete suggestion list size: @size', [
'@size' => $size,
]);
}
return $summary;
}
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$element = parent::formElement($items, $delta, $element, $form, $form_state);
$element['#target_type'] = $this
->getFieldSetting('target_type');
$element['#selection_handler'] = $this
->getFieldSetting('handler');
$element['#selection_settings'] = $this
->getSelectionSettings();
$element['#autocomplete'] = $this
->getSetting('autocomplete');
if ($this
->getSelectionHandlerSetting('auto_create') && ($bundle = $this
->getAutocreateBundle())) {
$entity = $items
->getEntity();
$element['#autocreate'] = [
'bundle' => $bundle,
'uid' => $entity instanceof EntityOwnerInterface ? $entity
->getOwnerId() : \Drupal::currentUser()
->id(),
];
}
$element['#multiple'] = $this->multiple && (count($this->options) > 1 || isset($element['#autocreate']) || $element['#autocomplete']);
if ($element['#autocomplete'] && $element['#multiple']) {
$entity_definition = $this->entityTypeManager
->getDefinition($element['#target_type']);
$message = $this
->t("Drag to re-order @entity_types.", [
'@entity_types' => $entity_definition
->getPluralLabel(),
]);
if (!empty($element['#description'])) {
$element['#description'] = [
'#theme' => 'item_list',
'#items' => [
$element['#description'],
$message,
],
];
}
else {
$element['#description'] = $message;
}
}
return $element;
}
protected function getSelectionSettings() {
$label_field = $this->entityTypeManager
->getDefinition($this
->getFieldSetting('target_type'))
->getKey('label') ?: '_none';
return [
'match_operator' => $this
->getSetting('match_operator'),
'match_limit' => $this
->getSetting('match_limit'),
'sort' => [
'field' => $label_field,
],
] + $this
->getFieldSetting('handler_settings');
}
protected static function prepareFieldValues(array $values, array $element) {
if (empty($element['#autocreate'])) {
return parent::prepareFieldValues($values, $element);
}
$handler_settings = $element['#selection_settings'] + [
'target_type' => $element['#target_type'],
'handler' => $element['#selection_handler'],
];
$handler = \Drupal::service('plugin.manager.entity_reference_selection')
->getInstance($handler_settings);
$options = empty($element['#options']) ? [] : static::getValidReferenceableEntities(array_keys(OptGroup::flattenOptions($element['#options'])), $handler_settings);
$items = [];
foreach ($values as $value) {
if (isset($options[$value])) {
$items[] = [
$element['#key_column'] => $value,
];
}
else {
if ($handler instanceof SelectionWithAutocreateInterface) {
$label = substr($value, 4);
$items[] = [
'entity' => $handler
->createNewEntity($element['#target_type'], $element['#autocreate']['bundle'], $label, $element['#autocreate']['uid']),
];
}
}
}
return $items;
}
protected function getAutocreateBundle() {
$bundle = NULL;
if ($this
->getSelectionHandlerSetting('auto_create')) {
if ($bundle = $this
->getSelectionHandlerSetting('auto_create_bundle')) {
return $bundle;
}
$target_bundles = $this
->getSelectionHandlerSetting('target_bundles');
if (empty($target_bundles)) {
$bundle = $this
->getFieldSetting('target_type');
}
elseif (count($target_bundles) == 1) {
$bundle = reset($target_bundles);
}
else {
trigger_error(sprintf("The 'Create referenced entities if they don't already exist' option is enabled but a specific destination bundle is not set. You should re-visit and fix the settings of the '%s' (%s) field.", $this->fieldDefinition
->getLabel(), $this->fieldDefinition
->getName()), E_USER_WARNING);
}
}
return $bundle;
}
protected function getSelectionHandlerSetting($setting_name) {
$settings = $this
->getFieldSetting('handler_settings');
return isset($settings[$setting_name]) ? $settings[$setting_name] : NULL;
}
protected function getMatchOperatorOptions() {
return [
'STARTS_WITH' => $this
->t('Starts with'),
'CONTAINS' => $this
->t('Contains'),
];
}
}