Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 namespace Drupal\Core\Field;
|
Chris@0
|
4
|
Chris@0
|
5 use Drupal\Component\Utility\Html;
|
Chris@0
|
6 use Drupal\Component\Utility\NestedArray;
|
Chris@0
|
7 use Drupal\Component\Utility\SortArray;
|
Chris@0
|
8 use Drupal\Core\Form\FormStateInterface;
|
Chris@0
|
9 use Drupal\Core\Render\Element;
|
Chris@0
|
10 use Symfony\Component\Validator\ConstraintViolationInterface;
|
Chris@0
|
11 use Symfony\Component\Validator\ConstraintViolationListInterface;
|
Chris@0
|
12
|
Chris@0
|
13 /**
|
Chris@0
|
14 * Base class for 'Field widget' plugin implementations.
|
Chris@0
|
15 *
|
Chris@0
|
16 * @ingroup field_widget
|
Chris@0
|
17 */
|
Chris@0
|
18 abstract class WidgetBase extends PluginSettingsBase implements WidgetInterface {
|
Chris@0
|
19
|
Chris@0
|
20 use AllowedTagsXssTrait;
|
Chris@0
|
21
|
Chris@0
|
22 /**
|
Chris@0
|
23 * The field definition.
|
Chris@0
|
24 *
|
Chris@0
|
25 * @var \Drupal\Core\Field\FieldDefinitionInterface
|
Chris@0
|
26 */
|
Chris@0
|
27 protected $fieldDefinition;
|
Chris@0
|
28
|
Chris@0
|
29 /**
|
Chris@0
|
30 * The widget settings.
|
Chris@0
|
31 *
|
Chris@0
|
32 * @var array
|
Chris@0
|
33 */
|
Chris@0
|
34 protected $settings;
|
Chris@0
|
35
|
Chris@0
|
36 /**
|
Chris@0
|
37 * Constructs a WidgetBase object.
|
Chris@0
|
38 *
|
Chris@0
|
39 * @param string $plugin_id
|
Chris@0
|
40 * The plugin_id for the widget.
|
Chris@0
|
41 * @param mixed $plugin_definition
|
Chris@0
|
42 * The plugin implementation definition.
|
Chris@0
|
43 * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
|
Chris@0
|
44 * The definition of the field to which the widget is associated.
|
Chris@0
|
45 * @param array $settings
|
Chris@0
|
46 * The widget settings.
|
Chris@0
|
47 * @param array $third_party_settings
|
Chris@0
|
48 * Any third party settings.
|
Chris@0
|
49 */
|
Chris@0
|
50 public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings) {
|
Chris@0
|
51 parent::__construct([], $plugin_id, $plugin_definition);
|
Chris@0
|
52 $this->fieldDefinition = $field_definition;
|
Chris@0
|
53 $this->settings = $settings;
|
Chris@0
|
54 $this->thirdPartySettings = $third_party_settings;
|
Chris@0
|
55 }
|
Chris@0
|
56
|
Chris@0
|
57 /**
|
Chris@0
|
58 * {@inheritdoc}
|
Chris@0
|
59 */
|
Chris@0
|
60 public function form(FieldItemListInterface $items, array &$form, FormStateInterface $form_state, $get_delta = NULL) {
|
Chris@0
|
61 $field_name = $this->fieldDefinition->getName();
|
Chris@0
|
62 $parents = $form['#parents'];
|
Chris@0
|
63
|
Chris@0
|
64 // Store field information in $form_state.
|
Chris@0
|
65 if (!static::getWidgetState($parents, $field_name, $form_state)) {
|
Chris@0
|
66 $field_state = [
|
Chris@0
|
67 'items_count' => count($items),
|
Chris@0
|
68 'array_parents' => [],
|
Chris@0
|
69 ];
|
Chris@0
|
70 static::setWidgetState($parents, $field_name, $form_state, $field_state);
|
Chris@0
|
71 }
|
Chris@0
|
72
|
Chris@0
|
73 // Collect widget elements.
|
Chris@0
|
74 $elements = [];
|
Chris@0
|
75
|
Chris@0
|
76 // If the widget is handling multiple values (e.g Options), or if we are
|
Chris@0
|
77 // displaying an individual element, just get a single form element and make
|
Chris@0
|
78 // it the $delta value.
|
Chris@0
|
79 if ($this->handlesMultipleValues() || isset($get_delta)) {
|
Chris@0
|
80 $delta = isset($get_delta) ? $get_delta : 0;
|
Chris@0
|
81 $element = [
|
Chris@0
|
82 '#title' => $this->fieldDefinition->getLabel(),
|
Chris@0
|
83 '#description' => FieldFilteredMarkup::create(\Drupal::token()->replace($this->fieldDefinition->getDescription())),
|
Chris@0
|
84 ];
|
Chris@0
|
85 $element = $this->formSingleElement($items, $delta, $element, $form, $form_state);
|
Chris@0
|
86
|
Chris@0
|
87 if ($element) {
|
Chris@0
|
88 if (isset($get_delta)) {
|
Chris@0
|
89 // If we are processing a specific delta value for a field where the
|
Chris@0
|
90 // field module handles multiples, set the delta in the result.
|
Chris@0
|
91 $elements[$delta] = $element;
|
Chris@0
|
92 }
|
Chris@0
|
93 else {
|
Chris@0
|
94 // For fields that handle their own processing, we cannot make
|
Chris@0
|
95 // assumptions about how the field is structured, just merge in the
|
Chris@0
|
96 // returned element.
|
Chris@0
|
97 $elements = $element;
|
Chris@0
|
98 }
|
Chris@0
|
99 }
|
Chris@0
|
100 }
|
Chris@0
|
101 // If the widget does not handle multiple values itself, (and we are not
|
Chris@0
|
102 // displaying an individual element), process the multiple value form.
|
Chris@0
|
103 else {
|
Chris@0
|
104 $elements = $this->formMultipleElements($items, $form, $form_state);
|
Chris@0
|
105 }
|
Chris@0
|
106
|
Chris@0
|
107 // Populate the 'array_parents' information in $form_state->get('field')
|
Chris@0
|
108 // after the form is built, so that we catch changes in the form structure
|
Chris@0
|
109 // performed in alter() hooks.
|
Chris@0
|
110 $elements['#after_build'][] = [get_class($this), 'afterBuild'];
|
Chris@0
|
111 $elements['#field_name'] = $field_name;
|
Chris@0
|
112 $elements['#field_parents'] = $parents;
|
Chris@0
|
113 // Enforce the structure of submitted values.
|
Chris@0
|
114 $elements['#parents'] = array_merge($parents, [$field_name]);
|
Chris@0
|
115 // Most widgets need their internal structure preserved in submitted values.
|
Chris@0
|
116 $elements += ['#tree' => TRUE];
|
Chris@0
|
117
|
Chris@0
|
118 return [
|
Chris@0
|
119 // Aid in theming of widgets by rendering a classified container.
|
Chris@0
|
120 '#type' => 'container',
|
Chris@0
|
121 // Assign a different parent, to keep the main id for the widget itself.
|
Chris@0
|
122 '#parents' => array_merge($parents, [$field_name . '_wrapper']),
|
Chris@0
|
123 '#attributes' => [
|
Chris@0
|
124 'class' => [
|
Chris@0
|
125 'field--type-' . Html::getClass($this->fieldDefinition->getType()),
|
Chris@0
|
126 'field--name-' . Html::getClass($field_name),
|
Chris@0
|
127 'field--widget-' . Html::getClass($this->getPluginId()),
|
Chris@0
|
128 ],
|
Chris@0
|
129 ],
|
Chris@0
|
130 'widget' => $elements,
|
Chris@0
|
131 ];
|
Chris@0
|
132 }
|
Chris@0
|
133
|
Chris@0
|
134 /**
|
Chris@0
|
135 * Special handling to create form elements for multiple values.
|
Chris@0
|
136 *
|
Chris@0
|
137 * Handles generic features for multiple fields:
|
Chris@0
|
138 * - number of widgets
|
Chris@0
|
139 * - AHAH-'add more' button
|
Chris@0
|
140 * - table display and drag-n-drop value reordering
|
Chris@0
|
141 */
|
Chris@0
|
142 protected function formMultipleElements(FieldItemListInterface $items, array &$form, FormStateInterface $form_state) {
|
Chris@0
|
143 $field_name = $this->fieldDefinition->getName();
|
Chris@0
|
144 $cardinality = $this->fieldDefinition->getFieldStorageDefinition()->getCardinality();
|
Chris@0
|
145 $parents = $form['#parents'];
|
Chris@0
|
146
|
Chris@0
|
147 // Determine the number of widgets to display.
|
Chris@0
|
148 switch ($cardinality) {
|
Chris@0
|
149 case FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED:
|
Chris@0
|
150 $field_state = static::getWidgetState($parents, $field_name, $form_state);
|
Chris@0
|
151 $max = $field_state['items_count'];
|
Chris@0
|
152 $is_multiple = TRUE;
|
Chris@0
|
153 break;
|
Chris@0
|
154
|
Chris@0
|
155 default:
|
Chris@0
|
156 $max = $cardinality - 1;
|
Chris@0
|
157 $is_multiple = ($cardinality > 1);
|
Chris@0
|
158 break;
|
Chris@0
|
159 }
|
Chris@0
|
160
|
Chris@0
|
161 $title = $this->fieldDefinition->getLabel();
|
Chris@0
|
162 $description = FieldFilteredMarkup::create(\Drupal::token()->replace($this->fieldDefinition->getDescription()));
|
Chris@0
|
163
|
Chris@0
|
164 $elements = [];
|
Chris@0
|
165
|
Chris@0
|
166 for ($delta = 0; $delta <= $max; $delta++) {
|
Chris@0
|
167 // Add a new empty item if it doesn't exist yet at this delta.
|
Chris@0
|
168 if (!isset($items[$delta])) {
|
Chris@0
|
169 $items->appendItem();
|
Chris@0
|
170 }
|
Chris@0
|
171
|
Chris@0
|
172 // For multiple fields, title and description are handled by the wrapping
|
Chris@0
|
173 // table.
|
Chris@0
|
174 if ($is_multiple) {
|
Chris@0
|
175 $element = [
|
Chris@0
|
176 '#title' => $this->t('@title (value @number)', ['@title' => $title, '@number' => $delta + 1]),
|
Chris@0
|
177 '#title_display' => 'invisible',
|
Chris@0
|
178 '#description' => '',
|
Chris@0
|
179 ];
|
Chris@0
|
180 }
|
Chris@0
|
181 else {
|
Chris@0
|
182 $element = [
|
Chris@0
|
183 '#title' => $title,
|
Chris@0
|
184 '#title_display' => 'before',
|
Chris@0
|
185 '#description' => $description,
|
Chris@0
|
186 ];
|
Chris@0
|
187 }
|
Chris@0
|
188
|
Chris@0
|
189 $element = $this->formSingleElement($items, $delta, $element, $form, $form_state);
|
Chris@0
|
190
|
Chris@0
|
191 if ($element) {
|
Chris@0
|
192 // Input field for the delta (drag-n-drop reordering).
|
Chris@0
|
193 if ($is_multiple) {
|
Chris@0
|
194 // We name the element '_weight' to avoid clashing with elements
|
Chris@0
|
195 // defined by widget.
|
Chris@0
|
196 $element['_weight'] = [
|
Chris@0
|
197 '#type' => 'weight',
|
Chris@0
|
198 '#title' => $this->t('Weight for row @number', ['@number' => $delta + 1]),
|
Chris@0
|
199 '#title_display' => 'invisible',
|
Chris@0
|
200 // Note: this 'delta' is the FAPI #type 'weight' element's property.
|
Chris@0
|
201 '#delta' => $max,
|
Chris@0
|
202 '#default_value' => $items[$delta]->_weight ?: $delta,
|
Chris@0
|
203 '#weight' => 100,
|
Chris@0
|
204 ];
|
Chris@0
|
205 }
|
Chris@0
|
206
|
Chris@0
|
207 $elements[$delta] = $element;
|
Chris@0
|
208 }
|
Chris@0
|
209 }
|
Chris@0
|
210
|
Chris@0
|
211 if ($elements) {
|
Chris@0
|
212 $elements += [
|
Chris@0
|
213 '#theme' => 'field_multiple_value_form',
|
Chris@0
|
214 '#field_name' => $field_name,
|
Chris@0
|
215 '#cardinality' => $cardinality,
|
Chris@0
|
216 '#cardinality_multiple' => $this->fieldDefinition->getFieldStorageDefinition()->isMultiple(),
|
Chris@0
|
217 '#required' => $this->fieldDefinition->isRequired(),
|
Chris@0
|
218 '#title' => $title,
|
Chris@0
|
219 '#description' => $description,
|
Chris@0
|
220 '#max_delta' => $max,
|
Chris@0
|
221 ];
|
Chris@0
|
222
|
Chris@0
|
223 // Add 'add more' button, if not working with a programmed form.
|
Chris@0
|
224 if ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED && !$form_state->isProgrammed()) {
|
Chris@0
|
225 $id_prefix = implode('-', array_merge($parents, [$field_name]));
|
Chris@0
|
226 $wrapper_id = Html::getUniqueId($id_prefix . '-add-more-wrapper');
|
Chris@0
|
227 $elements['#prefix'] = '<div id="' . $wrapper_id . '">';
|
Chris@0
|
228 $elements['#suffix'] = '</div>';
|
Chris@0
|
229
|
Chris@0
|
230 $elements['add_more'] = [
|
Chris@0
|
231 '#type' => 'submit',
|
Chris@0
|
232 '#name' => strtr($id_prefix, '-', '_') . '_add_more',
|
Chris@0
|
233 '#value' => t('Add another item'),
|
Chris@0
|
234 '#attributes' => ['class' => ['field-add-more-submit']],
|
Chris@0
|
235 '#limit_validation_errors' => [array_merge($parents, [$field_name])],
|
Chris@0
|
236 '#submit' => [[get_class($this), 'addMoreSubmit']],
|
Chris@0
|
237 '#ajax' => [
|
Chris@0
|
238 'callback' => [get_class($this), 'addMoreAjax'],
|
Chris@0
|
239 'wrapper' => $wrapper_id,
|
Chris@0
|
240 'effect' => 'fade',
|
Chris@0
|
241 ],
|
Chris@0
|
242 ];
|
Chris@0
|
243 }
|
Chris@0
|
244 }
|
Chris@0
|
245
|
Chris@0
|
246 return $elements;
|
Chris@0
|
247 }
|
Chris@0
|
248
|
Chris@0
|
249 /**
|
Chris@0
|
250 * After-build handler for field elements in a form.
|
Chris@0
|
251 *
|
Chris@0
|
252 * This stores the final location of the field within the form structure so
|
Chris@0
|
253 * that flagErrors() can assign validation errors to the right form element.
|
Chris@0
|
254 */
|
Chris@0
|
255 public static function afterBuild(array $element, FormStateInterface $form_state) {
|
Chris@0
|
256 $parents = $element['#field_parents'];
|
Chris@0
|
257 $field_name = $element['#field_name'];
|
Chris@0
|
258
|
Chris@0
|
259 $field_state = static::getWidgetState($parents, $field_name, $form_state);
|
Chris@0
|
260 $field_state['array_parents'] = $element['#array_parents'];
|
Chris@0
|
261 static::setWidgetState($parents, $field_name, $form_state, $field_state);
|
Chris@0
|
262
|
Chris@0
|
263 return $element;
|
Chris@0
|
264 }
|
Chris@0
|
265
|
Chris@0
|
266 /**
|
Chris@0
|
267 * Submission handler for the "Add another item" button.
|
Chris@0
|
268 */
|
Chris@0
|
269 public static function addMoreSubmit(array $form, FormStateInterface $form_state) {
|
Chris@0
|
270 $button = $form_state->getTriggeringElement();
|
Chris@0
|
271
|
Chris@0
|
272 // Go one level up in the form, to the widgets container.
|
Chris@0
|
273 $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -1));
|
Chris@0
|
274 $field_name = $element['#field_name'];
|
Chris@0
|
275 $parents = $element['#field_parents'];
|
Chris@0
|
276
|
Chris@0
|
277 // Increment the items count.
|
Chris@0
|
278 $field_state = static::getWidgetState($parents, $field_name, $form_state);
|
Chris@0
|
279 $field_state['items_count']++;
|
Chris@0
|
280 static::setWidgetState($parents, $field_name, $form_state, $field_state);
|
Chris@0
|
281
|
Chris@0
|
282 $form_state->setRebuild();
|
Chris@0
|
283 }
|
Chris@0
|
284
|
Chris@0
|
285 /**
|
Chris@0
|
286 * Ajax callback for the "Add another item" button.
|
Chris@0
|
287 *
|
Chris@0
|
288 * This returns the new page content to replace the page content made obsolete
|
Chris@0
|
289 * by the form submission.
|
Chris@0
|
290 */
|
Chris@0
|
291 public static function addMoreAjax(array $form, FormStateInterface $form_state) {
|
Chris@0
|
292 $button = $form_state->getTriggeringElement();
|
Chris@0
|
293
|
Chris@0
|
294 // Go one level up in the form, to the widgets container.
|
Chris@0
|
295 $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -1));
|
Chris@0
|
296
|
Chris@0
|
297 // Ensure the widget allows adding additional items.
|
Chris@0
|
298 if ($element['#cardinality'] != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
|
Chris@0
|
299 return;
|
Chris@0
|
300 }
|
Chris@0
|
301
|
Chris@0
|
302 // Add a DIV around the delta receiving the Ajax effect.
|
Chris@0
|
303 $delta = $element['#max_delta'];
|
Chris@0
|
304 $element[$delta]['#prefix'] = '<div class="ajax-new-content">' . (isset($element[$delta]['#prefix']) ? $element[$delta]['#prefix'] : '');
|
Chris@0
|
305 $element[$delta]['#suffix'] = (isset($element[$delta]['#suffix']) ? $element[$delta]['#suffix'] : '') . '</div>';
|
Chris@0
|
306
|
Chris@0
|
307 return $element;
|
Chris@0
|
308 }
|
Chris@0
|
309
|
Chris@0
|
310 /**
|
Chris@0
|
311 * Generates the form element for a single copy of the widget.
|
Chris@0
|
312 */
|
Chris@0
|
313 protected function formSingleElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
|
Chris@0
|
314 $element += [
|
Chris@0
|
315 '#field_parents' => $form['#parents'],
|
Chris@0
|
316 // Only the first widget should be required.
|
Chris@0
|
317 '#required' => $delta == 0 && $this->fieldDefinition->isRequired(),
|
Chris@0
|
318 '#delta' => $delta,
|
Chris@0
|
319 '#weight' => $delta,
|
Chris@0
|
320 ];
|
Chris@0
|
321
|
Chris@0
|
322 $element = $this->formElement($items, $delta, $element, $form, $form_state);
|
Chris@0
|
323
|
Chris@0
|
324 if ($element) {
|
Chris@0
|
325 // Allow modules to alter the field widget form element.
|
Chris@0
|
326 $context = [
|
Chris@0
|
327 'form' => $form,
|
Chris@0
|
328 'widget' => $this,
|
Chris@0
|
329 'items' => $items,
|
Chris@0
|
330 'delta' => $delta,
|
Chris@0
|
331 'default' => $this->isDefaultValueWidget($form_state),
|
Chris@0
|
332 ];
|
Chris@0
|
333 \Drupal::moduleHandler()->alter(['field_widget_form', 'field_widget_' . $this->getPluginId() . '_form'], $element, $form_state, $context);
|
Chris@0
|
334 }
|
Chris@0
|
335
|
Chris@0
|
336 return $element;
|
Chris@0
|
337 }
|
Chris@0
|
338
|
Chris@0
|
339 /**
|
Chris@0
|
340 * {@inheritdoc}
|
Chris@0
|
341 */
|
Chris@0
|
342 public function extractFormValues(FieldItemListInterface $items, array $form, FormStateInterface $form_state) {
|
Chris@0
|
343 $field_name = $this->fieldDefinition->getName();
|
Chris@0
|
344
|
Chris@0
|
345 // Extract the values from $form_state->getValues().
|
Chris@0
|
346 $path = array_merge($form['#parents'], [$field_name]);
|
Chris@0
|
347 $key_exists = NULL;
|
Chris@0
|
348 $values = NestedArray::getValue($form_state->getValues(), $path, $key_exists);
|
Chris@0
|
349
|
Chris@0
|
350 if ($key_exists) {
|
Chris@0
|
351 // Account for drag-and-drop reordering if needed.
|
Chris@0
|
352 if (!$this->handlesMultipleValues()) {
|
Chris@0
|
353 // Remove the 'value' of the 'add more' button.
|
Chris@0
|
354 unset($values['add_more']);
|
Chris@0
|
355
|
Chris@0
|
356 // The original delta, before drag-and-drop reordering, is needed to
|
Chris@0
|
357 // route errors to the correct form element.
|
Chris@0
|
358 foreach ($values as $delta => &$value) {
|
Chris@0
|
359 $value['_original_delta'] = $delta;
|
Chris@0
|
360 }
|
Chris@0
|
361
|
Chris@0
|
362 usort($values, function ($a, $b) {
|
Chris@0
|
363 return SortArray::sortByKeyInt($a, $b, '_weight');
|
Chris@0
|
364 });
|
Chris@0
|
365 }
|
Chris@0
|
366
|
Chris@0
|
367 // Let the widget massage the submitted values.
|
Chris@0
|
368 $values = $this->massageFormValues($values, $form, $form_state);
|
Chris@0
|
369
|
Chris@0
|
370 // Assign the values and remove the empty ones.
|
Chris@0
|
371 $items->setValue($values);
|
Chris@0
|
372 $items->filterEmptyItems();
|
Chris@0
|
373
|
Chris@0
|
374 // Put delta mapping in $form_state, so that flagErrors() can use it.
|
Chris@0
|
375 $field_state = static::getWidgetState($form['#parents'], $field_name, $form_state);
|
Chris@0
|
376 foreach ($items as $delta => $item) {
|
Chris@0
|
377 $field_state['original_deltas'][$delta] = isset($item->_original_delta) ? $item->_original_delta : $delta;
|
Chris@0
|
378 unset($item->_original_delta, $item->_weight);
|
Chris@0
|
379 }
|
Chris@0
|
380 static::setWidgetState($form['#parents'], $field_name, $form_state, $field_state);
|
Chris@0
|
381 }
|
Chris@0
|
382 }
|
Chris@0
|
383
|
Chris@0
|
384 /**
|
Chris@0
|
385 * {@inheritdoc}
|
Chris@0
|
386 */
|
Chris@0
|
387 public function flagErrors(FieldItemListInterface $items, ConstraintViolationListInterface $violations, array $form, FormStateInterface $form_state) {
|
Chris@0
|
388 $field_name = $this->fieldDefinition->getName();
|
Chris@0
|
389
|
Chris@0
|
390 $field_state = static::getWidgetState($form['#parents'], $field_name, $form_state);
|
Chris@0
|
391
|
Chris@0
|
392 if ($violations->count()) {
|
Chris@0
|
393 // Locate the correct element in the form.
|
Chris@0
|
394 $element = NestedArray::getValue($form_state->getCompleteForm(), $field_state['array_parents']);
|
Chris@0
|
395
|
Chris@0
|
396 // Do not report entity-level validation errors if Form API errors have
|
Chris@0
|
397 // already been reported for the field.
|
Chris@0
|
398 // @todo Field validation should not be run on fields with FAPI errors to
|
Chris@0
|
399 // begin with. See https://www.drupal.org/node/2070429.
|
Chris@0
|
400 $element_path = implode('][', $element['#parents']);
|
Chris@0
|
401 if ($reported_errors = $form_state->getErrors()) {
|
Chris@0
|
402 foreach (array_keys($reported_errors) as $error_path) {
|
Chris@0
|
403 if (strpos($error_path, $element_path) === 0) {
|
Chris@0
|
404 return;
|
Chris@0
|
405 }
|
Chris@0
|
406 }
|
Chris@0
|
407 }
|
Chris@0
|
408
|
Chris@0
|
409 // Only set errors if the element is visible.
|
Chris@0
|
410 if (Element::isVisibleElement($element)) {
|
Chris@0
|
411 $handles_multiple = $this->handlesMultipleValues();
|
Chris@0
|
412
|
Chris@0
|
413 $violations_by_delta = [];
|
Chris@0
|
414 foreach ($violations as $violation) {
|
Chris@0
|
415 // Separate violations by delta.
|
Chris@0
|
416 $property_path = explode('.', $violation->getPropertyPath());
|
Chris@0
|
417 $delta = array_shift($property_path);
|
Chris@0
|
418 $violations_by_delta[$delta][] = $violation;
|
Chris@0
|
419 $violation->arrayPropertyPath = $property_path;
|
Chris@0
|
420 }
|
Chris@0
|
421
|
Chris@0
|
422 /** @var \Symfony\Component\Validator\ConstraintViolationInterface[] $delta_violations */
|
Chris@0
|
423 foreach ($violations_by_delta as $delta => $delta_violations) {
|
Chris@0
|
424 // Pass violations to the main element:
|
Chris@0
|
425 // - if this is a multiple-value widget,
|
Chris@0
|
426 // - or if the violations are at the ItemList level.
|
Chris@0
|
427 if ($handles_multiple || !is_numeric($delta)) {
|
Chris@0
|
428 $delta_element = $element;
|
Chris@0
|
429 }
|
Chris@0
|
430 // Otherwise, pass errors by delta to the corresponding sub-element.
|
Chris@0
|
431 else {
|
Chris@0
|
432 $original_delta = $field_state['original_deltas'][$delta];
|
Chris@0
|
433 $delta_element = $element[$original_delta];
|
Chris@0
|
434 }
|
Chris@0
|
435 foreach ($delta_violations as $violation) {
|
Chris@0
|
436 // @todo: Pass $violation->arrayPropertyPath as property path.
|
Chris@0
|
437 $error_element = $this->errorElement($delta_element, $violation, $form, $form_state);
|
Chris@0
|
438 if ($error_element !== FALSE) {
|
Chris@0
|
439 $form_state->setError($error_element, $violation->getMessage());
|
Chris@0
|
440 }
|
Chris@0
|
441 }
|
Chris@0
|
442 }
|
Chris@0
|
443 }
|
Chris@0
|
444 }
|
Chris@0
|
445 }
|
Chris@0
|
446
|
Chris@0
|
447 /**
|
Chris@0
|
448 * {@inheritdoc}
|
Chris@0
|
449 */
|
Chris@0
|
450 public static function getWidgetState(array $parents, $field_name, FormStateInterface $form_state) {
|
Chris@0
|
451 return NestedArray::getValue($form_state->getStorage(), static::getWidgetStateParents($parents, $field_name));
|
Chris@0
|
452 }
|
Chris@0
|
453
|
Chris@0
|
454 /**
|
Chris@0
|
455 * {@inheritdoc}
|
Chris@0
|
456 */
|
Chris@0
|
457 public static function setWidgetState(array $parents, $field_name, FormStateInterface $form_state, array $field_state) {
|
Chris@0
|
458 NestedArray::setValue($form_state->getStorage(), static::getWidgetStateParents($parents, $field_name), $field_state);
|
Chris@0
|
459 }
|
Chris@0
|
460
|
Chris@0
|
461 /**
|
Chris@0
|
462 * Returns the location of processing information within $form_state.
|
Chris@0
|
463 *
|
Chris@0
|
464 * @param array $parents
|
Chris@0
|
465 * The array of #parents where the widget lives in the form.
|
Chris@0
|
466 * @param string $field_name
|
Chris@0
|
467 * The field name.
|
Chris@0
|
468 *
|
Chris@0
|
469 * @return array
|
Chris@0
|
470 * The location of processing information within $form_state.
|
Chris@0
|
471 */
|
Chris@0
|
472 protected static function getWidgetStateParents(array $parents, $field_name) {
|
Chris@0
|
473 // Field processing data is placed at
|
Chris@0
|
474 // $form_state->get(['field_storage', '#parents', ...$parents..., '#fields', $field_name]),
|
Chris@0
|
475 // to avoid clashes between field names and $parents parts.
|
Chris@0
|
476 return array_merge(['field_storage', '#parents'], $parents, ['#fields', $field_name]);
|
Chris@0
|
477 }
|
Chris@0
|
478
|
Chris@0
|
479 /**
|
Chris@0
|
480 * {@inheritdoc}
|
Chris@0
|
481 */
|
Chris@0
|
482 public function settingsForm(array $form, FormStateInterface $form_state) {
|
Chris@0
|
483 return [];
|
Chris@0
|
484 }
|
Chris@0
|
485
|
Chris@0
|
486 /**
|
Chris@0
|
487 * {@inheritdoc}
|
Chris@0
|
488 */
|
Chris@0
|
489 public function settingsSummary() {
|
Chris@0
|
490 return [];
|
Chris@0
|
491 }
|
Chris@0
|
492
|
Chris@0
|
493 /**
|
Chris@0
|
494 * {@inheritdoc}
|
Chris@0
|
495 */
|
Chris@0
|
496 public function errorElement(array $element, ConstraintViolationInterface $error, array $form, FormStateInterface $form_state) {
|
Chris@0
|
497 return $element;
|
Chris@0
|
498 }
|
Chris@0
|
499
|
Chris@0
|
500 /**
|
Chris@0
|
501 * {@inheritdoc}
|
Chris@0
|
502 */
|
Chris@0
|
503 public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
|
Chris@0
|
504 return $values;
|
Chris@0
|
505 }
|
Chris@0
|
506
|
Chris@0
|
507 /**
|
Chris@0
|
508 * Returns the array of field settings.
|
Chris@0
|
509 *
|
Chris@0
|
510 * @return array
|
Chris@0
|
511 * The array of settings.
|
Chris@0
|
512 */
|
Chris@0
|
513 protected function getFieldSettings() {
|
Chris@0
|
514 return $this->fieldDefinition->getSettings();
|
Chris@0
|
515 }
|
Chris@0
|
516
|
Chris@0
|
517 /**
|
Chris@0
|
518 * Returns the value of a field setting.
|
Chris@0
|
519 *
|
Chris@0
|
520 * @param string $setting_name
|
Chris@0
|
521 * The setting name.
|
Chris@0
|
522 *
|
Chris@0
|
523 * @return mixed
|
Chris@0
|
524 * The setting value.
|
Chris@0
|
525 */
|
Chris@0
|
526 protected function getFieldSetting($setting_name) {
|
Chris@0
|
527 return $this->fieldDefinition->getSetting($setting_name);
|
Chris@0
|
528 }
|
Chris@0
|
529
|
Chris@0
|
530 /**
|
Chris@0
|
531 * Returns whether the widget handles multiple values.
|
Chris@0
|
532 *
|
Chris@0
|
533 * @return bool
|
Chris@0
|
534 * TRUE if a single copy of formElement() can handle multiple field values,
|
Chris@0
|
535 * FALSE if multiple values require separate copies of formElement().
|
Chris@0
|
536 */
|
Chris@0
|
537 protected function handlesMultipleValues() {
|
Chris@0
|
538 $definition = $this->getPluginDefinition();
|
Chris@0
|
539 return $definition['multiple_values'];
|
Chris@0
|
540 }
|
Chris@0
|
541
|
Chris@0
|
542 /**
|
Chris@0
|
543 * {@inheritdoc}
|
Chris@0
|
544 */
|
Chris@0
|
545 public static function isApplicable(FieldDefinitionInterface $field_definition) {
|
Chris@0
|
546 // By default, widgets are available for all fields.
|
Chris@0
|
547 return TRUE;
|
Chris@0
|
548 }
|
Chris@0
|
549
|
Chris@0
|
550 /**
|
Chris@0
|
551 * Returns whether the widget used for default value form.
|
Chris@0
|
552 *
|
Chris@0
|
553 * @param \Drupal\Core\Form\FormStateInterface $form_state
|
Chris@0
|
554 * The current state of the form.
|
Chris@0
|
555 *
|
Chris@0
|
556 * @return bool
|
Chris@0
|
557 * TRUE if a widget used to input default value, FALSE otherwise.
|
Chris@0
|
558 */
|
Chris@0
|
559 protected function isDefaultValueWidget(FormStateInterface $form_state) {
|
Chris@0
|
560 return (bool) $form_state->get('default_value_widget');
|
Chris@0
|
561 }
|
Chris@0
|
562
|
Chris@0
|
563 /**
|
Chris@0
|
564 * Returns the filtered field description.
|
Chris@0
|
565 *
|
Chris@0
|
566 * @return \Drupal\Core\Field\FieldFilteredMarkup
|
Chris@0
|
567 * The filtered field description, with tokens replaced.
|
Chris@0
|
568 */
|
Chris@0
|
569 protected function getFilteredDescription() {
|
Chris@0
|
570 return FieldFilteredMarkup::create(\Drupal::token()->replace($this->fieldDefinition->getDescription()));
|
Chris@0
|
571 }
|
Chris@0
|
572
|
Chris@0
|
573 }
|