Problem/Motivation
When it is created paragraphed content type (for instance) and added translation (any language), removing one of entities translation removes the content.
By observation and experiments from @Berdir, the code that actually deletes is in \Drupal\entity_reference_revisions\Plugin\Field\FieldType\EntityReferenceRevisionsItem::delete() and it is called per translation.
Proposed resolution
Firstly, would be good to extend existing tests to verify this.
We should test those two things on the API level:
1. Create an entity like done there with a composite, add a translation. Delete the entity. This must delete the composite.
2. Create an entity like it does there with a composite, add a translation. Delete the translation. This must not delete. Then delete the entity, now we must delete the composite.
Remaining tasks
User interface changes
API changes
Data model changes
| Comment | File | Size | Author |
|---|---|---|---|
| #17 | delete_translations-2834034-17.patch | 15.03 KB | ModernMantra |
| #15 | 2834034-15.patch | 16.29 KB | hchonov |
| #14 | 2834034-14.patch | 2.74 KB | hchonov |
| #13 | interdiff-2834034-6-13.txt | 2.6 KB | ModernMantra |
| #13 | delete_translation-2834034-13.patch | 14.99 KB | ModernMantra |
Comments
Comment #2
ModernMantra commentedTried to write test coverage... I need some more work on that
Comment #3
berdirWorked on tests, also converted to a kernel test.
So, the good part is that this is not as bad as I feared. In fact, it only affects translatable fields (Note that fields by default are translatable, which is not what we expect in paragraphs and composite fields in general).
The bad part is that it *is* resulting in data loss in case of translatable fields. And I'm not sure how to deal with it properly. How do we decide when to delete and when to not delete a reference? We need to know if there are other translations that reference the same entity. It might even be in a different delta or so. And I honestly don't know how to track that.. Only idea i have right now is to disable deleting the data as a quickfix for translatable fields. Or maybe move it to hook_entity_delete(), so we can see when the entity is actually deleted.
Also something that we haven't talked about yet but might be even worse is deleting revisions. I suspect that actually fails consistently, for all kinds of fields. But probably better to do a new issue for that.
Comment #5
miro_dietikerWith translatable refrences, my proposal was always to start with non-shared paragraphs. Each entity would be only used in one language. Deleting the child entity would then be fine.
A mixed mode can only work if we do reference counting. However i'm not sure how much this is worth it.
Comment #6
berdirRight, but how would we enforce that exactly? There is a translation_created hook but there doesn't seem to be something on the field level. So we probably need to implement hook_entity_delete(), hook_entity_translation_delete() and hook_entity_translation_create().
Also wrote tests for revisions. Also there, it is not as bad as I feared, as deleting a revision does call a separate method. Only problem is that we don't delete old revisions, but withou enforcing that we always have a unique revision per host revision, for which we have some issues, we can't really do that. So we comment that part of the test out.
Also added a check for deleting a translation when the parent deletes it.
That means we now have the following 3 failures:
* Deleting a translation on a translatable field deletes the target even though it might still be in use by other translations
* Deleting a translation on a non-translatable field does not delete the translation of the composite
* Deleting a revision does not delete the revision of the composite
Also had to make entity_test_composite actually translatable, it wasn't before. Pretty weird that you can add translations that are then silently dropped if the entity is not translatable. But you can still translate configurable fields, so that's tricky.
Comment #8
hchonovI started working a patch however one of the problems I came across is that the field item delete method is not called on non-translatable fields when deleting a translation on the parent.
@see \Drupal\Core\Entity\ContentEntityStorageBase::invokeFieldMethod().
Comment #9
hchonovBeside that my approach looks like this :
Comment #10
hchonovBut I guess the else branch for a non-translatable field should be moved to the method ::preSave.
Comment #11
berdirThe fact that delete() on the field doesn't run for non-translatable fields actually prevents us from losing data in more cases, as we don't support translatable fields in paragraphs yet anyway.
My current plan is to do a quickfix here that simply disables deletion for translatable fields and then we open separate issues for the supporting that problem and the other issues. I imagine it will be easier to support those things from the translation_delete and similar hooks instead of tryin to do it from within the field.
Comment #12
hchonovI will try to provide a patch later today for the latest test in this issue.
Comment #13
ModernMantra commentedAs discussed with @berdir. created some follow-ups, fixing test failure and referenced issues that was created.
Comment #14
hchonovSo I have a solution and the test provided by @berdir is green now but for this to happen you have to apply the core patch from #2834384: Inconsistency: in FieldItem::delete we do not know if we are deleting the parent or only removing a translation.
Comment #15
hchonovOups I've forgotten to attach the test to the patch :).
Comment #16
berdirsyntax is not consistent for the URL, also should be on a separate line as it is too long.
Missing @todo ;)
Also, you can work on splitting up the different fixes from @hchonov's patch (thanks!) into the corresponding issues, at least post it as a commont or so. And once this is in, you can at least post a failing patch in each of those issues, simply by commenting out the relevant test line.
Even if we wouldn't have to depend on the core issue, some of those changes can be problematic, for example we currently don't enforce new revisions when the host gets a new one, so all you need to do is $node->setNewRevision(); $node->save(); and then delete that revision and your paragraphs are gone :) But we have an issue to enforce that, so we can wait on that.
Comment #17
ModernMantra commentedSmall fixes in comments regarding previous comment.
Comment #19
berdirSee ... should be indented by two spaces. And one space before the commented out line.
> 80 characters, should be written the same as the one above, with See .. on separate line
This @todo can be linked to #2801321: New host revisions do not always create new composite entity revisions, again, like the others.
and.. same here re formatting.
Those are all only comment changes, so can also be fixed by miro before commit. I think we have all the follow-ups now and we can then move @hchonov's fixes to the respective issues.
Comment #21
miro_dietikerCommitted with comment fixes as requested. Plus added/improved some more comment.