Migrate existing Entity Reference field to Entity Reference Revisions field

Entity Reference and Entity Reference Revisions fields are very similar, but the latter offers more flexibility when it comes to reverting to a previous revision in the case when you have a nested entity. In other words, this field type allows you to reference a specific entity revision. Let's see the easiest way to migrate an existing Entity Reference field to an Entity Reference Revisions field.

Entity reference revisions field
Entity reference revisions field

If your website is not yet live and you don't have any data that you can't afford to lose, just delete the old ER field and create a new ERR field. And that's it, you probably don't have to do anything else.

But, if you already have data that you can't lose or easily recreate then you have to migrate values from the old to the new field. The easiest way to do it is to create a new ERR field, then loop through all referenced entities and assign values to the new field.

After that, because you changed the field name you might need to make additional changes. Search your codebase for the old field name, and make necessary updates. For example, you might need to update your Twig templates.

Another approach is to perform the field type conversion, but this is a much more complicated solution and it could break something, so we won't be talking about that.

You can write an update hook or a Drush command, or as in our case a Views Bulk Operation plugin that will perform the data migration. This is probably the easiest way to do it because it can be executed from the UI.

Here's the VBO action plugin that will perform the migration from the existing Entity Reference field to the Entity Reference Revisions field.

<?php

namespace Drupal\MY_MODULE\Plugin\Action;

use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\views_bulk_operations\Action\ViewsBulkOperationsActionBase;

/**
 * Migrate ER to ERR action.
 *
 * @Action(
 *   id = "nigrate_er_to_err_action",
 *   label = @Translation("Migrate ER to ERR action"),
 *   type = "",
 *   confirm = TRUE,
 * )
 */
class MigrateErToErrAction extends ViewsBulkOperationsActionBase {

  /**
   * {@inheritdoc}
   */
  public function execute(EntityInterface $entity = NULL) {
    $needs_save = FALSE;

    foreach ($entity->get('field_line_items')->referencedEntities() as $line_item) {
      $needs_save = TRUE;

      $entity->get('field_line_items_2')->appendItem([
        'target_id' => $line_item->id(),
        'target_revision_id' => $line_item->getRevisionId(),
      ]);
    }

    if ($needs_save === TRUE) {
      $entity->save();
    }
  }

  /**
   * {@inheritdoc}
   */
  public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
    return $object->access('update', $account, $return_as_object);
  }

}

For this to work don't forget to install the Views Bulk Operations (VBO) module, clear the caches and then create a View that will list the entities. As I already said, you can adapt this code and write an update hook. Instead of relying on the Views to provide the entities, you will have to write the entity query yourself.

To finish this article I will add a final note, that there is an active issue to provide a service that will perform the migration. As you can see there isn't much activity so you are probably better of if you don't wait but proceed with your custom implementation following my example.

About the Author

Goran Nikolovski is a creator, speaker, open-source contributor, web developer specialized in Drupal and DevOps enthusiast. He is the founder of this website and he enjoys sharing his knowledge and experiences.