You are here

public function FieldTranslationSynchronizer::synchronizeItems in Drupal 9

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

Synchronize the items of a single field.

All the column values of the "active" language are compared to the unchanged values to detect any addition, removal or change in the items order. Subsequently the detected changes are performed on the field items in other available languages.

Parameters

array $field_values: The field values to be synchronized.

array $unchanged_items: The unchanged items to be used to detect changes.

string $sync_langcode: The language code of the items to use as source values.

array $translations: An array of all the available language codes for the given field.

array $properties: An array of property names to be synchronized.

Overrides FieldTranslationSynchronizerInterface::synchronizeItems

1 call to FieldTranslationSynchronizer::synchronizeItems()
FieldTranslationSynchronizer::synchronizeFields in core/modules/content_translation/src/FieldTranslationSynchronizer.php
Performs field column synchronization on the given entity.

File

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

Class

FieldTranslationSynchronizer
Provides field translation synchronization capabilities.

Namespace

Drupal\content_translation

Code

public function synchronizeItems(array &$values, array $unchanged_items, $sync_langcode, array $translations, array $properties) {
  $source_items = $values[$sync_langcode];

  // Make sure we can detect any change in the source items.
  $change_map = [];

  // By picking the maximum size between updated and unchanged items, we make
  // sure to process also removed items.
  $total = max([
    count($source_items),
    count($unchanged_items),
  ]);

  // As a first step we build a map of the deltas corresponding to the column
  // values to be synchronized. Recording both the old values and the new
  // values will allow us to detect any change in the order of the new items
  // for each column.
  for ($delta = 0; $delta < $total; $delta++) {
    foreach ([
      'old' => $unchanged_items,
      'new' => $source_items,
    ] as $key => $items) {
      if ($item_id = $this
        ->itemHash($items, $delta, $properties)) {
        $change_map[$item_id][$key][] = $delta;
      }
    }
  }

  // Backup field values and the change map.
  $original_field_values = $values;
  $original_change_map = $change_map;

  // Reset field values so that no spurious one is stored. Source values must
  // be preserved in any case.
  $values = [
    $sync_langcode => $source_items,
  ];

  // Update field translations.
  foreach ($translations as $langcode) {

    // We need to synchronize only values different from the source ones.
    if ($langcode != $sync_langcode) {

      // Reinitialize the change map as it is emptied while processing each
      // language.
      $change_map = $original_change_map;

      // By using the maximum cardinality we ensure to process removed items.
      for ($delta = 0; $delta < $total; $delta++) {

        // By inspecting the map we built before we can tell whether a value
        // has been created or removed. A changed value will be interpreted as
        // a new value, in fact it did not exist before.
        $created = TRUE;
        $removed = TRUE;
        $old_delta = NULL;
        $new_delta = NULL;
        if ($item_id = $this
          ->itemHash($source_items, $delta, $properties)) {
          if (!empty($change_map[$item_id]['old'])) {
            $old_delta = array_shift($change_map[$item_id]['old']);
          }
          if (!empty($change_map[$item_id]['new'])) {
            $new_delta = array_shift($change_map[$item_id]['new']);
          }
          $created = $created && !isset($old_delta);
          $removed = $removed && !isset($new_delta);
        }

        // If an item has been removed we do not store its translations.
        if ($removed) {
          continue;
        }
        elseif ($created && !empty($original_field_values[$langcode][$delta])) {
          $values[$langcode][$delta] = $this
            ->createMergedItem($source_items[$delta], $original_field_values[$langcode][$delta], $properties);
        }
        elseif ($created) {
          $values[$langcode][$delta] = $source_items[$delta];
        }
        elseif (isset($old_delta) && isset($new_delta)) {

          // If for any reason the old value is not defined for the current
          // language we fall back to the new source value, this way we ensure
          // the new values are at least propagated to all the translations.
          // If the value has only been reordered we just move the old one in
          // the new position.
          $item = isset($original_field_values[$langcode][$old_delta]) ? $original_field_values[$langcode][$old_delta] : $source_items[$new_delta];

          // When saving a default revision starting from a pending revision,
          // we may have desynchronized field values, so we make sure that
          // untranslatable properties are synchronized, even if in any other
          // situation this would not be necessary.
          $values[$langcode][$new_delta] = $this
            ->createMergedItem($source_items[$new_delta], $item, $properties);
        }
      }
    }
  }
}