Issue #2013837 by yched, plach: Rewrite field sync on top of NG entities.

8.0.x
Alex Pott 2013-06-14 15:36:25 +02:00
parent 9ddac9e9fa
commit e56a599e83
2 changed files with 58 additions and 44 deletions

View File

@ -9,6 +9,8 @@ namespace Drupal\translation_entity;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityManager;
use Drupal\Core\Entity\EntityNG;
use Drupal\Core\Entity\Field\FieldInterface;
/**
* Provides field translation synchronization capabilities.
@ -33,9 +35,15 @@ class FieldTranslationSynchronizer implements FieldTranslationSynchronizerInterf
}
/**
* Implements \Drupal\translation_entity\FieldTranslationSynchronizerInterface::synchronizeFields().
* {@inheritdoc}
*/
public function synchronizeFields(EntityInterface $entity, $sync_langcode, $original_langcode = NULL) {
// Field synchronization is only supported for NG entities.
$entity = $entity->getNGEntity();
if (!($entity instanceof EntityNG)) {
return;
}
$translations = $entity->getTranslationLanguages();
// If we have no information about what to sync to, if we are creating a new
@ -52,17 +60,14 @@ class FieldTranslationSynchronizer implements FieldTranslationSynchronizerInterf
return;
}
// Enable compatibility mode for NG entities.
$entity_unchanged = $entity_unchanged->getBCEntity();
// @todo Use Entity Field API to retrieve field definitions.
$instances = field_info_instances($entity_type, $entity->bundle());
foreach ($instances as $field_name => $instance) {
$field = field_info_field($field_name);
$field = $instance->getField();
// Sync when the field is not empty, when the synchronization translations
// setting is set, and the field is translatable.
if (!empty($entity->{$field_name}) && !empty($instance['settings']['translation_sync']) && field_is_translatable($entity_type, $field)) {
if (!$entity->get($field_name)->isEmpty() && !empty($instance['settings']['translation_sync']) && field_is_translatable($entity_type, $field)) {
// Retrieve all the untranslatable column groups and merge them into
// single list.
$groups = array_keys(array_diff($instance['settings']['translation_sync'], array_filter($instance['settings']['translation_sync'])));
@ -74,12 +79,21 @@ class FieldTranslationSynchronizer implements FieldTranslationSynchronizerInterf
$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 = !empty($entity_unchanged->{$field_name}[$langcode]) ? $entity_unchanged->{$field_name}[$langcode] : array();
$this->synchronizeItems($entity->{$field_name}, $unchanged_items, $sync_langcode, array_keys($translations), $columns);
$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]);
}
}
}
}
@ -87,10 +101,10 @@ class FieldTranslationSynchronizer implements FieldTranslationSynchronizerInterf
}
/**
* Implements \Drupal\translation_entity\FieldTranslationSynchronizerInterface::synchronizeItems().
* {@inheritdoc}
*/
public function synchronizeItems(array &$field_values, array $unchanged_items, $sync_langcode, array $translations, array $columns) {
$source_items = $field_values[$sync_langcode];
public function synchronizeItems(array &$values, array $unchanged_items, $sync_langcode, array $translations, array $columns) {
$source_items = $values[$sync_langcode];
// Make sure we can detect any change in the source items.
$change_map = array();
@ -112,15 +126,16 @@ class FieldTranslationSynchronizer implements FieldTranslationSynchronizerInterf
}
// Backup field values and the change map.
$original_field_values = $field_values;
$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.
$field_values = array($sync_langcode => $source_items);
$values = array($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
@ -155,7 +170,7 @@ class FieldTranslationSynchronizer implements FieldTranslationSynchronizerInterf
// If a synchronized column has changed or has been created from
// scratch we need to override the full items array for all languages.
elseif ($created) {
$field_values[$langcode][$delta] = $source_items[$delta];
$values[$langcode][$delta] = $source_items[$delta];
}
// Otherwise the current item might have been reordered.
elseif (isset($old_delta) && isset($new_delta)) {
@ -165,7 +180,7 @@ class FieldTranslationSynchronizer implements FieldTranslationSynchronizerInterf
// 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];
$field_values[$langcode][$new_delta] = $item;
$values[$langcode][$new_delta] = $item;
}
}
}

View File

