Problem/Motivation

EntityqueueDragtableWidget::addItemSubmit() writes the new row into user input:

$rows = NestedArray::getValue($user_input, $items_path) ?: [];
$rows[$delta] = ['target_id' => $row_value, '_weight' => $delta];

$rows already contains the add_more key, so it puts the new numeric key after it. key order becomes [0, 1, ... 38, 'add_more', 39].

core WidgetBase::deleteSubmit() then runs array_values($input) on that same array. So on the next Remove click:

  • add_more becomes a numeric row
  • the row that was just added is pushed past items_count

On rebuild:

  • the pushed row is dropped by unset($elements[$field_state['items_count']]), so the item the editor added is gone, no message
  • the promoted add_more row has no target_id, so formElement() falls back to $items[$delta] and renders an item that is already in the list. This duplicate is then saved.

Steps to reproduce

  1. Queue with some items
  2. Add item
  3. Remove any row, before saving
  4. One item is now duplicated, and the added item is gone

Add alone is fine. Remove alone is fine. Only add + remove in the same form session.

Probably also the reason for #3618249: EntitySubqueue::getItemPosition() triggers "Undefined array key target_id" PHP warning. A row without target_id is exactly what getItemPosition() warns about.

Proposed resolution

Keep add_more last at the end of addItemSubmit():

$add_more = $rows['add_more'];
unset($rows['add_more']);
ksort($rows, SORT_NUMERIC);
$rows['add_more'] = $add_more;

Comments

doxigo created an issue.