You are here

public function ContentEntityStorageBase::createRevision in Drupal 9

Same name and namespace in other branches
  1. 8 core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php \Drupal\Core\Entity\ContentEntityStorageBase::createRevision()
  2. 10 core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php \Drupal\Core\Entity\ContentEntityStorageBase::createRevision()

Creates a new revision starting off from the specified entity object.

When dealing with a translatable entity, this will merge the default revision with the active translation of the passed entity.

Parameters

\Drupal\Core\Entity\RevisionableInterface $entity: The revisionable entity object being modified.

bool $default: (optional) Whether the new revision should be marked as default. Defaults to TRUE.

bool|null $keep_untranslatable_fields: (optional) Whether untranslatable field values should be kept or copied from the default revision when generating a merged revision. Defaults to TRUE if the provided entity is the default translation and untranslatable fields should only affect the default translation, FALSE otherwise.

Return value

\Drupal\Core\Entity\TranslatableRevisionableInterface A new translatable entity revision object.

Overrides TranslatableRevisionableStorageInterface::createRevision

File

core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php, line 276

Class

ContentEntityStorageBase
Base class for content entity storage handlers.

Namespace

Drupal\Core\Entity

Code

public function createRevision(RevisionableInterface $entity, $default = TRUE, $keep_untranslatable_fields = NULL) {

  /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
  $new_revision = clone $entity;
  $original_keep_untranslatable_fields = $keep_untranslatable_fields;

  // For translatable entities, create a merged revision of the active
  // translation and the other translations in the default revision. This
  // permits the creation of pending revisions that can always be saved as the
  // new default revision without reverting changes in other languages.
  if (!$entity
    ->isNew() && !$entity
    ->isDefaultRevision() && $entity
    ->isTranslatable() && $this
    ->isAnyRevisionTranslated($entity)) {
    $active_langcode = $entity
      ->language()
      ->getId();
    $skipped_field_names = array_flip($this
      ->getRevisionTranslationMergeSkippedFieldNames());

    // By default we copy untranslatable field values from the default
    // revision, unless they are configured to affect only the default
    // translation. This way we can ensure we always have only one affected
    // translation in pending revisions. This constraint is enforced by
    // EntityUntranslatableFieldsConstraintValidator.
    if (!isset($keep_untranslatable_fields)) {
      $keep_untranslatable_fields = $entity
        ->isDefaultTranslation() && $entity
        ->isDefaultTranslationAffectedOnly();
    }

    /** @var \Drupal\Core\Entity\ContentEntityInterface $default_revision */
    $default_revision = $this
      ->load($entity
      ->id());
    $translation_languages = $default_revision
      ->getTranslationLanguages();
    foreach ($translation_languages as $langcode => $language) {
      if ($langcode == $active_langcode) {
        continue;
      }
      $default_revision_translation = $default_revision
        ->getTranslation($langcode);
      $new_revision_translation = $new_revision
        ->hasTranslation($langcode) ? $new_revision
        ->getTranslation($langcode) : $new_revision
        ->addTranslation($langcode);

      /** @var \Drupal\Core\Field\FieldItemListInterface[] $sync_items */
      $sync_items = array_diff_key($keep_untranslatable_fields ? $default_revision_translation
        ->getTranslatableFields() : $default_revision_translation
        ->getFields(), $skipped_field_names);
      foreach ($sync_items as $field_name => $items) {
        $new_revision_translation
          ->set($field_name, $items
          ->getValue());
      }

      // Make sure the "revision_translation_affected" flag is recalculated.
      $new_revision_translation
        ->setRevisionTranslationAffected(NULL);

      // No need to copy untranslatable field values more than once.
      $keep_untranslatable_fields = TRUE;
    }

    // Make sure we do not inadvertently recreate removed translations.
    foreach (array_diff_key($new_revision
      ->getTranslationLanguages(), $translation_languages) as $langcode => $language) {

      // Allow a new revision to be created for the active language.
      if ($langcode !== $active_langcode) {
        $new_revision
          ->removeTranslation($langcode);
      }
    }

    // The "original" property is used in various places to detect changes in
    // field values with respect to the stored ones. If the property is not
    // defined, the stored version is loaded explicitly. Since the merged
    // revision generated here is not stored anywhere, we need to populate the
    // "original" property manually, so that changes can be properly detected.
    $new_revision->original = clone $new_revision;
  }

  // Eventually mark the new revision as such.
  $new_revision
    ->setNewRevision();
  $new_revision
    ->isDefaultRevision($default);

  // Actually make sure the current translation is marked as affected, even if
  // there are no explicit changes, to be sure this revision can be related
  // to the correct translation.
  $new_revision
    ->setRevisionTranslationAffected(TRUE);

  // Notify modules about the new revision.
  $arguments = [
    $new_revision,
    $entity,
    $original_keep_untranslatable_fields,
  ];
  $this
    ->moduleHandler()
    ->invokeAll($this->entityTypeId . '_revision_create', $arguments);
  $this
    ->moduleHandler()
    ->invokeAll('entity_revision_create', $arguments);
  return $new_revision;
}