Completed
Pull Request — 8.x-1.x (#117)
by
unknown
04:17
created

EntityReference::updateSettingsAjax()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 2
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\KeyValueStore\KeyValueStoreExpirableInterface;
20
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
21
use Drupal\Core\Url;
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
28
/**
29
 * Plugin implementation of the 'entity_reference' widget for entity browser.
30
31
 * @FieldWidget(
32
 *   id = "entity_browser_entity_reference",
33
 *   label = @Translation("Entity browser"),
34
 *   description = @Translation("Uses entity browser to select entities."),
35
 *   multiple_values = TRUE,
36
 *   field_types = {
37
 *     "entity_reference"
38
 *   }
39
 * )
40
 */
41
class EntityReference extends WidgetBase implements ContainerFactoryPluginInterface {
42
43
  /**
44
   * Entity manager service
45
   *
46
   * @var \Drupal\Core\Entity\EntityManagerInterface
47
   */
48
  protected $entityManager;
49
50
  /**
51
   * Field widget display plugin manager.
52
   *
53
   * @var \Drupal\entity_browser\FieldWidgetDisplayManager
54
   */
55
  protected $fieldDisplayManager;
56
57
  /**
58
   * @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
59
   */
60
  protected $keyValue;
61
62
  /**
63
   * The depth of the delete button.
64
   *
65
   * This property exists so it can be changed if subclasses
66
   *
67
   * @var int
68
   */
69
  protected static $deleteDepth = 4;
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\EntityManagerInterface $entity_manager
85
   *   Entity 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\KeyValueStore\KeyValueStoreExpirableInterface $key_value
91
   *   The key value store.
92
   */
93 View Code Duplication
  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, KeyValueStoreExpirableInterface $key_value) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
94
    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
95
    $this->entityManager = $entity_manager;
96
    $this->fieldDisplayManager = $field_display_manager;
97
    $this->keyValue = $key_value;
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.manager'),
111
      $container->get('event_dispatcher'),
112
      $container->get('plugin.manager.entity_browser.field_widget_display'),
113
      $container->get('keyvalue.expirable')->get('entity_browser')
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' => NULL,
125
      'field_widget_edit' => TRUE,
126
      'field_widget_remove' => TRUE,
127
      'field_widget_display_settings' => [],
128
    ) + parent::defaultSettings();
129
  }
130
131
  /**
132
   * {@inheritdoc}
133
   */
