Completed
Push — 8.x-1.x ( 795405...792375 )
by Janez
02:47
created

EntityBrowserElement::getSelectionModeOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 5
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 7
rs 9.4285
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
 *
28
 * Return value will be an array of selected entities, which will appear under
29
 * 'entities' key on the root level of the element's values in the form state.
30
 *
31
 * @FormElement("entity_browser")
32
 */
33
class EntityBrowserElement extends FormElement {
34
35
  /**
36
   * Indicating an entity browser can return an unlimited number of values.
37
   *
38
   * Note: When entity browser is used in Fields, cardinality is directly
39
   * propagated from Field settings, that's why this constant should be equal to
40
   * FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED.
41
   */
42
  const CARDINALITY_UNLIMITED = -1;
43
44
  /**
45
   * Selection from entity browser will be appended to existing list.
46
   *
47
   * When this selection mode is used, then entity browser will not be
48
   * populated with existing selection. Preselected list will be empty.
49
   *
50
   * Note: This option is also used by "js/entity_browser.common.js".
51
   */
52
  const SELECTION_MODE_APPEND = 'selection_append';
53
54
  /**
55
   * Selection from entity browser will be prepended to existing list.
56
   *
57
   * When this selection mode is used, then entity browser will not be
58
   * populated with existing selection. Preselected list will be empty.
59
   *
60
   * Note: This option is also used by "js/entity_browser.common.js".
61
   */
62
  const SELECTION_MODE_PREPEND = 'selection_prepend';
63
64
  /**
65
   * Selection from entity browser will replace existing.
66
   *
67
   * When this selection mode is used, then entity browser will be populated
68
   * with existing selection and returned selected list will replace existing
69
   * selection. This option requires entity browser selection display with
70
   * preselection support.
71
   *
72
   * Note: This option is also used by "js/entity_browser.common.js".
73
   */
74
  const SELECTION_MODE_EDIT = 'selection_edit';
75
76
  /**
77
   * {@inheritdoc}
78
   */
79
  public function getInfo() {
80
    $class = get_class($this);
81
    return [
82
      '#input' => TRUE,
83
      '#tree' => TRUE,
84
      '#cardinality' => static::CARDINALITY_UNLIMITED,
85
      '#selection_mode' => static::SELECTION_MODE_APPEND,
86
      '#process' => [[$class, 'processEntityBrowser']],
87
      '#default_value' => [],
88
      '#entity_browser_validators' => [],
89
      '#attached' => ['library' => ['entity_browser/common']],
90
    ];
91
  }
92
93
  /**
94
   * Get selection mode options.
95
   *
96
   * @return array
97
   *   Selection mode options.
98
   */
99
  public static function getSelectionModeOptions() {
100
    return [
101
      static::SELECTION_MODE_APPEND => t('Append to selection'),
102
      static::SELECTION_MODE_PREPEND => t('Prepend selection'),
103
      static::SELECTION_MODE_EDIT => t('Edit selection'),
104
    ];
105
  }
106
107
  /**
108
   * Check whether entity browser should be available for selection of entities.
109
   *
110
   * @param string $selection_mode
111
   *   Used selection mode.
112
   * @param int $cardinality
113
   *   Used cardinality.
114
   * @param int $preselection_size
115
   *   Preseletion size, if it's available.
116
   *
117
   * @return bool
118
   *   Returns positive if entity browser can be used.
119
   */
120
  public static function isEntityBrowserAvailable($selection_mode, $cardinality, $preselection_size) {
121
    if ($selection_mode == static::SELECTION_MODE_EDIT) {
122
      return TRUE;
123
    }
124
125
    $cardinality_exceeded =
126
      $cardinality != static::CARDINALITY_UNLIMITED
127
      && $preselection_size >= $cardinality;
128
129
    return !$cardinality_exceeded;
130
  }
131
132
  /**
133
   * Render API callback: Processes the entity browser element.
134
   */
135
  public static function processEntityBrowser(&$element, FormStateInterface $form_state, &$complete_form) {
136
    /** @var \Drupal\entity_browser\EntityBrowserInterface $entity_browser */
137
    if (is_string($element['#entity_browser'])) {
138
      $entity_browser = EntityBrowser::load($element['#entity_browser']);
139
    }
140
    else {
141
      $entity_browser = $element['#entity_browser'];
142
    }
143
144
    // Propagate selection if edit selection mode is used.
145
    $entity_browser_preselected_entities = [];
146
    if ($element['#selection_mode'] === static::SELECTION_MODE_EDIT) {
147
      $entity_browser->getSelectionDisplay()->checkPreselectionSupport();
148
149
      $entity_browser_preselected_entities = $element['#default_value'];
150
    }
151
152
    $default_value = implode(' ', array_map(
153
      function (EntityInterface $item) {
154
        return $item->getEntityTypeId() . ':' . $item->id();
155
      },
156
      $entity_browser_preselected_entities
157
    ));
158
    $validators = array_merge(
159
      $element['#entity_browser_validators'],
160
      ['cardinality' => ['cardinality' => $element['#cardinality']]]
161
    );
162
163
    $display = $entity_browser->getDisplay();
164
    $display->setUuid(sha1(implode('-', array_merge([$complete_form['#build_id']], $element['#parents']))));
165
    $element['entity_browser'] = [
166
      '#eb_parents' => array_merge($element['#parents'], ['entity_browser']),
167
    ];
168
    $element['entity_browser'] = $display->displayEntityBrowser(
169
      $element['entity_browser'],
170
      $form_state,
171
      $complete_form,
172
      [
173
        'validators' => $validators,
174
        'selected_entities' => $entity_browser_preselected_entities,
175
      ]
176
    );
177
178
    $hidden_id = Html::getUniqueId($element['#id'] . '-target');
179
    $element['entity_ids'] = [
180
      '#type' => 'hidden',
181
      '#id' => $hidden_id,
182
      // We need to repeat ID here as it is otherwise skipped when rendering.
183
      '#attributes' => ['id' => $hidden_id, 'class' => ['ed-target']],
184
      '#default_value' => $default_value,
185
    ];
186
187
    $element['#attached']['drupalSettings']['entity_browser'] = [
188
      $entity_browser->getDisplay()->getUuid() => [
189
        'cardinality' => $element['#cardinality'],
190
        'selection_mode' => $element['#selection_mode'],
191
        'selector' => '#' . $hidden_id,
192
      ],
193
    ];
194
195
    return $element;
196
  }
197
198
  /**
199
   * {@inheritdoc}
200
   */
201
  public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
202
    if ($input === FALSE) {
203
      return $element['#default_value'] ?: [];
204
    }
205
206
    $entities = [];
207
    if ($input['entity_ids']) {
208
      $entities = static::processEntityIds($input['entity_ids']);
209
    }
210
211
    return ['entities' => $entities];
212
  }
