0ac85bc8c671801ef2ceb622f45b6a6a7c17f8e8
[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       $element['entity_browser'] = [
358         '#type' => 'entity_browser',
359         '#entity_browser' => $this->getSetting('entity_browser'),
360         '#cardinality' => $cardinality,
361         '#selection_mode' => $selection_mode,
362         '#default_value' => $entities,
363         '#entity_browser_validators' => ['entity_type' => ['type' => $entity_type]],
364         '#custom_hidden_id' => $hidden_id,
365         '#process' => [
366           ['\Drupal\entity_browser\Element\EntityBrowserElement', 'processEntityBrowser'],
367           [get_called_class(), 'processEntityBrowser'],
368         ],
369       ];
370
371     }
372     $element['#attached']['library'][] = 'entity_browser/entity_reference';
373
374     $field_parents = $element['#field_parents'];
375
376     $element['current'] = $this->displayCurrentSelection($details_id, $field_parents, $entities);
377
378     return $element;
379   }
380
381   /**
382    * Render API callback: Processes the entity browser element.
383    */
384   public static function processEntityBrowser(&$element, FormStateInterface $form_state, &$complete_form) {
385     $uuid = key($element['#attached']['drupalSettings']['entity_browser']);
386     $element['#attached']['drupalSettings']['entity_browser'][$uuid]['selector'] = '#' . $element['#custom_hidden_id'];
387     return $element;
388   }
389
390   /**
391    * {@inheritdoc}
392    */
393   public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
394     $entities = empty($values['target_id']) ? [] : explode(' ', trim($values['target_id']));
395     $return = [];
396     foreach ($entities as $entity) {
397       $return[]['target_id'] = explode(':', $entity)[1];
398     }
399
400     return $return;
401   }
402
403   /**
404    * AJAX form callback.
405    */
406   public static function updateWidgetCallback(array &$form, FormStateInterface $form_state) {
407     $trigger = $form_state->getTriggeringElement();
408     // AJAX requests can be triggered by hidden "target_id" element when
409     // entities are added or by one of the "Remove" buttons. Depending on that
410     // we need to figure out where root of the widget is in the form structure
411     // and use this information to return correct part of the form.
412     if (!empty($trigger['#ajax']['event']) && $trigger['#ajax']['event'] == 'entity_browser_value_updated') {
413       $parents = array_slice($trigger['#array_parents'], 0, -1);
414     }
415     elseif ($trigger['#type'] == 'submit' && strpos($trigger['#name'], '_remove_')) {
416       $parents = array_slice($trigger['#array_parents'], 0, -static::$deleteDepth);
417     }
418
419     return NestedArray::getValue($form, $parents);
420   }
421
422   /**
423    * {@inheritdoc}
424    */
425   public function errorElement(array $element, ConstraintViolationInterface $violation, array $form, FormStateInterface $form_state) {
426     if (($trigger = $form_state->getTriggeringElement())) {
427       // Can be triggered by "Remove" button.
428       if (end($trigger['#parents']) === 'remove_button') {
429         return FALSE;
430       }
431     }
432     return parent::errorElement($element, $violation, $form, $form_state);
433   }
434
435   /**
436    * Submit callback for remove buttons.
437    */
438   public static function removeItemSubmit(&$form, FormStateInterface $form_state) {
439     $triggering_element = $form_state->getTriggeringElement();
440     if (!empty($triggering_element['#attributes']['data-entity-id']) && isset($triggering_element['#attributes']['data-row-id'])) {
441       $id = $triggering_element['#attributes']['data-entity-id'];
442       $row_id = $triggering_element['#attributes']['data-row-id'];
443       $parents = array_slice($triggering_element['#parents'], 0, -static::$deleteDepth);
444       $array_parents = array_slice($triggering_element['#array_parents'], 0, -static::$deleteDepth);
445
446       // Find and remove correct entity.
447       $values = explode(' ', $form_state->getValue(array_merge($parents, ['target_id'])));
448       foreach ($values as $index => $item) {
449         if ($item == $id && $index == $row_id) {
450           array_splice($values, $index, 1);
451
452           break;
453         }
454       }
455       $target_id_value = implode(' ', $values);
456
457       // Set new value for this widget.
458       $target_id_element = &NestedArray::getValue($form, array_merge($array_parents, ['target_id']));
459       $form_state->setValueForElement($target_id_element, $target_id_value);
460       NestedArray::setValue($form_state->getUserInput(), $target_id_element['#parents'], $target_id_value);
461
462       // Rebuild form.
463       $form_state->setRebuild();
464     }
465   }
466
467   /**
468    * Builds the render array for displaying the current results.
469    *
470    * @param string $details_id
471    *   The ID for the details element.
472    * @param string[] $field_parents
473    *   Field parents.
474    * @param \Drupal\Core\Entity\ContentEntityInterface[] $entities
475    *   Array of referenced entities.
476    *
477    * @return array
478    *   The render array for the current selection.
479    */
480   protected function displayCurrentSelection($details_id, $field_parents, $entities) {
481
482     $field_widget_display = $this->fieldDisplayManager->createInstance(
483       $this->getSetting('field_widget_display'),
484       $this->getSetting('field_widget_display_settings') + ['entity_type' => $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type')]
485     );
486
487     return [
488       '#theme_wrappers' => ['container'],
489       '#attributes' => ['class' => ['entities-list']],
490       'items' => array_map(
491         function (ContentEntityInterface $entity, $row_id) use ($field_widget_display, $details_id, $field_parents) {
492           $display = $field_widget_display->view($entity);
493           $edit_button_access = $this->getSetting('field_widget_edit');
494           if ($entity->getEntityTypeId() == 'file') {
495             // On file entities, the "edit" button shouldn't be visible unless
496             // the module "file_entity" is present, which will allow them to be
497             // edited on their own form.
498             $edit_button_access &= $this->moduleHandler->moduleExists('file_entity');
499           }
500           if (is_string($display)) {
501             $display = ['#markup' => $display];
502           }
503           return [
504             '#theme_wrappers' => ['container'],
505             '#attributes' => [
506               'class' => ['item-container', Html::getClass($field_widget_display->getPluginId())],
507               'data-entity-id' => $entity->getEntityTypeId() . ':' . $entity->id(),
508               'data-row-id' => $row_id,
509             ],
510             'display' => $display,
511             'remove_button' => [
512               '#type' => 'submit',
513               '#value' => $this->t('Remove'),
514               '#ajax' => [
515                 'callback' => [get_class($this), 'updateWidgetCallback'],
516                 'wrapper' => $details_id,
517               ],
518               '#submit' => [[get_class($this), 'removeItemSubmit']],
519               '#name' => $this->fieldDefinition->getName() . '_remove_' . $entity->id() . '_' . $row_id . '_' . md5(json_encode($field_parents)),
520               '#limit_validation_errors' => [array_merge($field_parents, [$this->fieldDefinition->getName()])],
521               '#attributes' => [
522                 'data-entity-id' => $entity->getEntityTypeId() . ':' . $entity->id(),
523                 'data-row-id' => $row_id,
524               ],
525               '#access' => (bool) $this->getSetting('field_widget_remove'),
526             ],
527             'edit_button' => [
528               '#type' => 'submit',
529               '#value' => $this->t('Edit'),
530               '#ajax' => [
531                 'url' => Url::fromRoute(
532                   'entity_browser.edit_form', [
533                     'entity_type' => $entity->getEntityTypeId(),
534                     'entity' => $entity->id(),
535                   ]
536                 ),
537                 'options' => [
538                   'query' => [
539                     'details_id' => $details_id,
540                   ],
541                 ],
542               ],
543               '#access' => $edit_button_access,
544             ],
545           ];
546         },
547         $entities,
548         empty($entities) ? [] : range(0, count($entities) - 1)
549       ),
550     ];
551   }
552
553   /**
554    * Gets data that should persist across Entity Browser renders.
555    *
556    * @return array
557    *   Data that should persist after the Entity Browser is rendered.
558    */
559   protected function getPersistentData() {
560     return [
561       'validators' => [
562         'entity_type' => ['type' => $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type')],
563       ],
564     ];
565   }
566
567   /**
568    * Gets options that define where newly added entities are inserted.
569    *
570    * @return array
571    *   Mode labels indexed by key.
572    */
573   protected function selectionModeOptions() {
574     return ['append' => $this->t('Append'), 'prepend' => $this->t('Prepend')];
575   }
576
577   /**
578    * Provides base for settings summary shared by all EB widgets.
579    *
580    * @return array
581    *   A short summary of the widget settings.
582    */
583   protected function summaryBase() {
584     $summary = [];
585
586     $entity_browser_id = $this->getSetting('entity_browser');
587     if (empty($entity_browser_id)) {
588       return [$this->t('No entity browser selected.')];
589     }
590     else {
591       if ($browser = $this->entityTypeManager->getStorage('entity_browser')->load($entity_browser_id)) {
592         $summary[] = $this->t('Entity browser: @browser', ['@browser' => $browser->label()]);
593       }
594       else {
595         drupal_set_message($this->t('Missing entity browser!'), 'error');
596         return [$this->t('Missing entity browser!')];
597       }
598     }
599
600     $selection_mode = $this->getSetting('selection_mode');
601     $selection_mode_options = EntityBrowserElement::getSelectionModeOptions();
602     if (isset($selection_mode_options[$selection_mode])) {
603       $summary[] = $this->t('Selection mode: @selection_mode', ['@selection_mode' => $selection_mode_options[$selection_mode]]);
604     }
605     else {
606       $summary[] = $this->t('Undefined selection mode.');
607     }
608
609     return $summary;
610   }
611
612   /**
613    * Determines the entities used for the form element.
614    *
615    * @param \Drupal\Core\Field\FieldItemListInterface $items
616    *   The field item to extract the entities from.
617    * @param array $element
618    *   The form element.
619    * @param \Drupal\Core\Form\FormStateInterface $form_state
620    *   The form state.
621    *
622    * @return \Drupal\Core\Entity\EntityInterface[]
623    *   The list of entities for the form element.
624    */
625   protected function formElementEntities(FieldItemListInterface $items, array $element, FormStateInterface $form_state) {
626     $entities = [];
627     $entity_type = $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type');
628     $entity_storage = $this->entityTypeManager->getStorage($entity_type);
629
630     // Find IDs from target_id element (it stores selected entities in form).
631     // This was added to help solve a really edge casey bug in IEF.
632     if (($target_id_entities = $this->getEntitiesByTargetId($element, $form_state)) !== FALSE) {
633       return $target_id_entities;
634     }
635
636     // Determine if we're submitting and if submit came from this widget.
637     $is_relevant_submit = FALSE;
638     if (($trigger = $form_state->getTriggeringElement())) {
639       // Can be triggered by hidden target_id element or "Remove" button.
640       if (end($trigger['#parents']) === 'target_id' || (end($trigger['#parents']) === 'remove_button')) {
641         $is_relevant_submit = TRUE;
642
643         // In case there are more instances of this widget on the same page we
644         // need to check if submit came from this instance.
645         $field_name_key = end($trigger['#parents']) === 'target_id' ? 2 : static::$deleteDepth + 1;
646         $field_name_key = count($trigger['#parents']) - $field_name_key;
647         $is_relevant_submit &= ($trigger['#parents'][$field_name_key] === $this->fieldDefinition->getName()) &&
648           (array_slice($trigger['#parents'], 0, count($element['#field_parents'])) == $element['#field_parents']);
649       }
650     };
651
652     if ($is_relevant_submit) {
653       // Submit was triggered by hidden "target_id" element when entities were
654       // added via entity browser.
655       if (!empty($trigger['#ajax']['event']) && $trigger['#ajax']['event'] == 'entity_browser_value_updated') {
656         $parents = $trigger['#parents'];
657       }
658       // Submit was triggered by one of the "Remove" buttons. We need to walk
659       // few levels up to read value of "target_id" element.
660       elseif ($trigger['#type'] == 'submit' && strpos($trigger['#name'], $this->fieldDefinition->getName() . '_remove_') === 0) {
661         $parents = array_merge(array_slice($trigger['#parents'], 0, -static::$deleteDepth), ['target_id']);
662       }
663
664       if (isset($parents) && $value = $form_state->getValue($parents)) {
665         $entities = EntityBrowserElement::processEntityIds($value);
666         return $entities;
667       }
668       return $entities;
669     }
670     // IDs from a previous request might be saved in the form state.
671     elseif ($form_state->has([
672       'entity_browser_widget',
673       $this->getFormStateKey($items),
674     ])
675     ) {
676       $stored_ids = $form_state->get([
677         'entity_browser_widget',
678         $this->getFormStateKey($items),
679       ]);
680       $indexed_entities = $entity_storage->loadMultiple($stored_ids);
681
682       // Selection can contain same entity multiple times. Since loadMultiple()
683       // returns unique list of entities, it's necessary to recreate list of
684       // entities in order to preserve selection of duplicated entities.
685       foreach ($stored_ids as $entity_id) {
686         if (isset($indexed_entities[$entity_id])) {
687           $entities[] = $indexed_entities[$entity_id];
688         }
689       }
690       return $entities;
691     }
692     // We are loading for for the first time so we need to load any existing
693     // values that might already exist on the entity. Also, remove any leftover
694     // data from removed entity references.
695     else {
696       foreach ($items as $item) {
697         if (isset($item->target_id)) {
698           $entity = $entity_storage->load($item->target_id);
699           if (!empty($entity)) {
700             $entities[] = $entity;
701           }
702         }
703       }
704       return $entities;
705     }
706   }
707
708   /**
709    * {@inheritdoc}
710    */
711   public function calculateDependencies() {
712     $dependencies = parent::calculateDependencies();
713
714     // If an entity browser is being used in this widget, add it as a config
715     // dependency.
716     if ($browser_name = $this->getSetting('entity_browser')) {
717       $dependencies['config'][] = 'entity_browser.browser.' . $browser_name;
718     }
719
720     return $dependencies;
721   }
722
723   /**
724    * Get selected elements from target_id element on form.
725    *
726    * @param array $element
727    *   The form element.
728    * @param \Drupal\Core\Form\FormStateInterface $form_state
729    *   The form state.
730    *
731    * @return \Drupal\Core\Entity\EntityInterface[]|false
732    *   Return list of entities if they are available or false.
733    */
734   protected function getEntitiesByTargetId(array $element, FormStateInterface $form_state) {
735     $target_id_element_path = array_merge(
736       $element['#field_parents'],
737       [$this->fieldDefinition->getName(), 'target_id']
738     );
739
740     if (!NestedArray::keyExists($form_state->getUserInput(), $target_id_element_path)) {
741       return FALSE;
742     }
743
744     // TODO Figure out how to avoid using raw user input.
745     $current_user_input = NestedArray::getValue($form_state->getUserInput(), $target_id_element_path);
746     if (!is_array($current_user_input)) {
747       $entities = EntityBrowserElement::processEntityIds($current_user_input);
748       return $entities;
749     }
750
751     return FALSE;
752   }
753
754 }