Completed
Push — 8.x-1.x ( e2f153...09a174 )
by Janez
02:43
created

EntityReferenceBrowserWidget::settingsForm()   B

Complexity

Conditions 5
Paths 12

Size

Total Lines 81
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 55
c 1
b 0
f 0
nc 12
nop 2
dl 0
loc 81
rs 8.3904

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Drupal\entity_browser\Plugin\Field\FieldWidget;
4
5
use Symfony\Component\Validator\ConstraintViolationInterface;
6
use Drupal\Component\Utility\Html;
7
use Drupal\Component\Utility\NestedArray;
8
use Drupal\Core\Entity\ContentEntityInterface;
9
use Drupal\Core\Entity\EntityTypeManagerInterface;
10
use Drupal\Core\Field\FieldDefinitionInterface;
11
use Drupal\Core\Field\FieldItemListInterface;
12
use Drupal\Core\Field\FieldStorageDefinitionInterface;
13
use Drupal\Core\Field\WidgetBase;
14
use Drupal\Core\Form\FormStateInterface;
15
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
16
use Drupal\Core\Url;
17
use Drupal\Core\Validation\Plugin\Validation\Constraint\NotNullConstraint;
18
use Drupal\entity_browser\FieldWidgetDisplayManager;
19
use Symfony\Component\DependencyInjection\ContainerInterface;
20
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
21
use Symfony\Component\Validator\ConstraintViolation;
22
use Symfony\Component\Validator\ConstraintViolationListInterface;
23
24
/**
25
 * Plugin implementation of the 'entity_reference' widget for entity browser.
26
 *  * @FieldWidget(
27
 *   id = "entity_browser_entity_reference",
28
 *   label = @Translation("Entity browser"),
29
 *   description = @Translation("Uses entity browser to select entities."),
30
 *   multiple_values = TRUE,
31
 *   field_types = {
32
 *     "entity_reference"
33
 *   }
34
 * )
35
 */
36
class EntityReferenceBrowserWidget extends WidgetBase implements ContainerFactoryPluginInterface {
37
38
  /**
39
   * Entity type manager service.
40
   *
41
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
42
   */
43
  protected $entityTypeManager;
44
45
  /**
46
   * Field widget display plugin manager.
47
   *
48
   * @var \Drupal\entity_browser\FieldWidgetDisplayManager
49
   */
50
  protected $fieldDisplayManager;
51
52
  /**
53
   * The depth of the delete button.
54
   *
55
   * This property exists so it can be changed if subclasses.
56
   *
57
   * @var int
58
   */
59
  protected static $deleteDepth = 4;
60
61
  /**
62
   * Constructs widget plugin.
63
   *
64
   * @param string $plugin_id
65
   *   The plugin_id for the plugin instance.
66
   * @param mixed $plugin_definition
67
   *   The plugin implementation definition.
68
   * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
69
   *   The definition of the field to which the widget is associated.
70
   * @param array $settings
71
   *   The widget settings.
72
   * @param array $third_party_settings
73
   *   Any third party settings.
74
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
75
   *   Entity type manager service.
76
   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
77
   *   Event dispatcher.
78
   * @param \Drupal\entity_browser\FieldWidgetDisplayManager $field_display_manager
79
   *   Field widget display plugin manager.
80
   */
81
  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) {
82
    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
83
    $this->entityTypeManager = $entity_type_manager;
84
    $this->fieldDisplayManager = $field_display_manager;
85
  }
86
87
  /**
88
   * {@inheritdoc}
89
   */
90
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
91
    return new static(
92
      $plugin_id,
93
      $plugin_definition,
94
      $configuration['field_definition'],
95
      $configuration['settings'],
96
      $configuration['third_party_settings'],
97
      $container->get('entity_type.manager'),
98
      $container->get('event_dispatcher'),
99
      $container->get('plugin.manager.entity_browser.field_widget_display')
100
    );
101
  }
102
103
  /**
104
   * {@inheritdoc}
105
   */
106
  public static function defaultSettings() {
107
    return array(
108
      'entity_browser' => NULL,
109
      'open' => FALSE,
110
      'field_widget_display' => NULL,
111
      'field_widget_edit' => TRUE,
112
      'field_widget_remove' => TRUE,
113
      'field_widget_display_settings' => [],
114
    ) + parent::defaultSettings();
115
  }
116
117
  /**
118
   * {@inheritdoc}
119
   */
