Completed
Push — 8.x-1.x ( 2a7b34...3d8643 )
by Janez
03:43
created

EntityReference   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 490
Duplicated Lines 2.86 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 26
Bugs 3 Features 4
Metric Value
dl 14
loc 490
rs 8.5454
c 26
b 3
f 4
wmc 49
lcom 1
cbo 2

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A defaultSettings() 0 10 1
B settingsForm() 0 81 5
B settingsSummary() 0 24 4
B flagErrors() 0 26 4
A create() 0 12 1
A updateSettingsAjax() 0 3 1
A getFormStateKey() 0 3 1
D formElement() 8 114 19
A massageFormValues() 0 9 3
B updateWidgetCallback() 6 15 5
B removeItemSubmit() 0 24 2
A displayCurrentSelection() 0 55 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like EntityReference often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use EntityReference, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * @file
5
 * Contains \Drupal\entity_browser\Plugin\Field\FieldWidget\EntityReference.
6
 */
7
8
namespace Drupal\entity_browser\Plugin\Field\FieldWidget;
9
10
use Drupal\Component\Utility\Html;
11
use Drupal\Component\Utility\NestedArray;
12
use Drupal\Core\Entity\ContentEntityInterface;
13
use Drupal\Core\Entity\EntityManagerInterface;
14
use Drupal\Core\Field\FieldDefinitionInterface;
15
use Drupal\Core\Field\FieldItemListInterface;
16
use Drupal\Core\Field\FieldStorageDefinitionInterface;
17
use Drupal\Core\Field\WidgetBase;
18
use Drupal\Core\Form\FormStateInterface;
19
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
20
use Drupal\Core\Url;
21
use Drupal\Core\Validation\Plugin\Validation\Constraint\NotNullConstraint;
22
use Drupal\entity_browser\Events\Events;
23
use Drupal\entity_browser\Events\RegisterJSCallbacks;
24
use Drupal\entity_browser\FieldWidgetDisplayManager;
25
use Symfony\Component\DependencyInjection\ContainerInterface;
26
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
27
use Symfony\Component\Validator\ConstraintViolation;
28
use Symfony\Component\Validator\ConstraintViolationListInterface;
29
30
/**
31
 * Plugin implementation of the 'entity_reference' widget for entity browser.
32
33
 * @FieldWidget(
34
 *   id = "entity_browser_entity_reference",
35
 *   label = @Translation("Entity browser"),
36
 *   description = @Translation("Uses entity browser to select entities."),
37
 *   multiple_values = TRUE,
38
 *   field_types = {
39
 *     "entity_reference"
40
 *   }
41
 * )
42
 */
43
class EntityReference extends WidgetBase implements ContainerFactoryPluginInterface {
44
45
  /**
46
   * Entity manager service
47
   *
48
   * @var \Drupal\Core\Entity\EntityManagerInterface
49
   */
50
  protected $entityManager;
51
52
  /**
53
   * Field widget display plugin manager.
54
   *
55
   * @var \Drupal\entity_browser\FieldWidgetDisplayManager
56
   */
57
  protected $fieldDisplayManager;
58
59
  /**
60
   * The depth of the delete button.
61
   *
62
   * This property exists so it can be changed if subclasses
63
   *
64
   * @var int
65
   */
66
  protected static $deleteDepth = 4;
67
68
  /**
69
   * Constructs widget plugin.
70
   *
71
   * @param string $plugin_id
72
   *   The plugin_id for the plugin instance.
73
   * @param mixed $plugin_definition
74
   *   The plugin implementation definition.
75
   * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
76
   *   The definition of the field to which the widget is associated.
77
   * @param array $settings
78
   *   The widget settings.
79
   * @param array $third_party_settings
80
   *   Any third party settings.
81
   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
82
   *   Entity manager service.
83
   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
84
   *   Event dispatcher.
85
   * @param \Drupal\entity_browser\FieldWidgetDisplayManager $field_display_manager
86
   *   Field widget display plugin manager.
87
   */
88
  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, EntityManagerInterface $entity_manager, EventDispatcherInterface $event_dispatcher, FieldWidgetDisplayManager $field_display_manager) {
89
    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
90
    $this->entityManager = $entity_manager;
91
    $this->fieldDisplayManager = $field_display_manager;
92
  }
93
94
  /**
95
   * {@inheritdoc}
96
   */
