You are here

public static function FieldCollectionEmbedWidget::removeSubmit in Field collection 8

Same name and namespace in other branches
  1. 8.3 src/Plugin/Field/FieldWidget/FieldCollectionEmbedWidget.php \Drupal\field_collection\Plugin\Field\FieldWidget\FieldCollectionEmbedWidget::removeSubmit()

Submit callback to remove an item from the field UI multiple wrapper.

When a remove button is submitted, we need to find the item that it referenced and delete it. Since field UI has the deltas as a straight unbroken array key, we have to renumber everything down. Since we do this we *also* need to move all the deltas around in the $form_state->values and $form_state input so that user changed values follow. This is a bit of a complicated process.

File

src/Plugin/Field/FieldWidget/FieldCollectionEmbedWidget.php, line 246

Class

FieldCollectionEmbedWidget
Plugin implementation of the 'field_collection_embed' widget.

Namespace

Drupal\field_collection\Plugin\Field\FieldWidget

Code

public static function removeSubmit($form, FormStateInterface $form_state) {
  $button = $form_state
    ->getTriggeringElement();
  $delta = $button['#delta'];

  // Where in the form we'll find the parent element.
  $address = array_slice($button['#array_parents'], 0, -4);
  $address_state = array_slice($button['#parents'], 0, -3);

  // Go one level up in the form, to the widgets container.
  $parent_element = NestedArray::getValue($form, array_merge($address, [
    'widget',
  ]));
  $field_name = $parent_element['#field_name'];
  $parents = $parent_element['#field_parents'];
  $field_state = static::getWidgetState($parents, $field_name, $form_state);

  // Go ahead and renumber everything from our delta to the last
  // item down one. This will overwrite the item being removed.
  for ($i = $delta; $i <= $field_state['items_count']; $i++) {
    $old_element_address = array_merge($address, [
      'widget',
      $i + 1,
    ]);
    $old_element_state_address = array_merge($address_state, [
      $i + 1,
    ]);
    $new_element_state_address = array_merge($address_state, [
      $i,
    ]);
    $moving_element = NestedArray::getValue($form, $old_element_address);
    $moving_element_value = NestedArray::getValue($form_state
      ->getValues(), $old_element_state_address);
    $moving_element_input = NestedArray::getValue($form_state
      ->getUserInput(), $old_element_state_address);

    // Tell the element where it's being moved to.
    $moving_element['#parents'] = $new_element_state_address;

    // Move the element around.
    $form_state
      ->setValueForElement($moving_element, $moving_element_value);
    $user_input = $form_state
      ->getUserInput();
    NestedArray::setValue($user_input, $moving_element['#parents'], $moving_element_input);
    $form_state
      ->setUserInput($user_input);

    // Move the entity in our saved state.
    if (isset($field_state['field_collection_item'][$i + 1])) {
      $field_state['field_collection_item'][$i] = $field_state['field_collection_item'][$i + 1];
    }
    else {
      unset($field_state['field_collection_item'][$i]);
    }
  }

  // Then remove the last item. But we must not go negative.
  if ($field_state['items_count'] > 0) {
    $field_state['items_count']--;
  }
  else {

    // Create a new field collection item after deleting the last one so the
    // form will show a blank field collection item instead of resurrecting
    // the first one if there was already data.
    $field_state['field_collection_item'][0] = FieldCollectionItem::create([
      'field_name' => $field_name,
    ]);
  }

  // Fix the weights. Field UI lets the weights be in a range of
  // (-1 * item_count) to (item_count). This means that when we remove one,
  // the range shrinks; weights outside of that range then get set to
  // the first item in the select by the browser, floating them to the top.
  // We use a brute force method because we lost weights on both ends
  // and if the user has moved things around, we have to cascade because
  // if I have items weight weights 3 and 4, and I change 4 to 3 but leave
  // the 3, the order of the two 3s now is undefined and may not match what
  // the user had selected.
  $input = NestedArray::getValue($form_state
    ->getUserInput(), $address_state);

  // Sort by weight.
  uasort($input, '_field_collection_sort_items_helper');

  // Reweight everything in the correct order.
  $weight = -1 * $field_state['items_count'];
  foreach ($input as $key => $item) {
    if ($item) {
      $input[$key]['_weight'] = $weight++;
    }
  }
  $user_input = $form_state
    ->getUserInput();
  NestedArray::setValue($user_input, $address_state, $input);
  $form_state
    ->setUserInput($user_input);
  static::setWidgetState($parents, $field_name, $form_state, $field_state);
  $form_state
    ->setRebuild();
}