Change record status: 
Project: 
Introduced in branch: 
8.5.x
Introduced in version: 
8.5.0
Description: 

The DX of creating a new revision has been vastly improved by introducing a new RevisionableStorageInterface::createRevision() method. This returns a new object that's a clone of the original entity with the correct flags already set, which is useful to avoid messing with the state of the original $entity.

Before:

$storage = \Drupal::entityTypeManager()->getStorage($entity_type_id);
$entity = $storage->load($id);
$revision = clone $entity; // optional, but usually a good practice
$revision->setNewRevision();
$revision->isDefaultRevision($default);

After:

$storage = \Drupal::entityTypeManager()->getStorage($entity_type_id);
$entity = $storage->load($id);
$revision = $storage->createRevision($entity, $default);

This new API is particularly useful when the entity is both revisionable and translatable, because it encapsulates all the logic required to deal with translations in pending revisions. In fact in this case RevisionableStorageInterface::createRevision() returns a new entity object that is a merge of the active entity translation and all the other translations available in the default revision. Such an object can be saved and made the new default revision without affecting pending revisions in other languages. This logic is definitely non-trivial so using the new API is strongly recommended every time a new revision needs to be instantiated.

It should be noted that for revisionable and translatable entity types, there's a variant of the method in TranslatableRevisionableStorageInterface that defines an additional (optional) $keep_untranslatable_fields parameter. This can be used to determine whether untranslatable field values should be preserved or copied from the default revision. By default untranslatable field values are not kept, unless they are configured to affect only the default translation.

This is a method addition on two interfaces that have an implementing base class. For classes not extending \Drupal\Core\Entity\ContentEntityStorageBase the logic of ::createRevision() can be kept as-is, since it's storage agnostic.

Impacts: 
Module developers