Yaffs site version 1.1
[yaffs-website] / web / modules / contrib / entity_browser / src / Plugin / Field / FieldWidget / EntityReferenceBrowserWidget.php
1 <?php
2
3 namespace Drupal\entity_browser\Plugin\Field\FieldWidget;
4
5 use Drupal\Core\Entity\EntityInterface;
6 use Drupal\entity_browser\Element\EntityBrowserElement;
7 use Symfony\Component\Validator\ConstraintViolationInterface;
8 use Drupal\Component\Utility\Html;
9 use Drupal\Component\Utility\NestedArray;
10 use Drupal\Core\Entity\ContentEntityInterface;
11 use Drupal\Core\Entity\EntityTypeManagerInterface;
12 use Drupal\Core\Field\FieldDefinitionInterface;
13 use Drupal\Core\Field\FieldItemListInterface;
14 use Drupal\Core\Field\WidgetBase;
15 use Drupal\Core\Form\FormStateInterface;
16 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
17 use Drupal\Core\Url;
18 use Drupal\Core\Validation\Plugin\Validation\Constraint\NotNullConstraint;
19 use Drupal\entity_browser\FieldWidgetDisplayManager;
20 use Symfony\Component\DependencyInjection\ContainerInterface;
21 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
22 use Symfony\Component\Validator\ConstraintViolation;
23 use Symfony\Component\Validator\ConstraintViolationListInterface;
24 use Drupal\Core\Extension\ModuleHandlerInterface;
25
26 /**
27  * Plugin implementation of the 'entity_reference' widget for entity browser.
28  *
29  * @FieldWidget(
30  *   id = "entity_browser_entity_reference",
31  *   label = @Translation("Entity browser"),
32  *   description = @Translation("Uses entity browser to select entities."),
33  *   multiple_values = TRUE,
34  *   field_types = {
35  *     "entity_reference"
36  *   }
37  * )
38  */
39 class EntityReferenceBrowserWidget extends WidgetBase implements ContainerFactoryPluginInterface {
40
41   /**
42    * Entity type manager service.
43    *
44    * @var \Drupal\Core\Entity\EntityTypeManagerInterface
45    */
46   protected $entityTypeManager;
47
48   /**
49    * Field widget display plugin manager.
50    *
51    * @var \Drupal\entity_browser\FieldWidgetDisplayManager
52    */
53   protected $fieldDisplayManager;
54
55   /**
56    * The depth of the delete button.
57    *
58    * This property exists so it can be changed if subclasses.
59    *
60    * @var int
61    */
62   protected static $deleteDepth = 4;
63
64   /**
65    * The module handler interface.
66    *
67    * @var \Drupal\Core\Extension\ModuleHandlerInterface
68    */
69   protected $moduleHandler;
70
71   /**
72    * Constructs widget plugin.
73    *
74    * @param string $plugin_id
75    *   The plugin_id for the plugin instance.
76    * @param mixed $plugin_definition
77    *   The plugin implementation definition.
78    * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
79    *   The definition of the field to which the widget is associated.
80    * @param array $settings
81    *   The widget settings.
82    * @param array $third_party_settings
83    *   Any third party settings.
84    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
85    *   Entity type manager service.
86    * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
87    *   Event dispatcher.
88    * @param \Drupal\entity_browser\FieldWidgetDisplayManager $field_display_manager
89    *   Field widget display plugin manager.
90    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
91    *   The module handler service.
92    */
93   public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, EntityTypeManagerInterface $entity_type_manager, EventDispatcherInterface $event_dispatcher, FieldWidgetDisplayManager $field_display_manager, ModuleHandlerInterface $module_handler) {
94     parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
95     $this->entityTypeManager = $entity_type_manager;
96     $this->fieldDisplayManager = $field_display_manager;
97     $this->moduleHandler = $module_handler;
98   }
99
100   /**
101    * {@inheritdoc}
102    */
103   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
104     return new static(
105       $plugin_id,
106       $plugin_definition,
107       $configuration['field_definition'],
108       $configuration['settings'],
109       $configuration['third_party_settings'],
110       $container->get('entity_type.manager'),
111       $container->get('event_dispatcher'),
112       $container->get('plugin.manager.entity_browser.field_widget_display'),
113       $container->get('module_handler')
114     );
115   }
116
117   /**
118    * {@inheritdoc}
119    */
120   public static function defaultSettings() {
121     return array(
122       'entity_browser' => NULL,
123       'open' => FALSE,
124       'field_widget_display' => 'label',
125       'field_widget_edit' => TRUE,
126       'field_widget_remove' => TRUE,
127       'field_widget_display_settings' => [],
128       'selection_mode' => EntityBrowserElement::SELECTION_MODE_APPEND,
129     ) + parent::defaultSettings();
130   }
131
132   /**
133    * {@inheritdoc}
134    */
135   public function settingsForm(array $form, FormStateInterface $form_state) {
136     $element = parent::settingsForm($form, $form_state);
137
138     $browsers = [];
139     /** @var \Drupal\entity_browser\EntityBrowserInterface $browser */
140     foreach ($this->entityTypeManager->getStorage('entity_browser')->loadMultiple() as $browser) {
141       $browsers[$browser->id()] = $browser->label();
142     }
143
144     $element['entity_browser'] = [
145       '#title' => $this->t('Entity browser'),
146       '#type' => 'select',
147       '#default_value' => $this->getSetting('entity_browser'),
148       '#options' => $browsers,
149     ];
150
151     $target_type = $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type');
152     $entity_type = $this->entityTypeManager->getStorage($target_type)->getEntityType();
153
154     $displays = [];
155     foreach ($this->fieldDisplayManager->getDefinitions() as $id => $definition) {
156       if ($this->fieldDisplayManager->createInstance($id)->isApplicable($entity_type)) {
157         $displays[$id] = $definition['label'];
158       }
159     }
160
161     $id = Html::getUniqueId('field-' . $this->fieldDefinition->getName() . '-display-settings-wrapper');
162     $element['field_widget_display'] = [
163       '#title' => $this->t('Entity display plugin'),
164       '#type' => 'select',
165       '#default_value' => $this->getSetting('field_widget_display'),
166       '#options' => $displays,
167       '#ajax' => [
168         'callback' => array($this, 'updateSettingsAjax'),
169         'wrapper' => $id,
170       ],
171     ];
172
173     $edit_button_access = TRUE;
174     if ($entity_type->id() == 'file') {
175       // For entities of type "file", it only makes sense to have the edit
176       // button if the module "file_entity" is present.
177       $edit_button_access = $this->moduleHandler->moduleExists('file_entity');
178     }
179     $element['field_widget_edit'] = [
180       '#title' => $this->t('Display Edit button'),
181       '#type' => 'checkbox',
182       '#default_value' => $this->getSetting('field_widget_edit'),
183       '#access' => $edit_button_access,
184     ];
185
186     $element['field_widget_remove'] = [
187       '#title' => $this->t('Display Remove button'),
188       '#type' => 'checkbox',
189       '#default_value' => $this->getSetting('field_widget_remove'),
190     ];
191
192     $element['open'] = [
193       '#title' => $this->t('Show widget details as open by default'),
194       '#description' => $this->t('If marked, the fieldset container that wraps the browser on the entity form will be loaded initially expanded.'),
195       '#type' => 'checkbox',
196       '#default_value' => $this->getSetting('open'),
197     ];
198
199     $element['selection_mode'] = [
200       '#title' => $this->t('Selection mode'),
201       '#description' => $this->t('Determines how selection in entity browser will be handled. Will selection be appended/prepended or it will be replaced in case of editing.'),
202       '#type' => 'select',
203       '#options' => EntityBrowserElement::getSelectionModeOptions(),
204       '#default_value' => $this->getSetting('selection_mode'),
205     ];
206
207     $element['field_widget_display_settings'] = [
208       '#type' => 'fieldset',
209       '#title' => $this->t('Entity display plugin configuration'),
210       '#tree' => TRUE,
211       '#prefix' => '<div id="' . $id . '">',
212       '#suffix' => '</div>',
213     ];
214
215     if ($this->getSetting('field_widget_display')) {
216       $element['field_widget_display_settings'] += $this->fieldDisplayManager
217         ->createInstance(
218           $form_state->getValue(
219             ['fields', $this->fieldDefinition->getName(), 'settings_edit_form', 'settings', 'field_widget_display'],
220             $this->getSetting('field_widget_display')
221           ),
222           $form_state->getValue(
223             ['fields', $this->fieldDefinition->getName(), 'settings_edit_form', 'settings', 'field_widget_display_settings'],
224             $this->getSetting('field_widget_display_settings')
225           ) + ['entity_type' => $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type')]
226         )
227         ->settingsForm($form, $form_state);
228     }
229
230     return $element;
231   }
232
233   /**
234    * Ajax callback that updates field widget display settings fieldset.
235    */
236   public function updateSettingsAjax(array $form, FormStateInterface $form_state) {
237     return $form['fields'][$this->fieldDefinition->getName()]['plugin']['settings_edit_form']['settings']['field_widget_display_settings'];
238   }
239
240   /**
241    * {@inheritdoc}
242    */
243   public function settingsSummary() {
244     $summary = $this->summaryBase();
245     $field_widget_display = $this->getSetting('field_widget_display');
246
247     if (!empty($field_widget_display)) {
248       $plugin = $this->fieldDisplayManager->getDefinition($field_widget_display);
249       $summary[] = $this->t('Entity display: @name', ['@name' => $plugin['label']]);
250     }
251     return $summary;
252   }
253
254   /**
255    * {@inheritdoc}
256    */
257   public function flagErrors(FieldItemListInterface $items, ConstraintViolationListInterface $violations, array $form, FormStateInterface $form_state) {
258     if ($violations->count() > 0) {
259       /** @var \Symfony\Component\Validator\ConstraintViolation $violation */
260       foreach ($violations as $offset => $violation) {
261         // The value of the required field is checked through the "not null"
262         // constraint, whose message is not very useful. We override it here for
263         // better UX.
264         if ($violation->getConstraint() instanceof NotNullConstraint) {
265           $violations->set($offset, new ConstraintViolation(
266             $this->t('@name field is required.', ['@name' => $items->getFieldDefinition()->getLabel()]),
267             '',
268             [],
269             $violation->getRoot(),
270             $violation->getPropertyPath(),
271             $violation->getInvalidValue(),
272             $violation->getPlural(),
273             $violation->getCode(),
274             $violation->getConstraint(),
275             $violation->getCause()
276           ));
277         }
278       }
279     }
280
281     parent::flagErrors($items, $violations, $form, $form_state);
282   }
283
284   /**
285    * Returns a key used to store the previously loaded entity.
286    *
287    * @param \Drupal\Core\Field\FieldItemListInterface $items
288    *   The field items.
289    *
290    * @return string
291    *   A key for form state storage.
292    */
293   protected function getFormStateKey(FieldItemListInterface $items) {
294     return $items->getEntity()->uuid() . ':' . $items->getFieldDefinition()->getName();
295   }
296
297   /**
298    * {@inheritdoc}
299    */
300   public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
301     $entity_type = $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type');
302     $entities = $this->formElementEntities($items, $element, $form_state);
303
304     // Get correct ordered list of entity IDs.
305     $ids = array_map(
306       function (EntityInterface $entity) {
307         return $entity->id();
308       },
309       $entities
310     );
311
312     // We store current entity IDs as we might need them in future requests. If
313     // some other part of the form triggers an AJAX request with
314     // #limit_validation_errors we won't have access to the value of the
315     // target_id element and won't be able to build the form as a result of
316     // that. This will cause missing submit (Remove, Edit, ...) elements, which
317     // might result in unpredictable results.
318     $form_state->set(['entity_browser_widget', $this->getFormStateKey($items)], $ids);
319
320     $hidden_id = Html::getUniqueId('edit-' . $this->fieldDefinition->getName() . '-target-id');
321     $details_id = Html::getUniqueId('edit-' . $this->fieldDefinition->getName());
322
323     $element += [
324       '#id' => $details_id,
325       '#type' => 'details',
326       '#open' => !empty($entities) || $this->getSetting('open'),
327       '#required' => $this->fieldDefinition->isRequired(),
328       // We are not using Entity browser's hidden element since we maintain
329       // selected entities in it during entire process.
330       'target_id' => [
331         '#type' => 'hidden',
332         '#id' => $hidden_id,
333         // We need to repeat ID here as it is otherwise skipped when rendering.
334         '#attributes' => ['id' => $hidden_id],
335         '#default_value' => implode(' ', array_map(
336             function (EntityInterface $item) {
337               return $item->getEntityTypeId() . ':' . $item->id();
338             },
339             $entities
340         )),
341         // #ajax is officially not supported for hidden elements but if we
342         // specify event manually it works.
343         '#ajax' => [
344           'callback' => [get_class($this), 'updateWidgetCallback'],
345           'wrapper' => $details_id,
346           'event' => 'entity_browser_value_updated',
347         ],
348       ],
349     ];
350
351     // Get configuration required to check entity browser availability.
352     $cardinality = $this->fieldDefinition->getFieldStorageDefinition()->getCardinality();
353     $selection_mode = $this->getSetting('selection_mode');
354
355     // Enable entity browser if requirements for that are fulfilled.
356     if (EntityBrowserElement::isEntityBrowserAvailable($selection_mode, $cardinality, count($ids))) {
357       $persistentData = $this->getPersistentData();
358
359       $element['entity_browser'] = [
360         '#type' => 'entity_browser',
361         '#entity_browser' => $this->getSetting('entity_browser'),
362         '#cardinality' => $cardinality,
363         '#selection_mode' => $selection_mode,
364         '#default_value' => $entities,
365         '#entity_browser_validators' => $persistentData['validators'],
366         '#widget_context' => $persistentData['widget_context'],
367         '#custom_hidden_id' => $hidden_id,
368         '#process' => [
369           ['\Drupal\entity_browser\Element\EntityBrowserElement', 'processEntityBrowser'],
370           [get_called_class(), 'processEntityBrowser'],
371         ],
372       ];
373     }
374
375     $element['#attached']['library'][] = 'entity_browser/entity_reference';
376
377     $field_parents = $element['#field_parents'];
378
379     $element['current'] = $this->displayCurrentSelection($details_id, $field_parents, $entities);
380
381     return $element;
382   }
383
384   /**
385    * Render API callback: Processes the entity browser element.
386    */
387   public static function processEntityBrowser(&$element, FormStateInterface $form_state, &$complete_form) {
388     $uuid = key($element['#attached']['drupalSettings']['entity_browser']);
389     $element['#attached']['drupalSettings']['entity_browser'][$uuid]['selector'] = '#' . $element['#custom_hidden_id'];
390     return $element;
391   }
392
393   /**
394    * {@inheritdoc}
395    */
396   public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
397     $entities = empty($values['target_id']) ? [] : explode(' ', trim($values['target_id']));
398     $return = [];
399     foreach ($entities as $entity) {
400       $return[]['target_id'] = explode(':', $entity)[1];
401     }
402
403     return $return;
404   }
405
406   /**
407    * AJAX form callback.
408    */
409   public static function updateWidgetCallback(array &$form, FormStateInterface $form_state) {
410     $trigger = $form_state->getTriggeringElement();
411     // AJAX requests can be triggered by hidden "target_id" element when
412     // entities are added or by one of the "Remove" buttons. Depending on that
413     // we need to figure out where root of the widget is in the form structure
414     // and use this information to return correct part of the form.
415     if (!empty($trigger['#ajax']['event']) && $trigger['#ajax']['event'] == 'entity_browser_value_updated') {
416       $parents = array_slice($trigger['#array_parents'], 0, -1);
417     }
418     elseif ($trigger['#type'] == 'submit' && strpos($trigger['#name'], '_remove_')) {
419       $parents = array_slice($trigger['#array_parents'], 0, -static::$deleteDepth);
420     }
421
422     return NestedArray::getValue($form, $parents);
423   }
424
425   /**
426    * {@inheritdoc}
427    */
428   public function errorElement(array $element, ConstraintViolationInterface $violation, array $form, FormStateInterface $form_state) {
429     if (($trigger = $form_state->getTriggeringElement())) {
430       // Can be triggered by "Remove" button.
431       if (end($trigger['#parents']) === 'remove_button') {
432         return FALSE;
433       }
434     }
435     return parent::errorElement($element, $violation, $form, $form_state);
436   }
437
438   /**
439    * Submit callback for remove buttons.
440    */
441   public static function removeItemSubmit(&$form, FormStateInterface $form_state) {
442     $triggering_element = $form_state->getTriggeringElement();
443     if (!empty($triggering_element['#attributes']['data-entity-id']) && isset($triggering_element['#attributes']['data-row-id'])) {
444       $id = $triggering_element['#attributes']['data-entity-id'];
445       $row_id = $triggering_element['#attributes']['data-row-id'];
446       $parents = array_slice($triggering_element['#parents'], 0, -static::$deleteDepth);
447       $array_parents = array_slice($triggering_element['#array_parents'], 0, -static::$deleteDepth);
448
449       // Find and remove correct entity.
450       $values = explode(' ', $form_state->getValue(array_merge($parents, ['target_id'])));
451       foreach ($values as $index => $item) {
452         if ($item == $id && $index == $row_id) {
453           array_splice($values, $index, 1);
454
455           break;
456         }
457       }
458       $target_id_value = implode(' ', $values);
459
460       // Set new value for this widget.
461       $target_id_element = &NestedArray::getValue($form, array_merge($array_parents, ['target_id']));
462       $form_state->setValueForElement($target_id_element, $target_id_value);
463       NestedArray::setValue($form_state->getUserInput(), $target_id_element['#parents'], $target_id_value);
464
465       // Rebuild form.
466       $form_state->setRebuild();
467     }
468   }
469
470   /**
471    * Builds the render array for displaying the current results.
472    *
473    * @param string $details_id
474    *   The ID for the details element.
475    * @param string[] $field_parents
476    *   Field parents.
477    * @param \Drupal\Core\Entity\ContentEntityInterface[] $entities
478    *   Array of referenced entities.
479    *
480    * @return array
481    *   The render array for the current selection.
482    */
483   protected function displayCurrentSelection($details_id, $field_parents, $entities) {
484
485     $field_widget_display = $this->fieldDisplayManager->createInstance(
486       $this->getSetting('field_widget_display'),
487       $this->getSetting('field_widget_display_settings') + ['entity_type' => $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type')]
488     );
489
490     return [
491       '#theme_wrappers' => ['container'],
492       '#attributes' => ['class' => ['entities-list']],
493       'items' => array_map(
494         function (ContentEntityInterface $entity, $row_id) use ($field_widget_display, $details_id, $field_parents) {
495           $display = $field_widget_display->view($entity);
496           $edit_button_access = $this->getSetting('field_widget_edit');
497           if ($entity->getEntityTypeId() == 'file') {
498             // On file entities, the "edit" button shouldn't be visible unless
499             // the module "file_entity" is present, which will allow them to be
500             // edited on their own form.
501             $edit_button_access &= $this->moduleHandler->moduleExists('file_entity');
502           }
503           if (is_string($display)) {
504             $display = ['#markup' => $display];
505           }
506           return [
507             '#theme_wrappers' => ['container'],
508             '#attributes' => [
509               'class' => ['item-container', Html::getClass($field_widget_display->getPluginId())],
510               'data-entity-id' => $entity->getEntityTypeId() . ':' . $entity->id(),
511               'data-row-id' => $row_id,
512             ],
513             'display' => $display,
514             'remove_button' => [
515               '#type' => 'submit',
516               '#value' => $this->t('Remove'),
517               '#ajax' => [
518                 'callback' => [get_class($this), 'updateWidgetCallback'],
519                 'wrapper' => $details_id,
520               ],
521               '#submit' => [[get_class($this), 'removeItemSubmit']],
522               '#name' => $this->fieldDefinition->getName() . '_remove_' . $entity->id() . '_' . $row_id . '_' . md5(json_encode($field_parents)),
523               '#limit_validation_errors' => [array_merge($field_parents, [$this->fieldDefinition->getName()])],
524               '#attributes' => [
525                 'data-entity-id' => $entity->getEntityTypeId() . ':' . $entity->id(),
526                 'data-row-id' => $row_id,
527               ],
528               '#access' => (bool) $this->getSetting('field_widget_remove'),
529             ],
530             'edit_button' => [
531               '#type' => 'submit',
532               '#value' => $this->t('Edit'),
533               '#ajax' => [
534                 'url' => Url::fromRoute(
535                   'entity_browser.edit_form', [
536                     'entity_type' => $entity->getEntityTypeId(),
537                     'entity' => $entity->id(),
538                   ]
539                 ),
540                 'options' => [
541                   'query' => [
542                     'details_id' => $details_id,
543                   ],
544                 ],
545               ],
546               '#access' => $edit_button_access,
547             ],
548           ];
549         },
550         $entities,
551         empty($entities) ? [] : range(0, count($entities) - 1)
552       ),
553     ];
554   }
555
556   /**
557    * Gets data that should persist across Entity Browser renders.
558    *
559    * @return array
560    *   Data that should persist after the Entity Browser is rendered.
561    */
562   protected function getPersistentData() {
563     return [
564       'validators' => [
565         'entity_type' => ['type' => $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type')],
566       ],
567       'widget_context' => [],
568     ];
569   }
570
571   /**
572    * Gets options that define where newly added entities are inserted.
573    *
574    * @return array
575    *   Mode labels indexed by key.
576    */
577   protected function selectionModeOptions() {
578     return ['append' => $this->t('Append'), 'prepend' => $this->t('Prepend')];
579   }
580
581   /**
582    * Provides base for settings summary shared by all EB widgets.
583    *
584    * @return array
585    *   A short summary of the widget settings.
586    */
587   protected function summaryBase() {
588     $summary = [];
589
590     $entity_browser_id = $this->getSetting('entity_browser');
591     if (empty($entity_browser_id)) {
592       return [$this->t('No entity browser selected.')];
593     }
594     else {
595       if ($browser = $this->entityTypeManager->getStorage('entity_browser')->load($entity_browser_id)) {
596         $summary[] = $this->t('Entity browser: @browser', ['@browser' => $browser->label()]);
597       }
598       else {
599         drupal_set_message($this->t('Missing entity browser!'), 'error');
600         return [$this->t('Missing entity browser!')];
601       }
602     }
603
604     $selection_mode = $this->getSetting('selection_mode');
605     $selection_mode_options = EntityBrowserElement::getSelectionModeOptions();
606     if (isset($selection_mode_options[$selection_mode])) {
607       $summary[] = $this->t('Selection mode: @selection_mode', ['@selection_mode' => $selection_mode_options[$selection_mode]]);
608     }
609     else {
610       $summary[] = $this->t('Undefined selection mode.');
611     }
612
613     return $summary;
614   }
615
616   /**
617    * Determines the entities used for the form element.
618    *
619    * @param \Drupal\Core\Field\FieldItemListInterface $items
620    *   The field item to extract the entities from.
621    * @param array $element
622    *   The form element.
623    * @param \Drupal\Core\Form\FormStateInterface $form_state
624    *   The form state.
625    *
626    * @return \Drupal\Core\Entity\EntityInterface[]
627    *   The list of entities for the form element.
628    */
629   protected function formElementEntities(FieldItemListInterface $items, array $element, FormStateInterface $form_state) {
630     $entities = [];
631     $entity_type = $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type');
632     $entity_storage = $this->entityTypeManager->getStorage($entity_type);
633
634     // Find IDs from target_id element (it stores selected entities in form).
635     // This was added to help solve a really edge casey bug in IEF.
636     if (($target_id_entities = $this->getEntitiesByTargetId($element, $form_state)) !== FALSE) {
637       return $target_id_entities;
638     }
639
640     // Determine if we're submitting and if submit came from this widget.
641     $is_relevant_submit = FALSE;
642     if (($trigger = $form_state->getTriggeringElement())) {
643       // Can be triggered by hidden target_id element or "Remove" button.
644       if (end($trigger['#parents']) === 'target_id' || (end($trigger['#parents']) === 'remove_button')) {
645         $is_relevant_submit = TRUE;
646
647         // In case there are more instances of this widget on the same page we
648         // need to check if submit came from this instance.
649         $field_name_key = end($trigger['#parents']) === 'target_id' ? 2 : static::$deleteDepth + 1;
650         $field_name_key = count($trigger['#parents']) - $field_name_key;
651         $is_relevant_submit &= ($trigger['#parents'][$field_name_key] === $this->fieldDefinition->getName()) &&
652           (array_slice($trigger['#parents'], 0, count($element['#field_parents'])) == $element['#field_parents']);
653       }
654     };
655
656     if ($is_relevant_submit) {
657       // Submit was triggered by hidden "target_id" element when entities were
658       // added via entity browser.
659       if (!empty($trigger['#ajax']['event']) && $trigger['#ajax']['event'] == 'entity_browser_value_updated') {
660         $parents = $trigger['#parents'];
661       }
662       // Submit was triggered by one of the "Remove" buttons. We need to walk
663       // few levels up to read value of "target_id" element.
664       elseif ($trigger['#type'] == 'submit' && strpos($trigger['#name'], $this->fieldDefinition->getName() . '_remove_') === 0) {
665         $parents = array_merge(array_slice($trigger['#parents'], 0, -static::$deleteDepth), ['target_id']);
666       }
667
668       if (isset($parents) && $value = $form_state->getValue($parents)) {
669         $entities = EntityBrowserElement::processEntityIds($value);
670         return $entities;
671       }
672       return $entities;
673     }
674     // IDs from a previous request might be saved in the form state.
675     elseif ($form_state->has([
676       'entity_browser_widget',
677       $this->getFormStateKey($items),
678     ])
679     ) {
680       $stored_ids = $form_state->get([
681         'entity_browser_widget',
682         $this->getFormStateKey($items),
683       ]);
684       $indexed_entities = $entity_storage->loadMultiple($stored_ids);
685
686       // Selection can contain same entity multiple times. Since loadMultiple()
687       // returns unique list of entities, it's necessary to recreate list of
688       // entities in order to preserve selection of duplicated entities.
689       foreach ($stored_ids as $entity_id) {
690         if (isset($indexed_entities[$entity_id])) {
691           $entities[] = $indexed_entities[$entity_id];
692         }
693       }
694       return $entities;
695     }
696     // We are loading for for the first time so we need to load any existing
697     // values that might already exist on the entity. Also, remove any leftover
698     // data from removed entity references.
699     else {
700       foreach ($items as $item) {
701         if (isset($item->target_id)) {
702           $entity = $entity_storage->load($item->target_id);
703           if (!empty($entity)) {
704             $entities[] = $entity;
705           }
706         }
707       }
708       return $entities;
709     }
710   }
711
712   /**
713    * {@inheritdoc}
714    */
715   public function calculateDependencies() {
716     $dependencies = parent::calculateDependencies();
717
718     // If an entity browser is being used in this widget, add it as a config
719     // dependency.
720     if ($browser_name = $this->getSetting('entity_browser')) {
721       $dependencies['config'][] = 'entity_browser.browser.' . $browser_name;
722     }
723
724     return $dependencies;
725   }
726
727   /**
728    * Get selected elements from target_id element on form.
729    *
730    * @param array $element
731    *   The form element.
732    * @param \Drupal\Core\Form\FormStateInterface $form_state
733    *   The form state.
734    *
735    * @return \Drupal\Core\Entity\EntityInterface[]|false
736    *   Return list of entities if they are available or false.
737    */
738   protected function getEntitiesByTargetId(array $element, FormStateInterface $form_state) {
739     $target_id_element_path = array_merge(
740       $element['#field_parents'],
741       [$this->fieldDefinition->getName(), 'target_id']
742     );
743
744     if (!NestedArray::keyExists($form_state->getUserInput(), $target_id_element_path)) {
745       return FALSE;
746     }
747
748     // TODO Figure out how to avoid using raw user input.
749     $current_user_input = NestedArray::getValue($form_state->getUserInput(), $target_id_element_path);
750     if (!is_array($current_user_input)) {
751       $entities = EntityBrowserElement::processEntityIds($current_user_input);
752       return $entities;
753     }
754
755     return FALSE;
756   }
757
758 }