You are here

public function FieldTranslationSynchronizer::synchronizeFields in Drupal 9

Same name and namespace in other branches
  1. 8 core/modules/content_translation/src/FieldTranslationSynchronizer.php \Drupal\content_translation\FieldTranslationSynchronizer::synchronizeFields()

Performs field column synchronization on the given entity.

Field column synchronization takes care of propagating any change in the field items order and in the column values themselves to all the available translations. This functionality is provided by defining a 'translation_sync' key for the 'content_translation' module's portion of the field definition's 'third_party_settings', holding an array of column names to be synchronized. The synchronized column values are shared across translations, while the rest varies per-language. This is useful for instance to translate the "alt" and "title" textual elements of an image field, while keeping the same image on every translation.

Parameters

\Drupal\Core\Entity\ContentEntityInterface $entity: The entity whose values should be synchronized.

string $sync_langcode: The language of the translation whose values should be used as source for synchronization.

string $original_langcode: (optional) If a new translation is being created, this should be the language code of the original values. Defaults to NULL.

Overrides FieldTranslationSynchronizerInterface::synchronizeFields

File

core/modules/content_translation/src/FieldTranslationSynchronizer.php, line 79

Class

FieldTranslationSynchronizer
Provides field translation synchronization capabilities.

Namespace

Drupal\content_translation

Code

public function synchronizeFields(ContentEntityInterface $entity, $sync_langcode, $original_langcode = NULL) {
  $translations = $entity
    ->getTranslationLanguages();

  // If we have no information about what to sync to, if we are creating a new
  // entity, if we have no translations for the current entity and we are not
  // creating one, then there is nothing to synchronize.
  if (empty($sync_langcode) || $entity
    ->isNew() || count($translations) < 2) {
    return;
  }

  // If the entity language is being changed there is nothing to synchronize.
  $entity_unchanged = $this
    ->getOriginalEntity($entity);
  if ($entity
    ->getUntranslated()
    ->language()
    ->getId() != $entity_unchanged
    ->getUntranslated()
    ->language()
    ->getId()) {
    return;
  }
  if ($entity
    ->isNewRevision()) {
    if ($entity
      ->isDefaultTranslationAffectedOnly()) {

      // If changes to untranslatable fields are configured to affect only the
      // default translation, we need to skip synchronization in pending
      // revisions, otherwise multiple translations would be affected.
      if (!$entity
        ->isDefaultRevision()) {
        return;
      }
      else {
        $sync_langcode = $entity
          ->getUntranslated()
          ->language()
          ->getId();
      }
    }
    elseif ($entity
      ->isDefaultRevision()) {

      // If a new default revision is being saved, but a newer default
      // revision was created meanwhile, use any other translation as source
      // for synchronization, since that will have been merged from the
      // default revision. In this case the actual language does not matter as
      // synchronized properties are the same for all the translations in the
      // default revision.

      /** @var \Drupal\Core\Entity\ContentEntityInterface $default_revision */
      $default_revision = $this->entityTypeManager
        ->getStorage($entity
        ->getEntityTypeId())
        ->load($entity
        ->id());
      if ($default_revision
        ->getLoadedRevisionId() !== $entity
        ->getLoadedRevisionId()) {
        $other_langcodes = array_diff_key($default_revision
          ->getTranslationLanguages(), [
          $sync_langcode => FALSE,
        ]);
        if ($other_langcodes) {
          $sync_langcode = key($other_langcodes);
        }
      }
    }
  }

  /** @var \Drupal\Core\Field\FieldItemListInterface $items */
  foreach ($entity as $field_name => $items) {
    $field_definition = $items
      ->getFieldDefinition();
    $field_type_definition = $this->fieldTypeManager
      ->getDefinition($field_definition
      ->getType());
    $column_groups = $field_type_definition['column_groups'];

    // Sync if the field is translatable, not empty, and the synchronization
    // setting is enabled.
    if (($translation_sync = $this
      ->getFieldSynchronizationSettings($field_definition)) && !$items
      ->isEmpty()) {

      // Retrieve all the untranslatable column groups and merge them into
      // single list.
      $groups = array_keys(array_diff($translation_sync, array_filter($translation_sync)));

      // If a group was selected has the require_all_groups_for_translation
      // flag set, there are no untranslatable columns. This is done because
      // the UI adds JavaScript that disables the other checkboxes, so their
      // values are not saved.
      foreach (array_filter($translation_sync) as $group) {
        if (!empty($column_groups[$group]['require_all_groups_for_translation'])) {
          $groups = [];
          break;
        }
      }
      if (!empty($groups)) {
        $columns = [];
        foreach ($groups as $group) {
          $info = $column_groups[$group];

          // A missing 'columns' key indicates we have a single-column group.
          $columns = array_merge($columns, isset($info['columns']) ? $info['columns'] : [
            $group,
          ]);
        }
        if (!empty($columns)) {
          $values = [];
          foreach ($translations as $langcode => $language) {
            $values[$langcode] = $entity
              ->getTranslation($langcode)
              ->get($field_name)
              ->getValue();
          }

          // If a translation is being created, the original values should be
          // used as the unchanged items. In fact there are no unchanged items
          // to check against.
          $langcode = $original_langcode ?: $sync_langcode;
          $unchanged_items = $entity_unchanged
            ->getTranslation($langcode)
            ->get($field_name)
            ->getValue();
          $this
            ->synchronizeItems($values, $unchanged_items, $sync_langcode, array_keys($translations), $columns);
          foreach ($translations as $langcode => $language) {
            $entity
              ->getTranslation($langcode)
              ->get($field_name)
              ->setValue($values[$langcode]);
          }
        }
      }
    }
  }
}