97
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
98
    return new static(
99
      $plugin_id,
100
      $plugin_definition,
101
      $configuration['field_definition'],
102
      $configuration['settings'],
103
      $configuration['third_party_settings'],
104
      $container->get('entity.manager'),
105
      $container->get('event_dispatcher'),
106
      $container->get('plugin.manager.entity_browser.field_widget_display')
107
    );
108
  }
109
110
  /**
111
   * {@inheritdoc}
112
   */
113
  public static function defaultSettings() {
114
    return array(
115
      'entity_browser' => NULL,
116
      'open' => FALSE,
117
      'field_widget_display' => NULL,
118
      'field_widget_edit' => TRUE,
119
      'field_widget_remove' => TRUE,
120
      'field_widget_display_settings' => [],
121
    ) + parent::defaultSettings();
122
  }
123
124
  /**
125
   * {@inheritdoc}
126
   */
127
  public function settingsForm(array $form, FormStateInterface $form_state) {
128
    $element = parent::settingsForm($form, $form_state);
129
130
    $browsers = [];
131
    /** @var \Drupal\entity_browser\EntityBrowserInterface $browser */
132
    foreach ($this->entityManager->getStorage('entity_browser')->loadMultiple() as $browser) {
133
      $browsers[$browser->id()] = $browser->label();
134
    }
135
136
    $element['entity_browser'] = [
137
      '#title' => t('Entity browser'),
138
      '#type' => 'select',
139
      '#default_value' => $this->getSetting('entity_browser'),
140
      '#options' => $browsers,
141
    ];
142
143
    $target_type = $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type');
144
    $entity_type = \Drupal::entityTypeManager()->getStorage($target_type)->getEntityType();
145
146
    $displays = [];
147
    foreach ($this->fieldDisplayManager->getDefinitions() as $id => $definition) {
148
      if ($this->fieldDisplayManager->createInstance($id)->isApplicable($entity_type)) {
149
        $displays[$id] = $definition['label'];
150
      }
151
    }
152
153
    $id = Html::getUniqueId('field-' . $this->fieldDefinition->getName() . '-display-settings-wrapper');
154
    $element['field_widget_display'] = [
155
      '#title' => t('Entity display plugin'),
156
      '#type' => 'select',
157
      '#default_value' => $this->getSetting('field_widget_display'),
158
      '#options' => $displays,
159
      '#ajax' => [
160
        'callback' => array($this, 'updateSettingsAjax'),
161
        'wrapper' => $id,
162
      ],
163
    ];
164
165
    $element['field_widget_edit'] = [
166
      '#title' => t('Display Edit button'),
167
      '#type' => 'checkbox',
168
      '#default_value' => $this->getSetting('field_widget_edit')
169
    ];
170
171
    $element['field_widget_remove'] = [
172
      '#title' => t('Display Remove button'),
173
      '#type' => 'checkbox',
174
      '#default_value' => $this->getSetting('field_widget_remove')
175
    ];
176
177
    $element['open'] = [
178
      '#title' => t('Show widget details as open by default'),
179
      '#type' => 'checkbox',
180
      '#default_value' => $this->getSetting('open')
181
    ];
182
183
    $element['field_widget_display_settings'] = [
184
      '#type' => 'fieldset',
185
      '#title' => t('Entity display plugin configuration'),
186
      '#tree' => TRUE,
187
      '#prefix' => '<div id="' . $id . '">',
188
      '#suffix' => '</div>',
189
    ];
190
191
    if ($this->getSetting('field_widget_display')) {
192
      $element['field_widget_display_settings'] += $this->fieldDisplayManager
193
        ->createInstance(
194
          $form_state->getValue(
195
            ['fields', $this->fieldDefinition->getName(), 'settings_edit_form', 'settings', 'field_widget_display'],
196
            $this->getSetting('field_widget_display')
197
          ),
198
          $form_state->getValue(
199
            ['fields', $this->fieldDefinition->getName(), 'settings_edit_form', 'settings', 'field_widget_display_settings'],
200
            $this->getSetting('field_widget_display_settings')
201
          ) + ['entity_type' => $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type')]
202
        )
203
        ->settingsForm($form, $form_state);
204
    }
205
206
    return $element;
207
  }
208
209
  /**
210
   * Ajax callback that updates field widget display settings fieldset.
211
   */
212
  public function updateSettingsAjax(array $form, FormStateInterface $form_state) {
213
    return $form['fields'][$this->fieldDefinition->getName()]['plugin']['settings_edit_form']['settings']['field_widget_display_settings'];
214
  }
215
216
  /**
217
   * {@inheritdoc}
218
   */
219
  public function settingsSummary() {
220
    $summary = [];
221
    $entity_browser_id = $this->getSetting('entity_browser');
222
    $field_widget_display = $this->getSetting('field_widget_display');
223
224
    if (empty($entity_browser_id)) {
225
      return [t('No entity browser selected.')];
226
    }
227
    else {
228
      if ($browser = $this->entityManager->getStorage('entity_browser')->load($entity_browser_id)) {
229
        $summary[] = t('Entity browser: @browser', ['@browser' => $browser->label()]);
230
      } else {
231
        drupal_set_message(t('Missing entity browser!'), 'error');
232
        return [t('Missing entity browser!')];
233
      }
234
    }
235
236
    if (!empty($field_widget_display)) {
237
      $plugin = $this->fieldDisplayManager->getDefinition($field_widget_display);
238
      $summary[] = t('Entity display: @name', ['@name' => $plugin['label']]);
239
    }
240
241
    return $summary;
242
  }
243
244
  /**
245
   * {@inheritdoc}
246
   */
247
  public function flagErrors(FieldItemListInterface $items, ConstraintViolationListInterface $violations, array $form, FormStateInterface $form_state) {
248
    if ($violations->count() > 0) {
249
      /** @var \Symfony\Component\Validator\ConstraintViolation $violation */
250
      foreach ($violations as $offset => $violation) {
251
        // The value of the required field is checked through the "not null"
252
        // constraint, whose message is not very useful. We override it here for
253
        // better UX.
254
        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...
255
          $violations->set($offset, new ConstraintViolation(
256
            $this->t('@name field is required.', ['@name' => $items->getFieldDefinition()->getLabel()]),
257
            '',
258
            [],
259
            $violation->getRoot(),
260
            $violation->getPropertyPath(),
261
            $violation->getInvalidValue(),
262
            $violation->getPlural(),
263
            $violation->getCode(),
264
            $violation->getConstraint(),
265
            $violation->getCause()
266
          ));
267
        }
268
      }
269
    }
270
271
    parent::flagErrors($items, $violations, $form, $form_state);
272
  }