120
  public function settingsForm(array $form, FormStateInterface $form_state) {
121
    $element = parent::settingsForm($form, $form_state);
122
123
    $browsers = [];
124
    /** @var \Drupal\entity_browser\EntityBrowserInterface $browser */
125
    foreach ($this->entityTypeManager->getStorage('entity_browser')->loadMultiple() as $browser) {
126
      $browsers[$browser->id()] = $browser->label();
127
    }
128
129
    $element['entity_browser'] = [
130
      '#title' => t('Entity browser'),
131
      '#type' => 'select',
132
      '#default_value' => $this->getSetting('entity_browser'),
133
      '#options' => $browsers,
134
    ];
135
136
    $target_type = $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type');
137
    $entity_type = $this->entityTypeManager->getStorage($target_type)->getEntityType();
138
139
    $displays = [];
140
    foreach ($this->fieldDisplayManager->getDefinitions() as $id => $definition) {
141
      if ($this->fieldDisplayManager->createInstance($id)->isApplicable($entity_type)) {
142
        $displays[$id] = $definition['label'];
143
      }
144
    }
145
146
    $id = Html::getUniqueId('field-' . $this->fieldDefinition->getName() . '-display-settings-wrapper');
147
    $element['field_widget_display'] = [
148
      '#title' => t('Entity display plugin'),
149
      '#type' => 'select',
150
      '#default_value' => $this->getSetting('field_widget_display'),
151
      '#options' => $displays,
152
      '#ajax' => [
153
        'callback' => array($this, 'updateSettingsAjax'),
154
        'wrapper' => $id,
155
      ],
156
    ];
157
158
    $element['field_widget_edit'] = [
159
      '#title' => t('Display Edit button'),
160
      '#type' => 'checkbox',
161
      '#default_value' => $this->getSetting('field_widget_edit'),
162
    ];
163
164
    $element['field_widget_remove'] = [
165
      '#title' => t('Display Remove button'),
166
      '#type' => 'checkbox',
167
      '#default_value' => $this->getSetting('field_widget_remove'),
168
    ];
169
170
    $element['open'] = [
171
      '#title' => t('Show widget details as open by default'),
172
      '#type' => 'checkbox',
173
      '#default_value' => $this->getSetting('open'),
174
    ];
175
176
    $element['field_widget_display_settings'] = [
177
      '#type' => 'fieldset',
178
      '#title' => t('Entity display plugin configuration'),
179
      '#tree' => TRUE,
180
      '#prefix' => '<div id="' . $id . '">',
181
      '#suffix' => '</div>',
182
    ];
183
184
    if ($this->getSetting('field_widget_display')) {
185
      $element['field_widget_display_settings'] += $this->fieldDisplayManager
186
        ->createInstance(
187
          $form_state->getValue(
188
            ['fields', $this->fieldDefinition->getName(), 'settings_edit_form', 'settings', 'field_widget_display'],
189
            $this->getSetting('field_widget_display')
190
          ),
191
          $form_state->getValue(
192
            ['fields', $this->fieldDefinition->getName(), 'settings_edit_form', 'settings', 'field_widget_display_settings'],
193
            $this->getSetting('field_widget_display_settings')
194
          ) + ['entity_type' => $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type')]
195
        )
196
        ->settingsForm($form, $form_state);
197
    }
198
199
    return $element;
200
  }
201
202
  /**
203
   * Ajax callback that updates field widget display settings fieldset.
204
   */
205
  public function updateSettingsAjax(array $form, FormStateInterface $form_state) {
206
    return $form['fields'][$this->fieldDefinition->getName()]['plugin']['settings_edit_form']['settings']['field_widget_display_settings'];
207
  }
208
209
  /**
210
   * {@inheritdoc}
211
   */
212
  public function settingsSummary() {
213
    $summary = [];
214
    $entity_browser_id = $this->getSetting('entity_browser');
215
    $field_widget_display = $this->getSetting('field_widget_display');
216
217
    if (empty($entity_browser_id)) {
218
      return [t('No entity browser selected.')];
219
    }
220
    else {
221
      if ($browser = $this->entityTypeManager->getStorage('entity_browser')->load($entity_browser_id)) {
222
        $summary[] = t('Entity browser: @browser', ['@browser' => $browser->label()]);
223
      }
224
      else {
225
        drupal_set_message(t('Missing entity browser!'), 'error');
226
        return [t('Missing entity browser!')];
227
      }
228
    }
229
230
    if (!empty($field_widget_display)) {
231
      $plugin = $this->fieldDisplayManager->getDefinition($field_widget_display);
232
      $summary[] = t('Entity display: @name', ['@name' => $plugin['label']]);
233
    }
234
235
    return $summary;
236
  }
237
238
  /**
239
   * {@inheritdoc}
240
   */
241
  public function flagErrors(FieldItemListInterface $items, ConstraintViolationListInterface $violations, array $form, FormStateInterface $form_state) {
242
    if ($violations->count() > 0) {
243
      /** @var \Symfony\Component\Validator\ConstraintViolation $violation */
244
      foreach ($violations as $offset => $violation) {
245
        // The value of the required field is checked through the "not null"
246
        // constraint, whose message is not very useful. We override it here for
247
        // better UX.
248
        if ($violation->getConstraint() instanceof NotNullConstraint) {
0 ignored issues
show
Bug introduced by
The class Drupal\Core\Validation\P...raint\NotNullConstraint does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
249
          $violations->set($offset, new ConstraintViolation(
250
            $this->t('@name field is required.', ['@name' => $items->getFieldDefinition()->getLabel()]),
251
            '',
252
            [],
253
            $violation->getRoot(),
254
            $violation->getPropertyPath(),
255
            $violation->getInvalidValue(),
256
            $violation->getPlural(),
257
            $violation->getCode(),
258
            $violation->getConstraint(),
259
            $violation->getCause()
260
          ));
261
        }
262
      }
263
    }
264
265
    parent::flagErrors($items, $violations, $form, $form_state);
266
  }
267
268
  /**
269
   * Returns a key used to store the previously loaded entity.
270
   *
271
   * @param \Drupal\Core\Field\FieldItemListInterface $items
272
   *   The field items.
273
   *
274
   * @return string
275
   *   A key for form state storage.
276
   */
277
  protected function getFormStateKey(FieldItemListInterface $items) {
278
    return $items->getEntity()->uuid() . ':' . $items->getFieldDefinition()->getName();
279
  }
280
281
  /**
282
   * {@inheritdoc}
283
   */
284
  function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
285
    $entity_type = $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type');
286
    $entity_storage = $this->entityTypeManager->getStorage($entity_type);
287
288
    $ids = [];
289
    $entities = [];
290
291
    // Determine if we're submitting and if submit came from this widget.
292
    $is_relevant_submit = FALSE;
293
    if (($trigger = $form_state->getTriggeringElement())) {
294
      // Can be triggered by hidden target_id element or "Remove" button.
295
      if (end($trigger['#parents']) === 'target_id' || (end($trigger['#parents']) === 'remove_button')) {
296
        $is_relevant_submit = TRUE;
297
298
        // In case there are more instances of this widget on the same page we
299
        // need to check if submit came from this instance.
300
        $field_name_key = end($trigger['#parents']) === 'target_id' ? 2 : static::$deleteDepth + 1;
301
        $field_name_key = sizeof($trigger['#parents']) - $field_name_key;
302
        $is_relevant_submit &= ($trigger['#parents'][$field_name_key] === $this->fieldDefinition->getName()) &&
303
          (array_slice($trigger['#parents'], 0, count($element['#field_parents'])) == $element['#field_parents']);
304
      }
305
    };
306
307
    if ($is_relevant_submit) {
308
      // Submit was triggered by hidden "target_id" element when entities were
309
      // added via entity browser.
310 View Code Duplication
      if (!empty($trigger['#ajax']['event']) && $trigger['#ajax']['event'] == 'entity_browser_value_updated') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
311
        $parents = $trigger['#parents'];
312
      }
313
      // Submit was triggered by one of the "Remove" buttons. We need to walk
314
      // few levels up to read value of "target_id" element.
315
      elseif ($trigger['#type'] == 'submit' && strpos($trigger['#name'], $this->fieldDefinition->getName() . '_remove_') === 0) {
316
        $parents = array_merge(array_slice($trigger['#parents'], 0, -static::$deleteDepth), ['target_id']);
317
      }
318
319
      if (isset($parents) && $value = $form_state->getValue($parents)) {
320
        $ids = explode(' ', $value);
321
        $entities = $entity_storage->loadMultiple($ids);
322
      }
323
    }
