Closed iamEAP closed 8 years ago
I see two directions to hit this problem:
Try and fix it at the spot where it's detected... Specifically, when SomeTranslatable::getTargetEntity($lang)
is called and within it, $this->entity->value() === NULL
.
We could resolve the issue in this scenario by "creating" a blank entity/bundle, wrapping it, and setting it as the target. Although we can figure out the entity type and even field name relatively easily (based on data in the metadata wrapper), there's no easy way to determine the bundle of the wrapped entity...
In order to do that, we'd have to traverse the wrapper's parents until we got to the root entity... Determine the source entity (because we're looking at the target at this point), then dive back down into its children (same route as we took back up) just to get the bundle.
Summary: Trivial issue detection, difficult resolution.
Try and fix it before it happens... Specifically, when a target entity is being pulled, check the entity reference fields of the target compared to the source entity... If there are values in the source that are NULL in the target, clone them to the target before wrapping it.
Summary: Messier detection, relatively simple resolution.
There may be more exotic solutions...
Even the "top down" approach ends up being extremely cumbersome and I still couldn't get the regression tests to pass. Pasting here for reference...
/**
* Prepares an existing target entity for translation. Used to account for new
* content structure on the source that does not yet exist on the target.
*
* @param object $source
* @param object $target
* @return object
*/
public function prepareTarget($source, $target) {
$translatableFields = $this->getTranslatableFields();
$this->drupal->alter('entity_xliff_translatable_fields', $translatableFields, $this->entity);
foreach ($translatableFields as $field) {
$info = $this->entity->getPropertyInfo($field);
$pattern = '/^(list)?<?(.*?)(?:>)?$/';
if (isset($info['type']) && preg_match($pattern, $info['type'], $matches)) {
$type = $matches[2];
$isList = isset($matches[1]) && !empty($matches[1]);
$isEntity = isset($this->entityInfo[$type]);
if ($isEntity) {
$sourceWrapper = entity_metadata_wrapper('node', $source);
$sourceField = $sourceWrapper->{$field};
$targetWrapper = entity_metadata_wrapper('node', $target);
$targetField = $targetWrapper->{$field};
$entityCreateArray = function($entityInfo, $source) {
$keys = array();
foreach ($entityInfo['entity keys'] as $keyId => $key) {
if (!in_array($keyId, array('id', 'revision'))) {
$keys[$key] = $source->{$key};
}
}
return $keys;
};
if ($isList && count($sourceField->value()) > count($targetField->value())) {
foreach ($sourceField->value() as $delta => $valueWrapper) {
if (!isset($targetField[$delta])) {
$template = entity_create($type, $entityCreateArray($this->entityInfo[$type], $valueWrapper));
$template->save(TRUE);
$targetWrapper->{$field}[$delta] = $template;
}
}
}
elseif ($sourceField->value() !== NULL && $targetField->value() === NULL) {
$template = entity_create($type, $entityCreateArray($this->entityInfo[$type], $sourceField->value()));
$template->save(TRUE);
$targetWrapper->{$field} = $template;
}
}
}
}
return isset($targetWrapper) ? $targetWrapper->value() : $target;
What I ended up doing is essentially re-implementing translation_node_prepare()
...
...Which gave me the idea to just re-use the function against the source node, and then simply remove the $node->is_new
flag and paste in the nid/vid of the existing translation. It's a bit more wasteful in terms of Paragraphs/Field Collections (because new ones are created each time, rather than re-using existing versions), but I believe it's the only way to rid ourselves of this problem...
Still requires manual testing...
Manual testing has revealed that, although this fixes the fatal error... It dissociates the translation from the source node...
Manual testing of the changes above check out with expectations.
Todo
Closes #80