134
  public function settingsForm(array $form, FormStateInterface $form_state) {
135
    $element = parent::settingsForm($form, $form_state);
136
137
    $browsers = [];
138
    /** @var \Drupal\entity_browser\EntityBrowserInterface $browser */
139
    foreach ($this->entityManager->getStorage('entity_browser')->loadMultiple() as $browser) {
140
      $browsers[$browser->id()] = $browser->label();
141
    }
142
143
    $element['entity_browser'] = [
144
      '#title' => t('Entity browser'),
145
      '#type' => 'select',
146
      '#default_value' => $this->getSetting('entity_browser'),
147
      '#options' => $browsers,
148
    ];
149
150
    $target_type = $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type');
151
    $entity_type = \Drupal::entityTypeManager()->getStorage($target_type)->getEntityType();
152
153
    $displays = [];
154
    foreach ($this->fieldDisplayManager->getDefinitions() as $id => $definition) {
155
      if ($this->fieldDisplayManager->createInstance($id)->isApplicable($entity_type)) {
156
        $displays[$id] = $definition['label'];
157
      }
158
    }
159
160
    $id = Html::getUniqueId('field-' . $this->fieldDefinition->getName() . '-display-settings-wrapper');
161
    $element['field_widget_display'] = [
162
      '#title' => t('Entity display plugin'),
163
      '#type' => 'select',
164
      '#default_value' => $this->getSetting('field_widget_display'),
165
      '#options' => $displays,
166
      '#ajax' => [
167
        'callback' => array($this, 'updateSettingsAjax'),
168
        'wrapper' => $id,
169
      ],
170
    ];
171
172
    $element['field_widget_edit'] = [
173
      '#title' => t('Display Edit button'),
174
      '#type' => 'checkbox',
175
      '#default_value' => $this->getSetting('field_widget_edit')
176
    ];
177
178
    $element['field_widget_remove'] = [
179
      '#title' => t('Display Remove button'),
180
      '#type' => 'checkbox',
181
      '#default_value' => $this->getSetting('field_widget_remove')
182
    ];
183
184
    $element['open'] = [
185
      '#title' => t('Show widget details as open by default'),
186
      '#type' => 'checkbox',
187
      '#default_value' => $this->getSetting('open')
188
    ];
189
190
    $element['field_widget_display_settings'] = [
191
      '#type' => 'fieldset',
192
      '#title' => t('Entity display plugin configuration'),
193
      '#tree' => TRUE,
194
      '#prefix' => '<div id="' . $id . '">',
195
      '#suffix' => '</div>',
196
    ];
197
198
    if ($this->getSetting('field_widget_display')) {
199
      $element['field_widget_display_settings'] += $this->fieldDisplayManager
200
        ->createInstance(
201
          $form_state->getValue(
202
            ['fields', $this->fieldDefinition->getName(), 'settings_edit_form', 'settings', 'field_widget_display'],
203
            $this->getSetting('field_widget_display')
204
          ),
205
          $form_state->getValue(
206
            ['fields', $this->fieldDefinition->getName(), 'settings_edit_form', 'settings', 'field_widget_display_settings'],
207
            $this->getSetting('field_widget_display_settings')
208
          ) + ['entity_type' => $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type')]
209
        )
210
        ->settingsForm($form, $form_state);
211
    }
212
213
    return $element;
214
  }
215
216
  /**
217
   * Ajax callback that updates field widget display settings fieldset.
218
   */
219
  public function updateSettingsAjax(array $form, FormStateInterface $form_state) {
220
    return $form['fields'][$this->fieldDefinition->getName()]['plugin']['settings_edit_form']['settings']['field_widget_display_settings'];
221
  }
222
223
  /**
224
   * {@inheritdoc}
225
   */
226
  public function settingsSummary() {
227
    $summary = [];
228
    $entity_browser_id = $this->getSetting('entity_browser');
229
    $field_widget_display = $this->getSetting('field_widget_display');
230
231
    if (empty($entity_browser_id)) {
232
      return [t('No entity browser selected.')];
233
    }
234
    else {
235
      if ($browser = $this->entityManager->getStorage('entity_browser')->load($entity_browser_id)) {
236
        $summary[] = t('Entity browser: @browser', ['@browser' => $browser->label()]);
237
      } else {
238
        drupal_set_message(t('Missing entity browser!'), 'error');
239
        return [t('Missing entity browser!')];
240
      }
241
    }
242
243
    if (!empty($field_widget_display)) {
244
      $plugin = $this->fieldDisplayManager->getDefinition($field_widget_display);
245
      $summary[] = t('Entity display: @name', ['@name' => $plugin['label']]);
246
    }
247
248
    return $summary;
249
  }
250
251
  /**
252
   * {@inheritdoc}
253
   */
254
  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...
255
    $entity_type = $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type');
256
    $entity_storage = $this->entityManager->getStorage($entity_type);
257
258
    $ids = [];
259
    $entities = [];
260
261
    // Determine if we're submitting and if submit came from this widget.
262
    $is_relevant_submit = FALSE;
263
    if (($trigger = $form_state->getTriggeringElement())) {
264
      // Can be triggered by hidden target_id element or "Remove" button.
265
      if (end($trigger['#parents']) === 'target_id' || (end($trigger['#parents']) === 'remove_button')) {
266
        $is_relevant_submit = TRUE;
267
268
        // In case there are more instances of this widget on the same page we
269
        // need to check if submit came from this instance.
270
        $field_name_key = end($trigger['#parents']) === 'target_id' ? 2 : static::$deleteDepth + 1;
271
        $field_name_key = sizeof($trigger['#parents']) - $field_name_key;
272
        $is_relevant_submit &= ($trigger['#parents'][$field_name_key] === $this->fieldDefinition->getName());
273
      }
274
    };
275
276
    if ($is_relevant_submit) {
277
      // Submit was triggered by hidden "target_id" element when entities were
278
      // added via entity browser.
279 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...
280
        $parents = $trigger['#parents'];
281
      }
282
      // Submit was triggered by one of the "Remove" buttons. We need to walk
283
      // few levels up to read value of "target_id" element.
284
      elseif ($trigger['#type'] == 'submit' && strpos($trigger['#name'], $this->fieldDefinition->getName() . '_remove_') === 0) {
285
        $parents = array_merge(array_slice($trigger['#parents'], 0, -static::$deleteDepth), ['target_id']);
286
      }
287
288
      if (isset($parents) && $value = $form_state->getValue($parents)) {
289
        $ids = explode(' ', $value);
290
        $entities = $entity_storage->loadMultiple($ids);
291
      }
292
    }
293
    // IDs from a previous request might be saved in the form state.
294
    elseif ($form_state->has(['entity_browser_widget', $this->fieldDefinition->getName()])) {
295
      $ids = $form_state->get(['entity_browser_widget', $this->fieldDefinition->getName()]);
296
      $entities = $entity_storage->loadMultiple($ids);
297
    }
298
    // We are loading for for the first time so we need to load any existing
299
    // values that might already exist on the entity. Also, remove any leftover