213
214
  /**
215
   * Processes entity IDs and gets array of loaded entities.
216
   *
217
   * @param array|string $ids
218
   *   Processes entity IDs as they are returned from the entity browser. They
219
   *   are in [entity_type_id]:[entity_id] form. Array of IDs or a
220
   *   space-delimited string is supported.
221
   *
222
   * @return \Drupal\Core\Entity\EntityInterface[]
223
   *   Array of entity objects.
224
   */
225
  public static function processEntityIds($ids) {
226
    if (!is_array($ids)) {
227
      $ids = array_filter(explode(' ', $ids));
228
    }
229
230
    return array_map(
231
      function ($item) {
232
        list($entity_type, $entity_id) = explode(':', $item);
233
        return \Drupal::entityTypeManager()->getStorage($entity_type)->load($entity_id);
234
      },
235
      $ids
236
    );
237
  }
238
239
  /**
240
   * Processes entity IDs and gets array of loaded entities.
241
   *
242
   * @param string $id
243
   *   Processes entity ID as it is returned from the entity browser. ID should
244
   *   be in [entity_type_id]:[entity_id] form.
245
   *
246
   * @return \Drupal\Core\Entity\EntityInterface
247
   *   Entity object.
248
   */
249
  public static function processEntityId($id) {
250
    $return = static::processEntityIds([$id]);
251
    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 251 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...
252
  }
253
254
}
255