Skip to content
Snippets Groups Projects

Resolve #3263397 "Simplify form logic"

Files
3
@@ -279,7 +279,6 @@ public function form(FieldItemListInterface $items, array &$form, FormStateInter
// Load the items for form rebuilds from the field state.
$field_state = static::getWidgetState($form['#parents'], $this->fieldDefinition->getName(), $form_state);
if (isset($field_state['items'])) {
usort($field_state['items'], [SortArray::class, 'sortByWeightElement']);
$items->setValue($field_state['items']);
}
@@ -390,9 +389,20 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
],
],
];
$cardinality = $this->fieldDefinition->getFieldStorageDefinition()->getCardinality();
$field_state = static::getWidgetState($parents, $field_name, $form_state);
// Determine the number of widgets to display.
if ($cardinality === FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
$max = $field_state['items_count'];
}
else {
$max = $cardinality - 1;
}
foreach ($referenced_entities as $delta => $media_item) {
$element['selection'][$delta] = [
$original_delta = $field_state['original_deltas'][$delta] ?? $delta;
$element['selection'][$original_delta] = [
'#theme' => 'media_library_item__widget',
'#attributes' => [
'class' => [
@@ -411,7 +421,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
],
'remove_button' => [
'#type' => 'submit',
'#name' => $field_name . '-' . $delta . '-media-library-remove-button' . $id_suffix,
'#name' => $field_name . '-' . $original_delta . '-media-library-remove-button' . $id_suffix,
'#value' => $this->t('Remove'),
'#media_id' => $media_item->id(),
'#attributes' => [
@@ -435,13 +445,15 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
'#type' => 'hidden',
'#value' => $media_item->id(),
],
// This hidden value can be toggled visible for accessibility.
'weight' => [
'#type' => 'number',
'#theme' => 'input__number__media_library_item_weight',
'_weight' => [
'#type' => 'weight',
'#title' => $this->t('Weight'),
'#title_display' => 'invisible',
// Note: this 'delta' is the FAPI #type 'weight' element's property.
'#delta' => $max,
'#access' => $multiple_items,
'#default_value' => $delta,
'#default_value' => $items[$delta]->_weight ?: $delta,
'#weight' => 100,
'#attributes' => [
'class' => [
'js-media-library-item-weight',
@@ -661,7 +673,14 @@ public function errorElement(array $element, ConstraintViolationInterface $error
*/
public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
if (isset($values['selection'])) {
usort($values['selection'], [SortArray::class, 'sortByWeightElement']);
// The original delta, before drag-and-drop reordering, is needed to
// route errors to the correct form element.
foreach ($values['selection'] as $delta => &$value) {
$value['_original_delta'] = $delta;
}
usort($values['selection'], function ($a, $b) {
return SortArray::sortByKeyInt($a, $b, '_weight');
});
return $values['selection'];
}
return [];
@@ -715,7 +734,7 @@ public static function updateWidget(array $form, FormStateInterface $form_state)
// When the remove button is clicked, shift focus to the next remove button.
// When the last item is deleted, we no longer have a selection and shift
// the focus to the open button.
$removed_last = $is_remove_button && !count($field_state['items']);
$removed_last = $is_remove_button && !$field_state['items_count'];
if ($is_remove_button && !$removed_last) {
// Find the next media item by weight. The weight of the removed item is
// added to the field state when it is removed in ::removeItem(). If there
@@ -726,7 +745,7 @@ public static function updateWidget(array $form, FormStateInterface $form_state)
$delta_to_focus = 0;
foreach ($field_state['items'] as $delta => $item_fields) {
$delta_to_focus = $delta;
if ($item_fields['weight'] > $removed_item_weight) {
if ($item_fields['_weight'] > $removed_item_weight) {
// Stop directly when we find an item with a bigger weight. We also
// have to subtract 1 from the delta in this case, since the delta's
// are renumbered when rebuilding the form.
@@ -760,15 +779,7 @@ public static function updateWidget(array $form, FormStateInterface $form_state)
* The form state.
*/
public static function removeItem(array $form, FormStateInterface $form_state) {
// During the form rebuild, formElement() will create field item widget
// elements using re-indexed deltas, so clear out FormState::$input to
// avoid a mismatch between old and new deltas. The rebuilt elements will
// have #default_value set appropriately for the current state of the field,
// so nothing is lost in doing this.
// @see Drupal\media_library\Plugin\Field\FieldWidget\MediaLibraryWidget::extractFormValues
$triggering_element = $form_state->getTriggeringElement();
$parents = array_slice($triggering_element['#parents'], 0, -2);
NestedArray::setValue($form_state->getUserInput(), $parents, NULL);
// Get the parents required to find the top-level widget element.
if (count($triggering_element['#array_parents']) < 4) {
@@ -787,10 +798,13 @@ public static function removeItem(array $form, FormStateInterface $form_state) {
if (isset($values['selection'][$delta])) {
// Add the weight of the removed item to the field state so we can shift
// focus to the next/previous item in an easy way.
$field_state['removed_item_weight'] = $values['selection'][$delta]['weight'];
$field_state['removed_item_weight'] = $values['selection'][$delta]['_weight'];
$field_state['removed_item_id'] = $triggering_element['#media_id'];
unset($values['selection'][$delta]);
$field_state['items_count']--;
$field_state['items'] = $values['selection'];
unset($field_state['original_deltas']);
NestedArray::setValue($form_state->getUserInput(), $element['selection']['#parents'], $field_state['items']);
static::setFieldState($element, $form_state, $field_state);
}
@@ -839,7 +853,7 @@ public static function validateItems(array $form, FormStateInterface $form_state
// Check if more items were selected than we allow.
$cardinality_unlimited = ($element['#cardinality'] === FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
$selection = count($field_state['items']) + count($media);
$selection = $field_state['items_count'] + count($media);
if (!$cardinality_unlimited && ($selection > $element['#cardinality'])) {
$form_state->setError($element, \Drupal::translation()->formatPlural($element['#cardinality'], 'Only one item can be selected.', 'Only @count items can be selected.'));
}
@@ -868,35 +882,24 @@ public static function validateItems(array $form, FormStateInterface $form_state
* The form state.
*/
public static function addItems(array $form, FormStateInterface $form_state) {
// During the form rebuild, formElement() will create field item widget
// elements using re-indexed deltas, so clear out FormState::$input to
// avoid a mismatch between old and new deltas. The rebuilt elements will
// have #default_value set appropriately for the current state of the field,
// so nothing is lost in doing this.
// @see Drupal\media_library\Plugin\Field\FieldWidget\MediaLibraryWidget::extractFormValues
$button = $form_state->getTriggeringElement();
$parents = array_slice($button['#parents'], 0, -1);
$parents[] = 'selection';
NestedArray::setValue($form_state->getUserInput(), $parents, NULL);
$element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -1));
$field_state = static::getFieldState($element, $form_state);
$media = static::getNewMediaItems($element, $form_state);
if (!empty($media)) {
// Get the weight of the last items and count from there.
$last_element = end($field_state['items']);
$weight = $last_element ? $last_element['weight'] : 0;
foreach ($media as $media_item) {
// Any ID can be passed to the widget, so we have to check access.
if ($media_item->access('view')) {
$field_state['items'][] = [
'target_id' => $media_item->id(),
'weight' => ++$weight,
'_weight' => $field_state['items_count']++,
];
}
}
unset($field_state['original_deltas']);
NestedArray::setValue($form_state->getUserInput(), $element['selection']['#parents'], $field_state['items']);
static::setFieldState($element, $form_state, $field_state);
}
@@ -999,7 +1002,7 @@ public static function validateRequired(array $element, FormStateInterface $form
// Trigger error if the field is required and no media is present. Although
// the Form API's default validation would also catch this, the validation
// error message is too vague, so a more precise one is provided here.
if (count($field_state['items']) === 0) {
if ($field_state['items_count'] === 0) {
$form_state->setError($element, t('@name field is required.', ['@name' => $element['#title']]));
}
}
Loading