Clone entity data into existing entities in Drupal 8 & 9

30 Mar 2018

Creating a duplicate of an entity

Creating a duplicate of an entity is easily done via the entity API method Entity::createDuplicate(). This is a convenient method if the goal is to clone an entity into a new entity, as all identifiers of the previous entity get unset when using this method.

  1. $nid = 5;
  2. $entity = \Drupal::service('entity_type.manager')->getStorage('node')->load($nid); // Use dependency injection instead if in class context.
  3. $duplicate = $entity->createDuplicate();
  4. $duplicate->save();

Cloning data into an existing entity

However partially or fully cloning data into an existing entity is less straight forward (and rightfully so). Still, the ability to do so can be useful

  • in custom migration scripts where we want to overwrite old entities without creating new ones,
  • in cases where we need to overwrite the old entity as other internal or external data may reference it and creating a new entity would break these references.

The second case could be an entity reference field referencing the old entity in question (this could be technically solved by reassigning the reference), but it could also be 3rd party software referencing the old entity, which would complicate things.

This article is going to demonstrate a couple of possible ways of cloning entity data into existing entities.

Cloning field data 1:1

  1. $source_nid = 5;
  2. $destination_nid = 6;
  4. // Use dependency injection in class context instead.
  5. $source = \Drupal::service('entity_type.manager')->getStorage('node')->load($source_nid);
  6. $destination = \Drupal::service('entity_type.manager')->getStorage('node')->load($destination_nid);
  8. foreach ($source->getFields() as $name => $field) {
  9. $destination->set($name, $field->getValue());
  10. }
  11. $destination->save();

Importing new data only

To import new field data without overwriting existing data, just check if the destination field is empty before cloning into it like so:

  1. foreach ($source->getFields() as $name => $field) {
  2. if ($destination->get($name)->isEmpty()) {
  3. $destination->set($name, $field->getValue());
  4. }
  5. }

Putting it all together

A nifty method that could be used to clone field data of entities into other existing entitites could look like this:

  1. /**
  2.  * @param \Drupal\Core\Entity\Entity $source
  3.  * @param \Drupal\Core\Entity\Entity $destination
  4.  * @param string $mode
  5.  * Can be 'keep', 'overwrite' and 'clone'.
  6.  * @param array $skip_fields
  7.  * An array of fields not to be cloned into the destination entity.
  8.  */
  9. public function cloneFields(Entity $source, Entity &$destination, $mode, $skip_fields = []) {
  10. foreach ($source->getFields() as $name => $field) {
  12. // In this case clone only fields and leave out properties like title.
  13. if (strpos($name, 'field') === 0
  15. // Leave out certain fields.
  16. && !in_array($name, $skip_fields)) {
  18. switch ($mode) {
  20. // Import only those fields from source that are empty in destination.
  21. case 'keep':
  22. default:
  23. if (!$destination->get($name)->isEmpty()) {
  24. continue 2;
  25. }
  26. break;
  28. // Import field data from source overwriting all destination fields.
  29. // Do not empty fields in destination if they are empty in source.
  30. case 'overwrite':
  31. if ($source->get($name)->isEmpty()) {
  32. continue 2;
  33. }
  34. break;
  36. // Import field data from source overwriting all destination fields.
  37. // Empty fields in destination if they are empty in source.
  38. case 'clone':
  39. break;
  40. }
  41. $destination->set($name, $field->getValue());
  42. }
  43. }
  44. $destination->save();
  45. }

There you go. Make sure to comment below in case of questions or if you know a better way of doing the above.


Thanks to this post, I was able to solve a problem that had no solution online: the ability to programmatically create a translation with all the fields of the original node. I scoured for solutions and found none, but thanks to your suggestion on iterating over fields, I was able to do it. Huge thank-you!

Here's the basic code that did it. I'm sure it needs tweaks as I'm skipping all non-fields except for path, but for now it's getting the job done!

        $node_trans = $node->addTranslation('en-au'); // sample using Australian English
        $node_trans_fields = $node->getTranslatableFields();
        foreach ($node_trans_fields as $name => $field) {
          if (substr($name, 0, 6) == 'field_' || in_array($name, ['body', 'path'])) {
            $node_trans->set($name, $field->getValue());
        try {
        catch (\Exception $error) {
          $add_status .= 'ERROR saving ';

Thank you for this. I am new to Drupal and learning. Would this be possible to use this on a taxonomy term where one wishes to copy all the content to another existing term?

Add new comment

The content of this field is kept private and will not be shown publicly.

Restricted HTML

  • Allowed HTML tags: <a href hreflang target> <em> <strong> <cite> <blockquote cite> <pre> <ul type> <ol start type> <li> <dl> <dt> <dd> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.

Get a quote in 24 hours

Wether a huge commerce system, or a small business website, we will quote the project within 24h of you pressing the following button: Get quote