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) { |
|
|
|
|
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) { |
|
|
|
|
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') { |
|
|
|
|
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') { |
|
|
|
|
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); |
|
|
|
|
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
|
|
|
|
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 thecomposer.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
orrequire-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 you have not tested against this specific condition, such errors might go unnoticed.