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\EntityInterface;
use Drupal\Core\Entity\EntityManager; use Drupal\Core\Entity\EntityManager;
use Drupal\Core\Entity\EntityNG;
use Drupal\Core\Entity\Field\FieldInterface;
/** /**
* Provides field translation synchronization capabilities. * 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) { 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(); $translations = $entity->getTranslationLanguages();
// If we have no information about what to sync to, if we are creating a new // 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; return;
} }
// Enable compatibility mode for NG entities.
$entity_unchanged = $entity_unchanged->getBCEntity();
// @todo Use Entity Field API to retrieve field definitions. // @todo Use Entity Field API to retrieve field definitions.
$instances = field_info_instances($entity_type, $entity->bundle()); $instances = field_info_instances($entity_type, $entity->bundle());
foreach ($instances as $field_name => $instance) { 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 // Sync when the field is not empty, when the synchronization translations
// setting is set, and the field is translatable. // 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 // Retrieve all the untranslatable column groups and merge them into
// single list. // single list.
$groups = array_keys(array_diff($instance['settings']['translation_sync'], array_filter($instance['settings']['translation_sync']))); $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)); $columns = array_merge($columns, isset($info['columns']) ? $info['columns'] : array($group));
} }
if (!empty($columns)) { 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 // If a translation is being created, the original values should be
// used as the unchanged items. In fact there are no unchanged items // used as the unchanged items. In fact there are no unchanged items
// to check against. // to check against.
$langcode = $original_langcode ?: $sync_langcode; $langcode = $original_langcode ?: $sync_langcode;
$unchanged_items = !empty($entity_unchanged->{$field_name}[$langcode]) ? $entity_unchanged->{$field_name}[$langcode] : array(); $unchanged_items = $entity_unchanged->getTranslation($langcode)->get($field_name)->getValue();
$this->synchronizeItems($entity->{$field_name}, $unchanged_items, $sync_langcode, array_keys($translations), $columns); $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) { public function synchronizeItems(array &$values, array $unchanged_items, $sync_langcode, array $translations, array $columns) {
$source_items = $field_values[$sync_langcode]; $source_items = $values[$sync_langcode];
// Make sure we can detect any change in the source items. // Make sure we can detect any change in the source items.
$change_map = array(); $change_map = array();
@ -112,15 +126,16 @@ class FieldTranslationSynchronizer implements FieldTranslationSynchronizerInterf
} }
// Backup field values and the change map. // Backup field values and the change map.
$original_field_values = $field_values; $original_field_values = $values;
$original_change_map = $change_map; $original_change_map = $change_map;
// Reset field values so that no spurious one is stored. Source values must // Reset field values so that no spurious one is stored. Source values must
// be preserved in any case. // be preserved in any case.
$field_values = array($sync_langcode => $source_items); $values = array($sync_langcode => $source_items);
// Update field translations. // Update field translations.
foreach ($translations as $langcode) { foreach ($translations as $langcode) {
// We need to synchronize only values different from the source ones. // We need to synchronize only values different from the source ones.
if ($langcode != $sync_langcode) { if ($langcode != $sync_langcode) {
// Reinitialize the change map as it is emptied while processing each // 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 // If a synchronized column has changed or has been created from
// scratch we need to override the full items array for all languages. // scratch we need to override the full items array for all languages.
elseif ($created) { elseif ($created) {
$field_values[$langcode][$delta] = $source_items[$delta]; $values[$langcode][$delta] = $source_items[$delta];
} }
// Otherwise the current item might have been reordered. // Otherwise the current item might have been reordered.
elseif (isset($old_delta) && isset($new_delta)) { 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 // If the value has only been reordered we just move the old one in
// the new position. // the new position.
$item = isset($original_field_values[$langcode][$old_delta]) ? $original_field_values[$langcode][$old_delta] : $source_items[$new_delta]; $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]; $langcode = $this->langcodes[1];
// Populate the required contextual values. // Populate the required contextual values.
$attributes = drupal_container()->get('request')->attributes; $attributes = $this->container->get('request')->attributes;
$attributes->set('working_langcode', $langcode); $attributes->set('working_langcode', $langcode);
$attributes->set('source_langcode', $default_langcode); $attributes->set('source_langcode', $default_langcode);
@ -98,7 +98,7 @@ class EntityTranslationSyncImageTest extends EntityTranslationTestBase {
'user_id' => mt_rand(1, 128), 'user_id' => mt_rand(1, 128),
'langcode' => $default_langcode, '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. // Create some file entities from the generated test files and store them.
$values = array(); $values = array();
@ -122,10 +122,10 @@ class EntityTranslationSyncImageTest extends EntityTranslationTestBase {
// the entity. // the entity.
$item = array( $item = array(
'fid' => $fid, 'fid' => $fid,
'alt' => $this->randomName(), 'alt' => $default_langcode . '_' . $fid . '_' . $this->randomName(),
'title' => $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. // Store the generated values keying them by fid for easier lookup.
$values[$default_langcode][$fid] = $item; $values[$default_langcode][$fid] = $item;
@ -147,10 +147,10 @@ class EntityTranslationSyncImageTest extends EntityTranslationTestBase {
$fid = $this->files[$index]->fid; $fid = $this->files[$index]->fid;
$item = array( $item = array(
'fid' => $fid, 'fid' => $fid,
'alt' => $this->randomName(), 'alt' => $langcode . '_' . $fid . '_' . $this->randomName(),
'title' => $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. // Again store the generated values keying them by fid for easier lookup.
$values[$langcode][$fid] = $item; $values[$langcode][$fid] = $item;
@ -161,18 +161,18 @@ class EntityTranslationSyncImageTest extends EntityTranslationTestBase {
$entity = $this->saveEntity($entity); $entity = $this->saveEntity($entity);
// Check that one value has been dropped from the original values. // 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.'); $this->assertTrue($assert, 'One item correctly removed from the synchronized field values.');
// Check that fids have been synchronized and translatable column values // Check that fids have been synchronized and translatable column values
// have been retained. // have been retained.
$fids = array(); $fids = array();
foreach ($entity->{$this->fieldName}[$default_langcode] as $delta => $item) { foreach ($entity->{$this->fieldName} as $delta => $item) {
$value = $values[$default_langcode][$item['fid']]; $value = $values[$default_langcode][$item->fid];
$source_item = $entity->{$this->fieldName}[$langcode][$delta]; $source_item = $entity->getTranslation($langcode)->{$this->fieldName}->offsetGet($delta);
$assert = $item['fid'] == $source_item['fid'] && $item['alt'] == $value['alt'] && $item['title'] == $value['title']; $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']))); $this->assertTrue($assert, format_string('Field item @fid has been successfully synchronized.', array('@fid' => $item->fid)));
$fids[$item['fid']] = TRUE; $fids[$item->fid] = TRUE;
} }
// Check that the dropped value is the right one. // 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))); $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. // 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( $values[$langcode][$removed_fid] = array(
'fid' => $removed_fid, 'fid' => $removed_fid,
'alt' => $this->randomName(), 'alt' => $langcode . '_' . $removed_fid . '_' . $this->randomName(),
'title' => $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); $entity = $this->saveEntity($entity);
// Check that the value has been added to the default language. // 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.'); $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, // 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 // thus in this case the source language needs to be used to check the
// values instead of the target one. // values instead of the target one.
$fid_langcode = $item['fid'] != $removed_fid ? $default_langcode : $langcode; $fid_langcode = $item->fid != $removed_fid ? $default_langcode : $langcode;
$value = $values[$fid_langcode][$item['fid']]; $value = $values[$fid_langcode][$item->fid];
$source_item = $entity->{$this->fieldName}[$langcode][$delta]; $source_item = $entity->getTranslation($langcode)->{$this->fieldName}->offsetGet($delta);
$assert = $item['fid'] == $source_item['fid'] && $item['alt'] == $value['alt'] && $item['title'] == $value['title']; $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']))); $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) { protected function saveEntity(EntityInterface $entity) {
$entity->save(); $entity->save();
$entity = entity_test_mul_load($entity->id(), TRUE); $entity = entity_test_mul_load($entity->id(), TRUE);
return $entity->getBCEntity(); return $entity;
} }
} }