EntityBrowserElement::processEntityIds()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 8
nc 2
nop 1
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Drupal\entity_browser\Element;
4
5
use Drupal\Component\Utility\Html;
6
use Drupal\Core\Form\FormStateInterface;
7
use Drupal\Core\Render\Element\FormElement;
8
use Drupal\entity_browser\Entity\EntityBrowser;
9
use Drupal\Core\Entity\EntityInterface;
10
11
/**
12
 * Provides an Entity Browser form element.
13
 *
14
 * Properties:
15
 * - #entity_browser: Entity browser or ID of the Entity browser to be used.
16
 * - #cardinality: (optional) Maximum number of items that are expected from
17
 *     the entity browser. Unlimited by default.
18
 * - #default_value: (optional) Array of entities that Entity browser should be
19
 *     initialized with. It's only applicable when edit selection mode is used.
20
 * - #entity_browser_validators: (optional) Array of validators that are to be
21
 *     passed to the entity browser. Array keys are plugin IDs and array values
22
 *     are plugin configuration values. Cardinality validator will be set
23
 *     automatically.
24
 * - #selection_mode: (optional) Determines how selection in entity browser will
25
 *     be handled. Will selection be appended/prepended or it will be replaced
26
 *     in case of editing. Defaults to append.
27
 * - #widget_context: (optional) Widget configuration overrides which enable
28
 *     use cases where the instance of a widget needs awareness of contextual
29
 *     configuration like field settings.
30
 *
31
 * Return value will be an array of selected entities, which will appear under
32
 * 'entities' key on the root level of the element's values in the form state.
33
 *
34
 * @FormElement("entity_browser")
35
 */
36
class EntityBrowserElement extends FormElement {
37
38
  /**
39
   * Indicating an entity browser can return an unlimited number of values.
40
   *
41
   * Note: When entity browser is used in Fields, cardinality is directly
42
   * propagated from Field settings, that's why this constant should be equal to
43
   * FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED.
44
   */
45
  const CARDINALITY_UNLIMITED = -1;
46
47
  /**
48
   * Selection from entity browser will be appended to existing list.
49
   *
50
   * When this selection mode is used, then entity browser will not be
51
   * populated with existing selection. Preselected list will be empty.
52
   *
53
   * Note: This option is also used by "js/entity_browser.common.js".
54
   */
55
  const SELECTION_MODE_APPEND = 'selection_append';
56
57
  /**
58
   * Selection from entity browser will be prepended to existing list.
59
   *
60
   * When this selection mode is used, then entity browser will not be
61
   * populated with existing selection. Preselected list will be empty.
62
   *
63
   * Note: This option is also used by "js/entity_browser.common.js".
64
   */
65
  const SELECTION_MODE_PREPEND = 'selection_prepend';
66
67
  /**
68
   * Selection from entity browser will replace existing.
69
   *
70
   * When this selection mode is used, then entity browser will be populated
71
   * with existing selection and returned selected list will replace existing
72
   * selection. This option requires entity browser selection display with
73
   * preselection support.
74
   *
75
   * Note: This option is also used by "js/entity_browser.common.js".
76
   */
77
  const SELECTION_MODE_EDIT = 'selection_edit';
78
79
  /**
80
   * {@inheritdoc}
81
   */
82
  public function getInfo() {
83
    $class = get_class($this);
84
    return [
85
      '#input' => TRUE,
86
      '#tree' => TRUE,
87
      '#cardinality' => static::CARDINALITY_UNLIMITED,
88
      '#selection_mode' => static::SELECTION_MODE_APPEND,
89
      '#process' => [[$class, 'processEntityBrowser']],
90
      '#default_value' => [],
91
      '#entity_browser_validators' => [],
92
      '#widget_context' => [],
93
      '#attached' => ['library' => ['entity_browser/common']],
94
    ];
95
  }
96
97
  /**
98
   * Get selection mode options.
99
   *
100
   * @return array
101
   *   Selection mode options.
102
   */
103
  public static function getSelectionModeOptions() {
104
    return [
105
      static::SELECTION_MODE_APPEND => t('Append to selection'),
106
      static::SELECTION_MODE_PREPEND => t('Prepend selection'),
107
      static::SELECTION_MODE_EDIT => t('Edit selection'),
108
    ];
109
  }
110
111
  /**
112
   * Check whether entity browser should be available for selection of entities.
113
   *
114
   * @param string $selection_mode
115
   *   Used selection mode.
116
   * @param int $cardinality
117
   *   Used cardinality.
118
   * @param int $preselection_size
119
   *   Preseletion size, if it's available.
120
   *
121
   * @return bool
122
   *   Returns positive if entity browser can be used.
123
   */
124
  public static function isEntityBrowserAvailable($selection_mode, $cardinality, $preselection_size) {
125
    if ($selection_mode == static::SELECTION_MODE_EDIT) {
126
      return TRUE;
127
    }
128
129
    $cardinality_exceeded =
130
      $cardinality != static::CARDINALITY_UNLIMITED
131
      && $preselection_size >= $cardinality;
132
133
    return !$cardinality_exceeded;
134
  }
135
136
  /**
137
   * Render API callback: Processes the entity browser element.
138
   */
139
  public static function processEntityBrowser(&$element, FormStateInterface $form_state, &$complete_form) {
140
    /** @var \Drupal\entity_browser\EntityBrowserInterface $entity_browser */
141
    if (is_string($element['#entity_browser'])) {
142
      $entity_browser = EntityBrowser::load($element['#entity_browser']);
143
    }
144
    else {
145
      $entity_browser = $element['#entity_browser'];
146
    }
147
148
    // Propagate selection if edit selection mode is used.
149
    $entity_browser_preselected_entities = [];
150
    if ($element['#selection_mode'] === static::SELECTION_MODE_EDIT) {
151
      $entity_browser->getSelectionDisplay()->checkPreselectionSupport();
152
153
      $entity_browser_preselected_entities = $element['#default_value'];
154
    }
155
156
    $default_value = implode(' ', array_map(
157
      function (EntityInterface $item) {
158
        return $item->getEntityTypeId() . ':' . $item->id();
159
      },
160
      $entity_browser_preselected_entities
161
    ));
162
    $validators = array_merge(
163
      $element['#entity_browser_validators'],
164
      ['cardinality' => ['cardinality' => $element['#cardinality']]]
165
    );
166
167
    // Display error message if the entity browser was not found.
168
    if (!$entity_browser) {
169
      $element['entity_browser'] = [
170
        '#type' => 'markup',
171
        '#markup' => is_string($element['#entity_browser']) ? t('Entity browser @browser not found.', ['@browser' => $element['#entity_browser']]) : t('Entity browser not found.'),
172
      ];
173
    }
174
    // Display entity_browser
175
    else {
176
      $display = $entity_browser->getDisplay();
177
      $display->setUuid(sha1(implode('-', array_merge([$complete_form['#build_id']], $element['#parents']))));
178
      $element['entity_browser'] = [
179
        '#eb_parents' => array_merge($element['#parents'], ['entity_browser']),
180
      ];
181
      $element['entity_browser'] = $display->displayEntityBrowser(
182
        $element['entity_browser'],
183
        $form_state,
184
        $complete_form,
185
        [
186
          'validators' => $validators,
187
          'selected_entities' => $entity_browser_preselected_entities,
188
          'widget_context' => $element['#widget_context'],
189
        ]
190
      );
191
192
      $hidden_id = Html::getUniqueId($element['#id'] . '-target');
193
      $element['entity_ids'] = [
194
        '#type' => 'hidden',
195
        '#id' => $hidden_id,
196
        // We need to repeat ID here as it is otherwise skipped when rendering.
197
        '#attributes' => ['id' => $hidden_id, 'class' => ['eb-target']],
198
        '#default_value' => $default_value,
199
      ];
200
201
      $element['#attached']['drupalSettings']['entity_browser'] = [
202
        $entity_browser->getDisplay()->getUuid() => [
203
          'cardinality' => $element['#cardinality'],
204
          'selection_mode' => $element['#selection_mode'],
205
          'selector' => '#' . $hidden_id,
206
        ],
207
      ];
208
    }
209
210
    return $element;
211
  }
212
213
  /**
214
   * {@inheritdoc}
215
   */
216
  public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
217
    if ($input === FALSE) {
218
      return $element['#default_value'] ?: [];
219
    }
220
221
    $entities = [];
222
    if ($input['entity_ids']) {
223
      $entities = static::processEntityIds($input['entity_ids']);
224
    }
225
226
    return ['entities' => $entities];
227
  }
