/**
  * {@inheritdoc}
  */
 public function synchronizeFields(ContentEntityInterface $entity, $sync_langcode, $original_langcode = NULL)
 {
     $translations = $entity->getTranslationLanguages();
     $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
     // 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_type = $entity->getEntityTypeId();
     $entity_unchanged = isset($entity->original) ? $entity->original : $this->entityManager->getStorage($entity_type)->loadUnchanged($entity->id());
     if ($entity->getUntranslated()->language()->getId() != $entity_unchanged->getUntranslated()->language()->getId()) {
         return;
     }
     /** @var \Drupal\Core\Field\FieldItemListInterface $items */
     foreach ($entity as $field_name => $items) {
         $field_definition = $items->getFieldDefinition();
         $field_type_definition = $field_type_manager->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 ($field_definition instanceof ThirdPartySettingsInterface && $field_definition->isTranslatable() && !$items->isEmpty() && ($translation_sync = $field_definition->getThirdPartySetting('content_translation', 'translation_sync'))) {
             // Retrieve all the untranslatable column groups and merge them into
             // single list.
             $groups = array_keys(array_diff($translation_sync, array_filter($translation_sync)));
             if (!empty($groups)) {
                 $columns = array();
                 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'] : array($group));
                 }
                 if (!empty($columns)) {
                     $values = array();
                     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]);
                     }
                 }
             }
         }
     }
 }
 /**
  * Checks whether field languages are correctly stored for the given entity.
  *
  * @param \Drupal\Core\Entity\ContentEntityInterface $entity
  *   The entity fields are attached to.
  * @param string $message
  *   (optional) A message to display with the assertion.
  */
 protected function assertFieldStorageLangcode(ContentEntityInterface $entity, $message = '')
 {
     $status = TRUE;
     $entity_type = $entity->getEntityTypeId();
     $id = $entity->id();
     $langcode = $entity->getUntranslated()->language()->id;
     $fields = array($this->field_name, $this->untranslatable_field_name);
     foreach ($fields as $field_name) {
         $field_storage = FieldStorageConfig::loadByName($entity_type, $field_name);
         $tables = array(ContentEntityDatabaseStorage::_fieldTableName($field_storage), ContentEntityDatabaseStorage::_fieldRevisionTableName($field_storage));
         foreach ($tables as $table) {
             $record = \Drupal::database()->select($table, 'f')->fields('f')->condition('f.entity_id', $id)->condition('f.revision_id', $id)->execute()->fetchObject();
             if ($record->langcode != $langcode) {
                 $status = FALSE;
                 break;
             }
         }
     }
     return $this->assertTrue($status, $message);
 }
 /**
  * Saves values of fields that use dedicated tables.
  *
  * @param \Drupal\Core\Entity\ContentEntityInterface $entity
  *   The entity.
  * @param bool $update
  *   TRUE if the entity is being updated, FALSE if it is being inserted.
  * @param string[] $names
  *   (optional) The names of the fields to be stored. Defaults to all the
  *   available fields.
  */
 protected function saveToDedicatedTables(ContentEntityInterface $entity, $update = TRUE, $names = array())
 {
     $vid = $entity->getRevisionId();
     $id = $entity->id();
     $bundle = $entity->bundle();
     $entity_type = $entity->getEntityTypeId();
     $default_langcode = $entity->getUntranslated()->language()->getId();
     $translation_langcodes = array_keys($entity->getTranslationLanguages());
     $table_mapping = $this->getTableMapping();
     if (!isset($vid)) {
         $vid = $id;
     }
     $original = !empty($entity->original) ? $entity->original : NULL;
     // Determine which fields should be actually stored.
     $definitions = $this->entityManager->getFieldDefinitions($entity_type, $bundle);
     if ($names) {
         $definitions = array_intersect_key($definitions, array_flip($names));
     }
     foreach ($definitions as $field_name => $field_definition) {
         $storage_definition = $field_definition->getFieldStorageDefinition();
         if (!$table_mapping->requiresDedicatedTableStorage($storage_definition)) {
             continue;
         }
         // When updating an existing revision, keep the existing records if the
         // field values did not change.
         if (!$entity->isNewRevision() && $original && !$this->hasFieldValueChanged($field_definition, $entity, $original)) {
             continue;
         }
         $table_name = $table_mapping->getDedicatedDataTableName($storage_definition);
         $revision_name = $table_mapping->getDedicatedRevisionTableName($storage_definition);
         // Delete and insert, rather than update, in case a value was added.
         if ($update) {
             // Only overwrite the field's base table if saving the default revision
             // of an entity.
             if ($entity->isDefaultRevision()) {
                 $this->database->delete($table_name)->condition('entity_id', $id)->execute();
             }
             if ($this->entityType->isRevisionable()) {
                 $this->database->delete($revision_name)->condition('entity_id', $id)->condition('revision_id', $vid)->execute();
             }
         }
         // Prepare the multi-insert query.
         $do_insert = FALSE;
         $columns = array('entity_id', 'revision_id', 'bundle', 'delta', 'langcode');
         foreach ($storage_definition->getColumns() as $column => $attributes) {
             $columns[] = $table_mapping->getFieldColumnName($storage_definition, $column);
         }
         $query = $this->database->insert($table_name)->fields($columns);
         if ($this->entityType->isRevisionable()) {
             $revision_query = $this->database->insert($revision_name)->fields($columns);
         }
         $langcodes = $field_definition->isTranslatable() ? $translation_langcodes : array($default_langcode);
         foreach ($langcodes as $langcode) {
             $delta_count = 0;
             $items = $entity->getTranslation($langcode)->get($field_name);
             $items->filterEmptyItems();
             foreach ($items as $delta => $item) {
                 // We now know we have something to insert.
                 $do_insert = TRUE;
                 $record = array('entity_id' => $id, 'revision_id' => $vid, 'bundle' => $bundle, 'delta' => $delta, 'langcode' => $langcode);
                 foreach ($storage_definition->getColumns() as $column => $attributes) {
                     $column_name = $table_mapping->getFieldColumnName($storage_definition, $column);
                     // Serialize the value if specified in the column schema.
                     $record[$column_name] = !empty($attributes['serialize']) ? serialize($item->{$column}) : $item->{$column};
                 }
                 $query->values($record);
                 if ($this->entityType->isRevisionable()) {
                     $revision_query->values($record);
                 }
                 if ($storage_definition->getCardinality() != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED && ++$delta_count == $storage_definition->getCardinality()) {
                     break;
                 }
             }
         }
         // Execute the query if we have values to insert.
         if ($do_insert) {
             // Only overwrite the field's base table if saving the default revision
             // of an entity.
             if ($entity->isDefaultRevision()) {
                 $query->execute();
             }
             if ($this->entityType->isRevisionable()) {
                 $revision_query->execute();
             }
         }
     }
 }