3 namespace Drupal\Core\Field\Plugin\Field\FieldWidget;
5 use Drupal\Core\Field\FieldItemListInterface;
6 use Drupal\Core\Field\WidgetBase;
7 use Drupal\Core\Form\FormStateInterface;
8 use Drupal\user\EntityOwnerInterface;
9 use Symfony\Component\Validator\ConstraintViolationInterface;
12 * Plugin implementation of the 'entity_reference_autocomplete' widget.
15 * id = "entity_reference_autocomplete",
16 * label = @Translation("Autocomplete"),
17 * description = @Translation("An autocomplete text field."),
23 class EntityReferenceAutocompleteWidget extends WidgetBase {
28 public static function defaultSettings() {
30 'match_operator' => 'CONTAINS',
33 ] + parent::defaultSettings();
39 public function settingsForm(array $form, FormStateInterface $form_state) {
40 $element['match_operator'] = [
42 '#title' => t('Autocomplete matching'),
43 '#default_value' => $this->getSetting('match_operator'),
44 '#options' => $this->getMatchOperatorOptions(),
45 '#description' => t('Select the method used to collect autocomplete suggestions. Note that <em>Contains</em> can cause performance issues on sites with thousands of entities.'),
49 '#title' => t('Size of textfield'),
50 '#default_value' => $this->getSetting('size'),
54 $element['placeholder'] = [
55 '#type' => 'textfield',
56 '#title' => t('Placeholder'),
57 '#default_value' => $this->getSetting('placeholder'),
58 '#description' => t('Text that will be shown inside the field until a value is entered. This hint is usually a sample value or a brief description of the expected format.'),
66 public function settingsSummary() {
69 $operators = $this->getMatchOperatorOptions();
70 $summary[] = t('Autocomplete matching: @match_operator', ['@match_operator' => $operators[$this->getSetting('match_operator')]]);
71 $summary[] = t('Textfield size: @size', ['@size' => $this->getSetting('size')]);
72 $placeholder = $this->getSetting('placeholder');
73 if (!empty($placeholder)) {
74 $summary[] = t('Placeholder: @placeholder', ['@placeholder' => $placeholder]);
77 $summary[] = t('No placeholder');
86 public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
87 $entity = $items->getEntity();
88 $referenced_entities = $items->referencedEntities();
90 // Append the match operation to the selection settings.
91 $selection_settings = $this->getFieldSetting('handler_settings') + ['match_operator' => $this->getSetting('match_operator')];
94 '#type' => 'entity_autocomplete',
95 '#target_type' => $this->getFieldSetting('target_type'),
96 '#selection_handler' => $this->getFieldSetting('handler'),
97 '#selection_settings' => $selection_settings,
98 // Entity reference field items are handling validation themselves via
99 // the 'ValidReference' constraint.
100 '#validate_reference' => FALSE,
101 '#maxlength' => 1024,
102 '#default_value' => isset($referenced_entities[$delta]) ? $referenced_entities[$delta] : NULL,
103 '#size' => $this->getSetting('size'),
104 '#placeholder' => $this->getSetting('placeholder'),
107 if ($this->getSelectionHandlerSetting('auto_create') && ($bundle = $this->getAutocreateBundle())) {
108 $element['#autocreate'] = [
110 'uid' => ($entity instanceof EntityOwnerInterface) ? $entity->getOwnerId() : \Drupal::currentUser()->id(),
114 return ['target_id' => $element];
120 public function errorElement(array $element, ConstraintViolationInterface $error, array $form, FormStateInterface $form_state) {
121 return $element['target_id'];
127 public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
128 foreach ($values as $key => $value) {
129 // The entity_autocomplete form element returns an array when an entity
130 // was "autocreated", so we need to move it up a level.
131 if (is_array($value['target_id'])) {
132 unset($values[$key]['target_id']);
133 $values[$key] += $value['target_id'];
141 * Returns the name of the bundle which will be used for autocreated entities.
146 protected function getAutocreateBundle() {
148 if ($this->getSelectionHandlerSetting('auto_create') && $target_bundles = $this->getSelectionHandlerSetting('target_bundles')) {
149 // If there's only one target bundle, use it.
150 if (count($target_bundles) == 1) {
151 $bundle = reset($target_bundles);
153 // Otherwise use the target bundle stored in selection handler settings.
154 elseif (!$bundle = $this->getSelectionHandlerSetting('auto_create_bundle')) {
155 // If no bundle has been set as auto create target means that there is
156 // an inconsistency in entity reference field settings.
157 trigger_error(sprintf(
158 "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.",
159 $this->fieldDefinition->getLabel(),
160 $this->fieldDefinition->getName()
169 * Returns the value of a setting for the entity reference selection handler.
171 * @param string $setting_name
177 protected function getSelectionHandlerSetting($setting_name) {
178 $settings = $this->getFieldSetting('handler_settings');
179 return isset($settings[$setting_name]) ? $settings[$setting_name] : NULL;
183 * Returns the options for the match operator.
188 protected function getMatchOperatorOptions() {
190 'STARTS_WITH' => t('Starts with'),
191 'CONTAINS' => t('Contains'),