228
229
  /**
230
   * Processes entity IDs and gets array of loaded entities.
231
   *
232
   * @param array|string $ids
233
   *   Processes entity IDs as they are returned from the entity browser. They
234
   *   are in [entity_type_id]:[entity_id] form. Array of IDs or a
235
   *   space-delimited string is supported.
236
   *
237
   * @return \Drupal\Core\Entity\EntityInterface[]
238
   *   Array of entity objects.
239
   */
240
  public static function processEntityIds($ids) {
241
    if (!is_array($ids)) {
242
      $ids = array_filter(explode(' ', $ids));
243
    }
244
245
    return array_map(
246
      function ($item) {
247
        list($entity_type, $entity_id) = explode(':', $item);
248
        return \Drupal::entityTypeManager()->getStorage($entity_type)->load($entity_id);
249
      },
250
      $ids
251
    );
252
  }
253
254
  /**
255
   * Processes entity IDs and gets array of loaded entities.
256
   *
257
   * @param string $id
258
   *   Processes entity ID as it is returned from the entity browser. ID should
259
   *   be in [entity_type_id]:[entity_id] form.
260
   *
261
   * @return \Drupal\Core\Entity\EntityInterface
262
   *   Entity object.
263
   */
264
  public static function processEntityId($id) {
265
    $return = static::processEntityIds([$id]);
266
    return current($return);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression current($return); of type Drupal\Core\Entity\EntityInterface|false adds false to the return on line 266 which is incompatible with the return type documented by Drupal\entity_browser\El...lement::processEntityId of type Drupal\Core\Entity\EntityInterface. It seems like you forgot to handle an error condition.
Loading history...
267
  }
268
269
}
270