@ -88,7 +88,7 @@ class EntityTranslationSyncImageTest extends EntityTranslationTestBase {
$langcode = $this->langcodes[1];
// Populate the required contextual values.
$attributes = drupal_container()->get('request')->attributes;
$attributes = $this->container->get('request')->attributes;
$attributes->set('working_langcode', $langcode);
$attributes->set('source_langcode', $default_langcode);
@ -98,7 +98,7 @@ class EntityTranslationSyncImageTest extends EntityTranslationTestBase {
'user_id' => mt_rand(1, 128),
'langcode' => $default_langcode,
);
$entity = entity_create($this->entityType, $values)->getBCEntity();
$entity = entity_create($this->entityType, $values);
// Create some file entities from the generated test files and store them.
$values = array();
@ -122,10 +122,10 @@ class EntityTranslationSyncImageTest extends EntityTranslationTestBase {
// the entity.
$item = array(
'fid' => $fid,
'alt' => $this->randomName(),
'title' => $this->randomName(),
'alt' => $default_langcode . '_' . $fid . '_' . $this->randomName(),
'title' => $default_langcode . '_' . $fid . '_' . $this->randomName(),
);
$entity->{$this->fieldName}[$default_langcode][$delta] = $item;
$entity->{$this->fieldName}->offsetGet($delta)->setValue($item);
// Store the generated values keying them by fid for easier lookup.
$values[$default_langcode][$fid] = $item;
@ -147,10 +147,10 @@ class EntityTranslationSyncImageTest extends EntityTranslationTestBase {
$fid = $this->files[$index]->fid;
$item = array(
'fid' => $fid,
'alt' => $this->randomName(),
'title' => $this->randomName(),
'alt' => $langcode . '_' . $fid . '_' . $this->randomName(),
'title' => $langcode . '_' . $fid . '_' . $this->randomName(),
);
$entity->{$this->fieldName}[$langcode][$delta] = $item;
$entity->getTranslation($langcode)->{$this->fieldName}->offsetGet($delta)->setValue($item);
// Again store the generated values keying them by fid for easier lookup.
$values[$langcode][$fid] = $item;
@ -161,18 +161,18 @@ class EntityTranslationSyncImageTest extends EntityTranslationTestBase {
$entity = $this->saveEntity($entity);
// Check that one value has been dropped from the original values.
$assert = count($entity->{$this->fieldName}[$default_langcode]) == 2;
$assert = count($entity->{$this->fieldName}) == 2;
$this->assertTrue($assert, 'One item correctly removed from the synchronized field values.');
// Check that fids have been synchronized and translatable column values
// have been retained.
$fids = array();
foreach ($entity->{$this->fieldName}[$default_langcode] as $delta => $item) {
$value = $values[$default_langcode][$item['fid']];
$source_item = $entity->{$this->fieldName}[$langcode][$delta];
$assert = $item['fid'] == $source_item['fid'] && $item['alt'] == $value['alt'] && $item['title'] == $value['title'];
$this->assertTrue($assert, format_string('Field item @fid has been successfully synchronized.', array('@fid' => $item['fid'])));
$fids[$item['fid']] = TRUE;
foreach ($entity->{$this->fieldName} as $delta => $item) {
$value = $values[$default_langcode][$item->fid];
$source_item = $entity->getTranslation($langcode)->{$this->fieldName}->offsetGet($delta);
$assert = $item->fid == $source_item->fid && $item->alt == $value['alt'] && $item->title == $value['title'];
$this->assertTrue($assert, format_string('Field item @fid has been successfully synchronized.', array('@fid' => $item->fid)));
$fids[$item->fid] = TRUE;
}
// Check that the dropped value is the right one.
@ -180,30 +180,29 @@ class EntityTranslationSyncImageTest extends EntityTranslationTestBase {
$this->assertTrue(!isset($fids[$removed_fid]), format_string('Field item @fid has been correctly removed.', array('@fid' => $removed_fid)));
// Add back an item for the dropped value and perform synchronization again.
// @todo Actually we would need to reset the contextual information to test
// an update, but there is no entity field class for image fields yet,
// hence field translation update does not work properly for those.
$values[$langcode][$removed_fid] = array(
'fid' => $removed_fid,
'alt' => $this->randomName(),
'title' => $this->randomName(),
'alt' => $langcode . '_' . $removed_fid . '_' . $this->randomName(),
'title' => $langcode . '_' . $removed_fid . '_' . $this->randomName(),
);
$entity->{$this->fieldName}[$langcode] = array_values($values[$langcode]);
$entity->getTranslation($langcode)->{$this->fieldName}->setValue(array_values($values[$langcode]));
// When updating an entity we do not have a source language defined.
$attributes->remove('source_langcode');
$entity = $this->saveEntity($entity);
// Check that the value has been added to the default language.
$assert = count($entity->{$this->fieldName}[$default_langcode]) == 3;
$assert = count($entity->{$this->fieldName}->getValue()) == 3;
$this->assertTrue($assert, 'One item correctly added to the synchronized field values.');
foreach ($entity->{$this->fieldName}[$default_langcode] as $delta => $item) {
foreach ($entity->{$this->fieldName} as $delta => $item) {
// When adding an item its value is copied over all the target languages,
// thus in this case the source language needs to be used to check the
// values instead of the target one.
$fid_langcode = $item['fid'] != $removed_fid ? $default_langcode : $langcode;
$value = $values[$fid_langcode][$item['fid']];
$source_item = $entity->{$this->fieldName}[$langcode][$delta];
$assert = $item['fid'] == $source_item['fid'] && $item['alt'] == $value['alt'] && $item['title'] == $value['title'];
$this->assertTrue($assert, format_string('Field item @fid has been successfully synchronized.', array('@fid' => $item['fid'])));
$fid_langcode = $item->fid != $removed_fid ? $default_langcode : $langcode;
$value = $values[$fid_langcode][$item->fid];
$source_item = $entity->getTranslation($langcode)->{$this->fieldName}->offsetGet($delta);
$assert = $item->fid == $source_item->fid && $item->alt == $value['alt'] && $item->title == $value['title'];
$this->assertTrue($assert, format_string('Field item @fid has been successfully synchronized.', array('@fid' => $item->fid)));
}
}
@ -219,7 +218,7 @@ class EntityTranslationSyncImageTest extends EntityTranslationTestBase {
protected function saveEntity(EntityInterface $entity) {
$entity->save();
$entity = entity_test_mul_load($entity->id(), TRUE);
return $entity->getBCEntity();
return $entity;
}
}