273
274
  /**
275
   * Returns a key used to store the previously loaded entity.
276
   *
277
   * @param \Drupal\Core\Field\FieldItemListInterface $items
278
   *   The field items.
279
   *
280
   * @return string
281
   *   A key for form state storage.
282
   */
283
  protected function getFormStateKey(FieldItemListInterface $items) {
284
    return $items->getEntity()->uuid() . ':' . $items->getFieldDefinition()->getName();
285
  }
286
287
  /**
288
   * {@inheritdoc}
289
   */
290
  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...
291
    $entity_type = $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type');
292
    $entity_storage = $this->entityManager->getStorage($entity_type);
293
294
    $ids = [];
295
    $entities = [];
296
297
    // Determine if we're submitting and if submit came from this widget.
298
    $is_relevant_submit = FALSE;
299
    if (($trigger = $form_state->getTriggeringElement())) {
300
      // Can be triggered by hidden target_id element or "Remove" button.
301
      if (end($trigger['#parents']) === 'target_id' || (end($trigger['#parents']) === 'remove_button')) {
302
        $is_relevant_submit = TRUE;
303
304
        // In case there are more instances of this widget on the same page we
305
        // need to check if submit came from this instance.
306
        $field_name_key = end($trigger['#parents']) === 'target_id' ? 2 : static::$deleteDepth + 1;
307
        $field_name_key = sizeof($trigger['#parents']) - $field_name_key;
308
        $is_relevant_submit &= ($trigger['#parents'][$field_name_key] === $this->fieldDefinition->getName());
309
      }
310
    };
311
312
    if ($is_relevant_submit) {
313
      // Submit was triggered by hidden "target_id" element when entities were
314
      // added via entity browser.
315 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...
316
        $parents = $trigger['#parents'];
317
      }
318
      // Submit was triggered by one of the "Remove" buttons. We need to walk
319
      // few levels up to read value of "target_id" element.
320
      elseif ($trigger['#type'] == 'submit' && strpos($trigger['#name'], $this->fieldDefinition->getName() . '_remove_') === 0) {
321
        $parents = array_merge(array_slice($trigger['#parents'], 0, -static::$deleteDepth), ['target_id']);
322
      }
323
324
      if (isset($parents) && $value = $form_state->getValue($parents)) {
325
        $ids = explode(' ', $value);
326
        $entities = $entity_storage->loadMultiple($ids);
327
      }
