diff --git a/src/DeepCloningTrait.php b/src/DeepCloningTrait.php index 790998acecc47d2cbb9a1d2d48ebccb6d98a3c8c..486cc8eb9733646014ceafdc4add0e4a8a247277 100644 --- a/src/DeepCloningTrait.php +++ b/src/DeepCloningTrait.php @@ -139,7 +139,10 @@ trait DeepCloningTrait { $value = $entity_field->getValue(); // Handle Paragraph as special case as it does not support // referencedEntities(), check issue #3089724, also it uses revision id. - $this->cloneReferencedParagraphsEntities($field_key, $entity_field, $entity); + // Only call cloneReferencedParagraphsEntities for paragraph fields. + if ($entity_field->getSetting('target_type') && $entity_field->getSetting('target_type') == 'paragraph') { + $this->cloneReferencedParagraphsEntities($field_key, $entity_field, $entity); + } // The general rule for any entity. if ($entity_field->getName() != 'type' && isset($value[0]['target_id'])) { @@ -189,7 +192,7 @@ trait DeepCloningTrait { protected function cloneReferencedParagraphsEntities($field_key, $entity_field, &$entity) { $value = $entity_field->getValue(); $new_value = []; - // Paragraphs Classic, EXPERIMENTAL and IEF - simple form mode structure. + // Unsaved inline blocks using paragraph form modes and IEF - simple. if (isset($value[0]['entity'])) { foreach ($value as $key => $item) { if ($item['entity']) { @@ -198,19 +201,44 @@ trait DeepCloningTrait { } } } - // IEF - complex form mode structure. + // IEF - complex form mode structure and saved inline blocks. elseif ( $entity_field->getName() != 'type' && isset($value[0]['target_id']) && $entity_field->getSetting('target_type') == 'paragraph' ) { foreach ($value as $key => $item) { - $paragraph = \Drupal::entityTypeManager()->getStorage('paragraph')->load($value[$key]['target_id']); - $new_paragraph = $paragraph->createDuplicate(); - // It's important to save the entity in the IEF - complex case. - $new_paragraph->save(); - $new_value[$key]['target_id'] = $new_paragraph->id(); - $new_value[$key]['target_revision_id'] = $new_paragraph->getRevisionId(); + // createDuplicates() creates a duplicate with the same parent_id. + // Our duplicate needs to be independent of original inline block id. + $paragraph_storage = \Drupal::entityTypeManager()->getStorage('paragraph'); + $paragraph = $paragraph_storage->load($item['target_id']); + $default_fields = \Drupal::service('entity_field.manager')->getFieldDefinitions('paragraph', NULL); + $fields = $paragraph->getFields(); + $new_paragraph = $paragraph_storage->create([ + 'type' => $paragraph->getType(), + 'parent_type' => $entity->getEntityTypeId(), + 'parent_field_name' => $field_key, + ]); + $new_paragraph->isNew(); + foreach ($fields as $field) { + $field_name = $field->getName(); + // Copy non-default field values to new paragraph. + if (!in_array($field_name, array_keys($default_fields))) { + $field_value = $field->getValue(); + + if (!empty($field_value)) { + // Recursively clone referenced paragraphs. + if (isset($field_value[0]['target_id']) + && $field->getSetting('target_type') == 'paragraph') { + $this->cloneReferencedParagraphsEntities($field_name, $field, $new_paragraph); + } + else { + $new_paragraph->set($field_name, $field_value); + } + } + } + } + $new_value[$key]['entity'] = $new_paragraph; } }