324
    // IDs from a previous request might be saved in the form state.
325
    elseif ($form_state->has(['entity_browser_widget', $this->getFormStateKey($items)])) {
326
      $ids = $form_state->get(['entity_browser_widget', $this->getFormStateKey($items)]);
327
      $entities = $entity_storage->loadMultiple($ids);
328
    }
329
    // We are loading for for the first time so we need to load any existing
330
    // values that might already exist on the entity. Also, remove any leftover
331
    // data from removed entity references.
332
    else {
333
      foreach ($items as $item) {
334
        if (isset($item->target_id)) {
335
          $entity = $entity_storage->load($item->target_id);
336
          if (!empty($entity)) {
337
            $entities[$item->target_id] = $entity;
338
          }
339
        }
340
      }
341
      $ids = array_keys($entities);
342
    }
343
    $ids = array_filter($ids);
344
    // We store current entity IDs as we might need them in future requests. If
345
    // some other part of the form triggers an AJAX request with #limit_validation_errors
346
    // we won't have access to the value of the target_id element and won't be
347
    // able to build the form as a result of that. This will cause missing
348
    // submit (Remove, Edit, ...) elements, which might result in unpredictable
349
    // results.
350
    $form_state->set(['entity_browser_widget', $this->getFormStateKey($items)], $ids);