300
    // data from removed entity references.
301
    else {
302
      foreach ($items as $item) {
303
        if (isset($item->target_id)) {
304
          $entity = $entity_storage->load($item->target_id);
305
          if (!empty($entity)) {
306
            $entities[$item->target_id] = $entity;
307
          }
308
        }
309
      }
310
      $ids = array_keys($entities);
311
    }
312
    $ids = array_filter($ids);
313
    // We store current entity IDs as we might need them in future requests. If
314
    // some other part of the form triggers an AJAX request with #limit_validation_errors
315
    // we won't have access to the value of the target_id element and won't be
316
    // able to build the form as a result of that. This will cause missing
317
    // submit (Remove, Edit, ...) elements, which might result in unpredictable
318
    // results.
319
    $form_state->set(['entity_browser_widget', $this->fieldDefinition->getName()], $ids);
320
321
    $hidden_id = Html::getUniqueId('edit-' . $this->fieldDefinition->getName() . '-target-id');
322
    $details_id = Html::getUniqueId('edit-' . $this->fieldDefinition->getName());
323
    /** @var \Drupal\entity_browser\EntityBrowserInterface $entity_browser */
324
    $entity_browser = $this->entityManager->getStorage('entity_browser')->load($this->getSetting('entity_browser'));
325
326
    $element += [
327
      '#id' => $details_id,
328
      '#type' => 'details',
329
      '#open' => !empty($ids) || $this->getSetting('open'),
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' => $ids,
336
        // #ajax is officially not supported for hidden elements but if we
337
        // specify event manually it works.
338
        '#ajax' => [
339
          'callback' => [get_class($this), 'updateWidgetCallback'],
340
          'wrapper' => $details_id,
341
          'event' => 'entity_browser_value_updated',
342
        ],
343
      ],
344
    ];
345
346
347
    // Gather and set validators.
348
    // @todo Is there a better place to do that?
349
    $cardinality = $this->fieldDefinition->getFieldStorageDefinition()->getCardinality();
350
    $validators = $this->prepareValidators([
0 ignored issues
show
Unused Code introduced by
$validators is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
351
      'cardinality' => ['min' => $cardinality],
352
      'entity_type' => ['type' => $entity_type],
353
    ]);
354
355
    if ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED || count($ids) < $cardinality) {
356
      $entity_browser_uuid = sha1(implode('-', array_merge($form['#parents'], [$this->fieldDefinition->getName(), $delta])));
357
      $entity_browser_display = $entity_browser->getDisplay();
358
      $entity_browser_display->setUuid($entity_browser_uuid);
359
      $element['entity_browser'] = $entity_browser_display->displayEntityBrowser();
360
      $element['#attached']['library'][] = 'entity_browser/entity_reference';
361
      $element['#attached']['drupalSettings']['entity_browser'] = [
362
        $entity_browser->getDisplay()->getUuid() => [
363
          'cardinality' => $this->fieldDefinition->getFieldStorageDefinition()->getCardinality(),
364
          'selector' => '#'.$element['target_id']['#attributes']['id'],
365
        ]
366
      ];
367
    }
368
369
    $field_parents = $element['#field_parents'];
370
371
    $element['current'] = $this->displayCurrentSelection($details_id, $field_parents, $entities);
372
373
    return $element;
374
  }
375
376
  /**
377
   * {@inheritdoc}
378
   */
379
  public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
380
    $ids = empty($values['target_id']) ? [] : explode(' ', trim($values['target_id']));
381
    $return = [];
382
    foreach ($ids as $id) {
383
      $return[]['target_id'] = $id;
384
    }
385
386
    return $return;
387
  }
388
389
  /**
390
   * AJAX form callback.
391
   */
392
  public static function updateWidgetCallback(array &$form, FormStateInterface $form_state) {
393
    $trigger = $form_state->getTriggeringElement();
394
    // AJAX requests can be triggered by hidden "target_id" element when entities
395
    // are added or by one of the "Remove" buttons. Depending on that we need to
396
    // figure out where root of the widget is in the form structure and use this
397
    // information to return correct part of the form.
398 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...
399
      $parents = array_slice($trigger['#array_parents'], 0, -1);
400
    }
401
    elseif ($trigger['#type'] == 'submit' && strpos($trigger['#name'], '_remove_')) {
402
      $parents = array_slice($trigger['#array_parents'], 0, -static::$deleteDepth);
403
    }
404
405
    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...
406
  }
407
408
  /**
409
   * Submit callback for remove buttons.
410
   */
