protected static function ParagraphsWidget::reorderParagraphs in Paragraphs 8
Reorder paragraphs.
Parameters
\Drupal\Core\Form\FormStateInterface $form_state: The form state.
$field_values_parents: The field value parents.
1 call to ParagraphsWidget::reorderParagraphs()
- ParagraphsWidget::massageFormValues in src/
Plugin/ Field/ FieldWidget/ ParagraphsWidget.php - Massages the form values into the format expected for field values.
File
- src/
Plugin/ Field/ FieldWidget/ ParagraphsWidget.php, line 1930
Class
- ParagraphsWidget
- Plugin implementation of the 'entity_reference_revisions paragraphs' widget.
Namespace
Drupal\paragraphs\Plugin\Field\FieldWidgetCode
protected static function reorderParagraphs(FormStateInterface $form_state, $field_values_parents) {
$field_name = end($field_values_parents);
$field_values = NestedArray::getValue($form_state
->getValues(), $field_values_parents);
$complete_field_storage = NestedArray::getValue($form_state
->getStorage(), [
'field_storage',
'#parents',
]);
$new_field_storage = $complete_field_storage;
// Set a flag to prevent this from running twice, as the entity is built
// for validation as well as saving and would fail the second time as we
// already altered the field storage.
if (!empty($new_field_storage['#fields'][$field_name]['reordered'])) {
return;
}
$new_field_storage['#fields'][$field_name]['reordered'] = TRUE;
// Clear out all current paragraphs keys in all nested paragraph widgets
// as there might be fewer than before or none in a certain widget.
$clear_paragraphs = function ($field_storage) use (&$clear_paragraphs) {
foreach ($field_storage as $key => $value) {
if ($key === '#fields') {
foreach ($value as $field_name => $widget_state) {
if (isset($widget_state['paragraphs'])) {
$field_storage['#fields'][$field_name]['paragraphs'] = [];
}
}
}
else {
$field_storage[$key] = $clear_paragraphs($field_storage[$key]);
}
}
return $field_storage;
};
// Only clear the current field and its children to avoid deleting
// paragraph references in other fields.
$new_field_storage['#fields'][$field_name]['paragraphs'] = [];
if (isset($new_field_storage[$field_name])) {
$new_field_storage[$field_name] = $clear_paragraphs($new_field_storage[$field_name]);
}
$reorder_paragraphs = function ($reorder_values, $parents = [], FieldableEntityInterface $parent_entity = NULL) use ($complete_field_storage, &$new_field_storage, &$reorder_paragraphs) {
foreach ($reorder_values as $field_name => $values) {
foreach ($values['list'] as $delta => $item_values) {
$old_keys = array_merge($parents, [
'#fields',
$field_name,
'paragraphs',
$delta,
]);
$path = explode('][', $item_values['_path']);
$new_field_name = array_pop($path);
$key_parents = [];
foreach ($path as $i => $key) {
$key_parents[] = $key;
if ($i % 2 == 1) {
$key_parents[] = 'subform';
}
}
$new_keys = array_merge($key_parents, [
'#fields',
$new_field_name,
'paragraphs',
$item_values['_weight'],
]);
$key_exists = NULL;
$item_state = NestedArray::getValue($complete_field_storage, $old_keys, $key_exists);
if (!$key_exists && $parent_entity) {
// If key does not exist, then this parent widget was previously
// not expanded. This can only happen on nested levels. In that
// case, initialize a new item state and set the widget state to
// an empty array if it is not already set from an earlier item.
// If something else is placed there, it will be put in there,
// otherwise the widget will know that nothing is there anymore.
$item_state = [
'entity' => $parent_entity
->get($field_name)
->get($delta)->entity,
'mode' => 'closed',
];
$widget_state_keys = array_slice($old_keys, 0, count($old_keys) - 2);
if (!NestedArray::getValue($new_field_storage, $widget_state_keys)) {
NestedArray::setValue($new_field_storage, $widget_state_keys, [
'paragraphs' => [],
]);
}
}
// Ensure the referenced paragraph will be saved.
$item_state['entity']
->setNeedsSave(TRUE);
NestedArray::setValue($new_field_storage, $new_keys, $item_state);
if (isset($item_values['dragdrop'])) {
// If there is no field storage yet for the new position, initialize
// it to an empty array in case all paragraphs have been moved away
// from it.
foreach (array_keys($item_values['dragdrop']) as $sub_field_name) {
$new_widget_state_keys = array_merge($parents, [
$field_name,
$item_values['_weight'],
'subform',
'#fields',
$sub_field_name,
]);
if (!NestedArray::getValue($new_field_storage, $new_widget_state_keys)) {
NestedArray::setValue($new_field_storage, $new_widget_state_keys, [
'paragraphs' => [],
]);
}
}
$reorder_paragraphs($item_values['dragdrop'], array_merge($parents, [
$field_name,
$delta,
'subform',
]), $item_state['entity']);
}
}
}
};
$reorder_paragraphs($field_values['dragdrop']);
// Recalculate original deltas.
$recalculate_original_deltas = function ($field_storage, ContentEntityInterface $parent_entity) use (&$recalculate_original_deltas) {
if (isset($field_storage['#fields'])) {
foreach ($field_storage['#fields'] as $field_name => $widget_state) {
if (isset($widget_state['paragraphs'])) {
// If the parent field does not exist but we have paragraphs in
// widget state, something went wrong and we have a mismatch.
// Throw an exception.
if (!$parent_entity
->hasField($field_name) && !empty($widget_state['paragraphs'])) {
throw new \LogicException('Reordering paragraphs resulted in paragraphs on non-existing field ' . $field_name . ' on parent entity ' . $parent_entity
->getEntityTypeId() . '/' . $parent_entity
->id());
}
// Sort the paragraphs by key so that they will be assigned to
// the entity in the right order. Reset the deltas.
ksort($widget_state['paragraphs']);
$widget_state['paragraphs'] = array_values($widget_state['paragraphs']);
$original_deltas = range(0, count($widget_state['paragraphs']) - 1);
$field_storage['#fields'][$field_name]['original_deltas'] = $original_deltas;
$field_storage['#fields'][$field_name]['items_count'] = count($widget_state['paragraphs']);
$field_storage['#fields'][$field_name]['real_item_count'] = count($widget_state['paragraphs']);
// Update the parent entity and point to the new children, if the
// parent field does not exist, we also have no paragraphs, so
// we can just skip this, this is a dead leaf after re-ordering.
// @todo Clean this up somehow?
if ($parent_entity
->hasField($field_name)) {
$parent_entity
->set($field_name, array_column($widget_state['paragraphs'], 'entity'));
// Next process that field recursively.
foreach (array_keys($widget_state['paragraphs']) as $delta) {
if (isset($field_storage[$field_name][$delta]['subform'])) {
$field_storage[$field_name][$delta]['subform'] = $recalculate_original_deltas($field_storage[$field_name][$delta]['subform'], $parent_entity
->get($field_name)
->get($delta)->entity);
}
}
}
}
}
}
return $field_storage;
};
$parent_entity = $form_state
->getFormObject()
->getEntity();
$new_field_storage = $recalculate_original_deltas($new_field_storage, $parent_entity);
$form_state
->set([
'field_storage',
'#parents',
], $new_field_storage);
}