351
352
    $hidden_id = Html::getUniqueId('edit-' . $this->fieldDefinition->getName() . '-target-id');
353
    $details_id = Html::getUniqueId('edit-' . $this->fieldDefinition->getName());
354
    /** @var \Drupal\entity_browser\EntityBrowserInterface $entity_browser */
355
    $entity_browser = $this->entityTypeManager->getStorage('entity_browser')->load($this->getSetting('entity_browser'));
356
357
    $element += [
358
      '#id' => $details_id,
359
      '#type' => 'details',
360
      '#open' => !empty($ids) || $this->getSetting('open'),
361
      '#required' => $this->fieldDefinition->isRequired(),
362
      'target_id' => [
363
        '#type' => 'hidden',
364
        '#id' => $hidden_id,
365
        // We need to repeat ID here as it is otherwise skipped when rendering.
366
        '#attributes' => ['id' => $hidden_id],
367
        '#default_value' => $ids,
368
        // #ajax is officially not supported for hidden elements but if we
369
        // specify event manually it works.
370
        '#ajax' => [
371
          'callback' => [get_class($this), 'updateWidgetCallback'],
372
          'wrapper' => $details_id,
373
          'event' => 'entity_browser_value_updated',
374
        ],
375
      ],
376
    ];
377
378
    $cardinality = $this->fieldDefinition->getFieldStorageDefinition()->getCardinality();
379
    if ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED || count($ids) < $cardinality) {
380
      $entity_browser_uuid = sha1(implode('-', array_merge($form['#parents'], [$this->fieldDefinition->getName(), $delta])));
381
      $entity_browser_display = $entity_browser->getDisplay();
382
      $entity_browser_display->setUuid($entity_browser_uuid);
383
384
      // Gather and set validators.
385
      $validators = [
386
        'entity_type' => ['type' => $entity_type],
387
        'cardinality' => ['cardinality' => $cardinality],
388
      ];
389
390
      $element['entity_browser'] = $entity_browser_display->displayEntityBrowser($form_state, $validators, []);
391
      $element['#attached']['library'][] = 'entity_browser/entity_reference';
392
      $element['#attached']['drupalSettings']['entity_browser'] = [
393
        $entity_browser->getDisplay()->getUuid() => [
394
          'cardinality' => $cardinality,
395
          'selector' => '#' . $element['target_id']['#attributes']['id'],
396
        ],
397
      ];
398
    }
399
400
    $field_parents = $element['#field_parents'];
401
402
    $element['current'] = $this->displayCurrentSelection($details_id, $field_parents, $entities);
403
404
    return $element;
405
  }
406
407
  /**
408
   * {@inheritdoc}
409
   */
410
  public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
411
    $ids = empty($values['target_id']) ? [] : explode(' ', trim($values['target_id']));
412
    $return = [];
413
    foreach ($ids as $id) {
414
      $return[]['target_id'] = $id;
415
    }
416
417
    return $return;
418
  }
419
420
  /**
421
   * AJAX form callback.
422
   */
423
  public static function updateWidgetCallback(array &$form, FormStateInterface $form_state) {
424
    $trigger = $form_state->getTriggeringElement();
425
    // AJAX requests can be triggered by hidden "target_id" element when entities
426
    // are added or by one of the "Remove" buttons. Depending on that we need to
427
    // figure out where root of the widget is in the form structure and use this
428
    // information to return correct part of the form.
429 View Code Duplication
    if (!empty($trigger['#ajax']['event']) && $trigger['#ajax']['event'] == 'entity_browser_value_updated') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
430
      $parents = array_slice($trigger['#array_parents'], 0, -1);
431
    }