411
  public static function removeItemSubmit(&$form, FormStateInterface $form_state) {
412
    $triggering_element = $form_state->getTriggeringElement();
413
    if (!empty($triggering_element['#attributes']['data-entity-id'])) {
414
      $id = $triggering_element['#attributes']['data-entity-id'];
415
      $parents = array_slice($triggering_element['#parents'], 0, -static::$deleteDepth);
416
      $array_parents = array_slice($triggering_element['#array_parents'], 0, -static::$deleteDepth);
417
418
      // Find and remove correct entity.
419
      $values = explode(' ', $form_state->getValue(array_merge($parents, ['target_id'])));
420
      $values = array_filter(
421
        $values,
422
        function($item) use ($id) { return $item != $id; }
423
      );
424
      $values = implode(' ', $values);
425
426
      // Set new value for this widget.
427
      $target_id_element = &NestedArray::getValue($form, array_merge($array_parents, ['target_id']));
428
      $form_state->setValueForElement($target_id_element, $values);
429
      NestedArray::setValue($form_state->getUserInput(), $target_id_element['#parents'], $values);
430
431
      // Rebuild form.
432
      $form_state->setRebuild();
433
    }
434
  }
435
436
  /**
437
   * Builds the render array for displaying the current results.
438
   *
439
   * @param string $details_id
440
   *   The ID for the details element.
441
   * @param string[] $field_parents
442
   *   Field parents.
443
   * @param \Drupal\Core\Entity\ContentEntityInterface[] $entities
444
   *
445
   * @return array
446
   *   The render array for the current selection.
447
   */
448
  protected function displayCurrentSelection($details_id, $field_parents, $entities) {
449
450
    $field_widget_display = $this->fieldDisplayManager->createInstance(
451
      $this->getSetting('field_widget_display'),
452
      $this->getSetting('field_widget_display_settings') + ['entity_type' => $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type')]
453
    );
454
455
    return [
456
      '#theme_wrappers' => ['container'],
457
      '#attributes' => ['class' => ['entities-list']],
458
      'items' => array_map(
459
        function (ContentEntityInterface $entity) use ($field_widget_display, $details_id, $field_parents) {
460
          $display = $field_widget_display->view($entity);
461
          if (is_string($display)) {
462
            $display = ['#markup' => $display];
463
          }
464
          return [
465
            '#theme_wrappers' => ['container'],
466
            '#attributes' => [
467
              'class' => ['item-container', Html::getClass($field_widget_display->getPluginId())],
468
              'data-entity-id' => $entity->id()
469
            ],
470
            'display' => $display,
471
            'remove_button' => [
472
              '#type' => 'submit',
473
              '#value' => $this->t('Remove'),
474
              '#ajax' => [
475
                'callback' => [get_class($this), 'updateWidgetCallback'],
476
                'wrapper' => $details_id,
477
              ],
478
              '#submit' => [[get_class($this), 'removeItemSubmit']],
479
              '#name' => $this->fieldDefinition->getName() . '_remove_' . $entity->id(),
480
              '#limit_validation_errors' => [array_merge($field_parents, [$this->fieldDefinition->getName()])],
481
              '#attributes' => ['data-entity-id' => $entity->id()],
482
              '#access' => (bool) $this->getSetting('field_widget_remove')
483
            ],
484
            'edit_button' => [
485
              '#type' => 'submit',
486
              '#value' => $this->t('Edit'),
487
              '#ajax' => [
488
                'url' => Url::fromRoute(
489
                  'entity_browser.edit_form', [
490
                  'entity_type' => $entity->getEntityTypeId(),
491
                  'entity' => $entity->id()
492
                ]
493
                )
494
              ],
495
              '#access' => (bool) $this->getSetting('field_widget_edit')
496
            ]
497
          ];
498
        },
499
        $entities
500
      ),
501
    ];
502
  }
503
504
  /**
505
   * Prepare validators.
506
   *
507
   * Saves Entity Browser Widget validators in key/value storage if an identical
508
   * set of constraints is not already stored there.
509
   *
510
   * @param array $validators
511
   *   An array where keys are validator ids and values configurations for them.
512
   *
513
   * @return string
514
   *   The hash generated from hashing the validators array.
515
   */
516
  public function prepareValidators(array $validators) {
517
    // Generate the hash that we use as key for the key/value.
518
    $hash = md5(serialize($validators));
519
520
    if (!$this->keyValue->has($hash)) {
521
      $this->keyValue->set($hash, $validators);
522
    }
523
524
    return $hash;
525
  }
526
527
  /**
528
   * Get validators.
529
   *
530
   * @param \Drupal\entity_browser\string $hash
531
   *   The hash generated from hashing the validators array.
532
   *
533
   * @return mixed
534
   *   An array where keys are validator ids and values configurations for them
535
   *   or empty array if no validators are stored.
536
   */
537
  public function getValidators($hash) {
538
    return $this->keyValue->get($hash, []);
539
  }
540
}
541