- Issue created by @bojanz
- 🇩🇪Germany hchonov 🇪🇺🇩🇪🇧🇬
@bojanz, I've proposed something similar for tracking the
revision_parent
of duplicate entities in #2725523-30: Add a revision_parent field to revisionable entities → :If for example we take a food magazine, then we create a duplicate of some recipe and want to offer different extended variants by the users, which create a duplicate and extend/modify the new recipe, but a link between the recipes is automatically created. This actually sounds like a really great out of the box feature for Umami and for presenting Drupal.
I think that this feature might be used by a lot of projects and as such, it would be good to be part of the Drupal core.I think that in case of revisionable entity types the duplicate entity should reference its source through the new field "revision_parent". For non-revisionable entity types we could add a simply entity reference "parent" field and for config entities we could add a "source" property.
- 🇷🇸Serbia bojanz
It is a fair suggestion, but maybe we don't even need to store this. Just knowing the source/parent at the time of hook firing would be enough as a first step.
Also note that Entity::create() currently calls the storage create() method, but createDuplicate() doesn't and no hook is fired.
It might make sense to introduce a storage-level createDuplicate() which calls a hook_entity_create_duplicate().
That would be a fairly non-controversial change, though it still doesn't solve the problem of generically knowing the source ID at save time. - 🇩🇪Germany hchonov 🇪🇺🇩🇪🇧🇬
It is a fair suggestion, but maybe we don't even need to store this. Just knowing the source/parent at the time of hook firing would be enough as a first step.
This will be for sure much easier to implement and maybe easier to get into core than what I am proposing and still makes it possible to add the persisting layer later :).
Also introduce a hook_entity_duplicate(). This would allow us to pass $original_entity / $source_entity as a second argument if we feel like $entity->original is too much of a historical oddity.
+1 on the hook, as it allows to duplicate nested referenced structures as well.
I think it would be good to also have a new "source" property and the respective
getSourceEntity()
method on theEntityBase
class, which we could access during the saving process.I am in favor of adding a new property and not reusing the "original" property, because while being similar those are different operations and it is assumed that "original" is not set during the saving process of new entities.
It might make sense to introduce a storage-level createDuplicate() which calls a hook_entity_create_duplicate().
Yes, this makes sense. We've done this for example with the
createRevision
method on the storage. - 🇷🇸Serbia bojanz
You are right about $entity->original being too unclear in this case. Entities often have preSave() logic that compares ->original values, that logic would be unexpectedly triggered in case of a duplicate.
We need a fresh property.
- 🇷🇸Serbia bojanz
Updating issue summary with the new plan, as thought-up with berdir and hchonov.
- 🇬🇧United Kingdom AaronMcHale Edinburgh, Scotland
I wonder if the proposed
getDuplicateSource()
method might be a little too ambiguous of a name and could potentially result in accidental incorrect use, maybegetOriginatingEntity()
andgetOriginatingEntityId()
(in the event you don't want to load the Entity and just want the ID)? To me that seems a little clearer that the entity which is being returned was the Entity that this Entity was based upon, but then as has previously been mentioned maybe that is out of the scope of this issue. - 🇨🇭Switzerland berdir Switzerland
I find getDuplicateSource() less ambiguous than your suggestions, especially because we already have $entity->original (which we want to deprecate in favor of methods) which is something entirely different.
Drupal 9.1.0-alpha1 → will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule → and the Allowed changes during the Drupal 9 release cycle → .
Drupal 8.9.0-beta1 → was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule → and the Allowed changes during the Drupal 8 and 9 release cycles → .
Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule → and the Allowed changes during the Drupal 8 and 9 release cycles → .
- 🇩🇪Germany hchonov 🇪🇺🇩🇪🇧🇬
In addition to #9 -
getOriginatingEntity()
is too general and sounds like there might be multiple origins, whilegetDuplicateSource()
is more specific and semantically relates tocreateDuplicate()
. - First commit to issue fork.
- @sakiland opened merge request.
- @sakiland opened merge request.
Drupal 9.2.0-alpha1 → will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule → and the Allowed changes during the Drupal core release cycle → .
- 🇷🇸Serbia sakiland
I've created an initial quick solution without creating hook support and move logic into entity storage. I need this so I can fix issue with duplicating paragraph via Layout Builder #3190523: Paragraph is not cloned in Layout builder → . This is just start point for this issue, but it's working solution.
I've also crated two branches (one for 8.x and another for 9.x) and created two MR.
If someone needs patch file to apply these changes via composer, near each MR there is 'plan diff' url to the appropriate patch.
Drupal 9.3.0-rc1 → was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule → and the Allowed changes during the Drupal core release cycle → .
- 🇩🇪Germany Anybody Porta Westfalica
Just ran into a situation with layout_paragraphs where this also seems to be a blocker: 🐛 Cloning Entities containing Layout Paragraphs breaks structure Needs work at least for some cloning strategies. I'll add it to the related issues.
- First commit to issue fork.
- 🇺🇸United States richgerdes New Jersey, USA
I've taken a pass at improvements here. I've updated the workflow for the
createDuplicate()
function to mirror that ofcreate()
, and making a call out to the storage service to handle the duplication. Similarly to thecreate()
on the storage class, this implements acreateDuplicate()
invokespreCreateDuplicate()
on the entity class, then callsdoCreateDuplicate()
, and ultimately calls$entity->postDuplicate()
before invoking new hooks (ENTITY_TYPE_duplicate_create
andentity_duplicate_create
).I've opted for the name `duplicate_create` for the hook, which follows the same format as `revision_create`. Additionally new apis for getting/setting the duplicate source entity and checking if the entity is a duplicate (like isNew) are included now.
- 🇺🇸United States richgerdes New Jersey, USA
Sorry for the spam. I rebased the branches so that I could roll a patch that applies against 9.4.x, instead of 9.2.x, but forgot that I don't have access to change the MR target branch. Someone will need to update the target branch to 9.4.x so this can be better reviewed....
In the mean time, I'm posting a patch here for reference. This should apply fine.
- 🇫🇷France julien.sibi
Hello @richgerdes,
I was having errors with your patch when cloning a display mode on a order entity type.
I got it working changing the following lines.
In entityStorageBase :
$duplicate->{$entity_type->getKey('id')} = NULL;
to
$duplicate->set($entity_type->getKey('id'), NULL);
$duplicate->{$entity_type->getKey('uuid')} = $this->uuidService->generate();
to
$duplicate->set($entity_type->getKey('uuid'), $this->uuidService->generate());
then in configEntityBase :
parent::postDuplicate();
to
parent::postDuplicate($storage);
But I don't really measure any consequences of this patch, and I think it needs review before using in production..
Julien
- First commit to issue fork.
- 🇺🇦Ukraine Taran2L Lviv
So, tests are green:
Changes since the #23:
- Removed class property
duplicateSource
(as this is run-time information; alsooriginal
property during update operation does not exist either) - Added uuid service to the base EntityStorageBase class
- Created a deprecation + a test for it
- Updated all children classes/related tests
- Removed class property
- Moved duplicate test into the correct place