328
    }
329
    // IDs from a previous request might be saved in the form state.
330
    elseif ($form_state->has(['entity_browser_widget', $this->getFormStateKey($items)])) {
331
      $ids = $form_state->get(['entity_browser_widget', $this->getFormStateKey($items)]);
332
      $entities = $entity_storage->loadMultiple($ids);
333
    }
334
    // We are loading for for the first time so we need to load any existing
335
    // values that might already exist on the entity. Also, remove any leftover
336
    // data from removed entity references.
337
    else {
338
      foreach ($items as $item) {
339
        if (isset($item->target_id)) {
340
          $entity = $entity_storage->load($item->target_id);
341
          if (!empty($entity)) {
342
            $entities[$item->target_id] = $entity;
343
          }
344
        }
345
      }
346
      $ids = array_keys($entities);
347
    }
348
    $ids = array_filter($ids);
349
    // We store current entity IDs as we might need them in future requests. If
350
    // some other part of the form triggers an AJAX request with #limit_validation_errors
351
    // we won't have access to the value of the target_id element and won't be
352
    // able to build the form as a result of that. This will cause missing
353
    // submit (Remove, Edit, ...) elements, which might result in unpredictable
354
    // results.
355
    $form_state->set(['entity_browser_widget', $this->getFormStateKey($items)], $ids);
356
357
    $hidden_id = Html::getUniqueId('edit-' . $this->fieldDefinition->getName() . '-target-id');
358
    $details_id = Html::getUniqueId('edit-' . $this->fieldDefinition->getName());
359
    /** @var \Drupal\entity_browser\EntityBrowserInterface $entity_browser */
360
    $entity_browser = $this->entityManager->getStorage('entity_browser')->load($this->getSetting('entity_browser'));
361
362
    $element += [
363
      '#id' => $details_id,
364
      '#type' => 'details',
365
      '#open' => !empty($ids) || $this->getSetting('open'),
366
      '#required' => $this->fieldDefinition->isRequired(),
367
      'target_id' => [
368
        '#type' => 'hidden',
369
        '#id' => $hidden_id,
370
        // We need to repeat ID here as it is otherwise skipped when rendering.
371
        '#attributes' => ['id' => $hidden_id],
372
        '#default_value' => $ids,
373
        // #ajax is officially not supported for hidden elements but if we
374
        // specify event manually it works.
375
        '#ajax' => [
376
          'callback' => [get_class($this), 'updateWidgetCallback'],
377
          'wrapper' => $details_id,
378
          'event' => 'entity_browser_value_updated',
379
        ],
380
      ],
381
    ];
382
383
    $cardinality = $this->fieldDefinition->getFieldStorageDefinition()->getCardinality();
384
    if ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED || count($ids) < $cardinality) {
385
      $entity_browser_uuid = sha1(implode('-', array_merge($form['#parents'], [$this->fieldDefinition->getName(), $delta])));
386
      $entity_browser_display = $entity_browser->getDisplay();
387
      $entity_browser_display->setUuid($entity_browser_uuid);
388
      $element['entity_browser'] = $entity_browser_display->displayEntityBrowser($form_state);
389
      $element['#attached']['library'][] = 'entity_browser/entity_reference';
390
      $element['#attached']['drupalSettings']['entity_browser'] = [
391
        $entity_browser->getDisplay()->getUuid() => [
392
          'cardinality' => $this->fieldDefinition->getFieldStorageDefinition()->getCardinality(),
393
          'selector' => '#'.$element['target_id']['#attributes']['id'],
394
        ]
395
      ];
396
    }
397
398
    $field_parents = $element['#field_parents'];
399
400
    $element['current'] = $this->displayCurrentSelection($details_id, $field_parents, $entities);
401
402
    return $element;
403
  }
404
405
  /**
406
   * {@inheritdoc}
407
   */
408
  public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
409
    $ids = empty($values['target_id']) ? [] : explode(' ', trim($values['target_id']));
410
    $return = [];
411
    foreach ($ids as $id) {
412
      $return[]['target_id'] = $id;
413
    }
414
415
    return $return;
416
  }
417
418
  /**
419
   * AJAX form callback.
420
   */