432
    elseif ($trigger['#type'] == 'submit' && strpos($trigger['#name'], '_remove_')) {
433
      $parents = array_slice($trigger['#array_parents'], 0, -static::$deleteDepth);
434
    }
435
436
    return NestedArray::getValue($form, $parents);
0 ignored issues
show
Bug introduced by
The variable $parents does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
437
  }
438
439
  /**
440
   * {@inheritdoc}
441
   */
442
  public function errorElement(array $element, ConstraintViolationInterface $violation, array $form, FormStateInterface $form_state) {
443
    if (($trigger = $form_state->getTriggeringElement())) {
444
      // Can be triggered by "Remove" button.
445
      if (end($trigger['#parents']) === 'remove_button') {
446
        return FALSE;
447
      }
448
    }
449
    return parent::errorElement($element, $violation, $form, $form_state);
450
  }
451
452
  /**
453
   * Submit callback for remove buttons.
454
   */
455
  public static function removeItemSubmit(&$form, FormStateInterface $form_state) {
456
    $triggering_element = $form_state->getTriggeringElement();
457
    if (!empty($triggering_element['#attributes']['data-entity-id'])) {
458
      $id = $triggering_element['#attributes']['data-entity-id'];
459
      $parents = array_slice($triggering_element['#parents'], 0, -static::$deleteDepth);
460
      $array_parents = array_slice($triggering_element['#array_parents'], 0, -static::$deleteDepth);
461
462
      // Find and remove correct entity.
463
      $values = explode(' ', $form_state->getValue(array_merge($parents, ['target_id'])));
464
      $values = array_filter(
465
        $values,
466
        function($item) use ($id) {
467
          return $item != $id;
468
        }
469
      );
470
      $values = implode(' ', $values);
471
472
      // Set new value for this widget.
473
      $target_id_element = &NestedArray::getValue($form, array_merge($array_parents, ['target_id']));
474
      $form_state->setValueForElement($target_id_element, $values);
475
      NestedArray::setValue($form_state->getUserInput(), $target_id_element['#parents'], $values);
476
477
      // Rebuild form.
478
      $form_state->setRebuild();
479
    }
480
  }
481
482
  /**
483
   * Builds the render array for displaying the current results.
484
   *
485
   * @param string $details_id
486
   *   The ID for the details element.
487
   * @param string[] $field_parents
488
   *   Field parents.
489
   * @param \Drupal\Core\Entity\ContentEntityInterface[] $entities
490
   *
491
   * @return array
492
   *   The render array for the current selection.
493
   */
494
  protected function displayCurrentSelection($details_id, $field_parents, $entities) {
495
496
    $field_widget_display = $this->fieldDisplayManager->createInstance(
497
      $this->getSetting('field_widget_display'),
498
      $this->getSetting('field_widget_display_settings') + ['entity_type' => $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type')]
499
    );
500
501
    return [
502
      '#theme_wrappers' => ['container'],
503
      '#attributes' => ['class' => ['entities-list']],
504
      'items' => array_map(
505
        function (ContentEntityInterface $entity) use ($field_widget_display, $details_id, $field_parents) {
506
          $display = $field_widget_display->view($entity);
507
          if (is_string($display)) {
508
            $display = ['#markup' => $display];
509
          }
510
          return [
511
            '#theme_wrappers' => ['container'],
512
            '#attributes' => [
513
              'class' => ['item-container', Html::getClass($field_widget_display->getPluginId())],
514
              'data-entity-id' => $entity->id(),
515
            ],
516
            'display' => $display,
517
            'remove_button' => [
518
              '#type' => 'submit',
519
              '#value' => $this->t('Remove'),
520
              '#ajax' => [
521
                'callback' => [get_class($this), 'updateWidgetCallback'],
522
                'wrapper' => $details_id,
523
              ],
524
              '#submit' => [[get_class($this), 'removeItemSubmit']],
525
              '#name' => $this->fieldDefinition->getName() . '_remove_' . $entity->id(),
526
              '#limit_validation_errors' => [array_merge($field_parents, [$this->fieldDefinition->getName()])],
527
              '#attributes' => ['data-entity-id' => $entity->id()],
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
              ],
541
              '#access' => (bool) $this->getSetting('field_widget_edit'),
542
            ],
543
          ];
544
        },
545
        $entities
546
      ),
547
    ];
548
  }
549
}
550