421
  public static function updateWidgetCallback(array &$form, FormStateInterface $form_state) {
422
    $trigger = $form_state->getTriggeringElement();
423
    // AJAX requests can be triggered by hidden "target_id" element when entities
424
    // are added or by one of the "Remove" buttons. Depending on that we need to
425
    // figure out where root of the widget is in the form structure and use this
426
    // information to return correct part of the form.
427 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...
428
      $parents = array_slice($trigger['#array_parents'], 0, -1);
429
    }
430
    elseif ($trigger['#type'] == 'submit' && strpos($trigger['#name'], '_remove_')) {
431
      $parents = array_slice($trigger['#array_parents'], 0, -static::$deleteDepth);
432
    }
433
434
    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...
435
  }
436
437
  /**
438
   * Submit callback for remove buttons.
439
   */
440
  public static function removeItemSubmit(&$form, FormStateInterface $form_state) {
441
    $triggering_element = $form_state->getTriggeringElement();
442
    if (!empty($triggering_element['#attributes']['data-entity-id'])) {
443
      $id = $triggering_element['#attributes']['data-entity-id'];
444
      $parents = array_slice($triggering_element['#parents'], 0, -static::$deleteDepth);
445
      $array_parents = array_slice($triggering_element['#array_parents'], 0, -static::$deleteDepth);
446
447
      // Find and remove correct entity.
448
      $values = explode(' ', $form_state->getValue(array_merge($parents, ['target_id'])));
449
      $values = array_filter(
450
        $values,
451
        function($item) use ($id) { return $item != $id; }
452
      );
453
      $values = implode(' ', $values);
454
455
      // Set new value for this widget.
456
      $target_id_element = &NestedArray::getValue($form, array_merge($array_parents, ['target_id']));
457
      $form_state->setValueForElement($target_id_element, $values);
458
      NestedArray::setValue($form_state->getUserInput(), $target_id_element['#parents'], $values);
459
460
      // Rebuild form.
461
      $form_state->setRebuild();
462
    }
463
  }
464
465
  /**
466
   * Builds the render array for displaying the current results.
467
   *
468
   * @param string $details_id
469
   *   The ID for the details element.
470
   * @param string[] $field_parents
471
   *   Field parents.
472
   * @param \Drupal\Core\Entity\ContentEntityInterface[] $entities
473
   *
474
   * @return array
475
   *   The render array for the current selection.
476
   */
477
  protected function displayCurrentSelection($details_id, $field_parents, $entities) {
478
479
    $field_widget_display = $this->fieldDisplayManager->createInstance(
480
      $this->getSetting('field_widget_display'),
481
      $this->getSetting('field_widget_display_settings') + ['entity_type' => $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type')]
482
    );
483
484
    return [
485
      '#theme_wrappers' => ['container'],
486
      '#attributes' => ['class' => ['entities-list']],
487
      'items' => array_map(
488
        function (ContentEntityInterface $entity) use ($field_widget_display, $details_id, $field_parents) {
489
          $display = $field_widget_display->view($entity);
490
          if (is_string($display)) {
491
            $display = ['#markup' => $display];
492
          }
493
          return [
494
            '#theme_wrappers' => ['container'],
495
            '#attributes' => [
496
              'class' => ['item-container', Html::getClass($field_widget_display->getPluginId())],
497
              'data-entity-id' => $entity->id()
498
            ],
499
            'display' => $display,
500
            'remove_button' => [
501
              '#type' => 'submit',
502
              '#value' => $this->t('Remove'),
503
              '#ajax' => [
504
                'callback' => [get_class($this), 'updateWidgetCallback'],
505
                'wrapper' => $details_id,
506
              ],
507
              '#submit' => [[get_class($this), 'removeItemSubmit']],
508
              '#name' => $this->fieldDefinition->getName() . '_remove_' . $entity->id(),
509
              '#limit_validation_errors' => [array_merge($field_parents, [$this->fieldDefinition->getName()])],
510
              '#attributes' => ['data-entity-id' => $entity->id()],
511
              '#access' => (bool) $this->getSetting('field_widget_remove')
512
            ],
513
            'edit_button' => [
514
              '#type' => 'submit',
515
              '#value' => $this->t('Edit'),
516
              '#ajax' => [
517
                'url' => Url::fromRoute(
518
                  'entity_browser.edit_form', [
519
                  'entity_type' => $entity->getEntityTypeId(),
520
                  'entity' => $entity->id()
521
                ]
522
                )
523
              ],
524
              '#access' => (bool) $this->getSetting('field_widget_edit')
525
            ]
526
          ];
527
        },
528
        $entities
529
      ),
530
    ];
531
  }
532
}
533