Problem/Motivation
ContentEntityBase keeps track of the status of each translation - (TRANSLATION_REMOVED, TRANSLATION_EXISTING or TRANSLATION_CREATED). There are use cases in contrib, where this information needs to be accessed. Therefore the feature request to add the getTranslationStatus function to the entity API. This funciton was as well mentioned in #1810370: Entity Translation API improvements.
Proposed resolution
In order to make it work properly removeTranslation should completely remove any trace of a translation which has been added and then removed without being saved, in this case removeTranslaiton sets the status to TRANSLATION_REMOVED, which from the point of getTranslationStatus is wrong, as the translation did not existed yet, it was added and removed without saving it. The proper return status should be NULL.
The second change should be done in addTranslation. If an existing translation has been removed and afterwards added again (wihout saving) the translation status would be TRANSLATION_CREATED, which from the point of getTranslationStatus is wrong, as the translation existed already, it was removed but not saved and added again. The proper return status should be TRANSLATION_EXISTING.
Third change : After creating a new entity it had the TRANSLATION_EXISTING status, but it should be TRANSLATION_CREATED.
Fourth change : After saving the entity the translation states have to be updated.
Additionally there is the new interface TranslationStatusInterface, which ContentEntityBase implements and provides the new function TranslationStatusInterface::getTranslationStatus($langcode).
Remaining tasks
Review.
User interface changes
none
API changes
none.
Change record - https://www.drupal.org/node/2789903
Data model changes
none
Comment | File | Size | Author |
---|---|---|---|
#45 | interdiff-27-45.txt | 799 bytes | hchonov |
#45 | 2666032-45.patch | 10.74 KB | hchonov |
Comments
Comment #2
hchonovComment #3
hchonovComment #5
hchonovI thought that the translatable entity keys could be removed as well, but it seems that the entity translation is used even after its deletion.
Comment #6
mkalkbrennerComment #8
hchonovComment #9
hchonovComment #10
Gábor HojtsyReviewing the issue, I did not find anything that does not sound like oversights or where you are not fixing inaccuracies. Keep in mind I am not very well versed in the internals of this code, but the changes look very logical. Indeed tests are still needed.
Nitpick: spacing.
The biggest concern is changing an existing interface in core could very well be problematic for backwards compatibility. I think we ideally want to have a new interface that inherits from the old one and deprecate the old one. Otherwise this patch may start to WSOD contrib modules, which does not satisfy backwards compatibility.
Comment #11
hchonovThere should be no problem that I am moving the constants from ContentEntityBase to the TranslatableInterface, because the ContentEntityInterface extends the TranslatableInterface
Beside that I've added tests and this way I've discovered, that there are needed some adjustments :
1. After creating a new entity it had the TRANSLATION_EXISTING status, but it should be TRANSLATION_CREATED.
2. After saving the entity the translation states have to be updated.
The tests cover all these cases now.
Comment #12
hchonovComment #13
mkalkbrenner+1 for the new feature!
I think you should create a change record for the new API and point out that the move of the constants is backwards compatible.
Beside that => RTBC
Comment #15
hchonovCreated a draft change record, which is to be published on commit.
Comment #16
Gábor HojtsyRepeating myself from two months ago:
Comment #17
hchonov@Gábor Hojtsy: I am not really sure, how my changes could cause any problems. Like I've already mentioned, currently the constants are defined in ContentEntityBase, which implements the ContentEntityInterface, which on its turn extends the TranslatableInterface. I could not imagine how moving the constants upward in the hierarchy (from ContentEntityInterface to the TranslatableInterface) could cause any problems, as they remain accessible downward in the hierarchy and contrib will not notice any change here.
Comment #18
Gábor Hojtsy@hchonov: you are adding a new method. Consider this:
1. core provides an interface
2. contrib implements the interface
3. core adds a new method on the interface
4. site updates to new core
5=> whitescreen because contrib does not yet implement the new method
I don't think that matches the definition of backwards compatibility we intend to support. See https://www.drupal.org/core/d8-allowed-changes#bc-break
Does this change require changes in code implementing a public API? Yes.
Comment #19
mkalkbrennerGabor's argumentation is correct.
If someone implements the TranslatableInterface in contrib, it will broken until public function getTranslationStatus() is implemented. Regarding the moved constants, everything is OK.
So a backward compatible implementation will be to create a TranslationStatusInterface for the constants and the accessor function.
Comment #20
Gábor HojtsyYeah @mkalkbrenner's solution sounds almost elegant :) Also ContentEntityBase would then implement that interface thus implementing that method and receiving the constants.
Comment #21
hchonovI've created the TranslationStatusInterface and flagged it directly as deprecated and that it will be merged into the TranslatableInterface in Drupal 9.0.0.
The constants are currently present at two places - in ContentEntityBase and in TranslationStatusInterface, but in Drupal 9.0.0 should moved to the TranslatableInterface.
When this gets committed I'll create an another issue for both points.
Comment #22
hchonovComment #23
hchonovComment #24
Gábor HojtsyI don't think this resolved any of the concerns with backwards compatibility. The same problem applies to ContentEntityInterface, making it composite does not make a difference.
Making ContentEntityBase implementing it would still let anyone else impplementing ContentEntityInterface to be backwards compatible with code extending from ContentEntityBase would get the new method/interface.
Comment #25
Gábor HojtsyAlso I don't think its correct to mark something as deprecated if we don't have a replacement for it in core and we want people to use it. Things that are deprecated are supposed to be not used in favor of something better already available or in favor of being not used because they are bad.
Comment #26
mkalkbrennerLike Gabor already pointed out, ContentEntityInterface must not extend TranslationStatusInterface to keep BC. Instead, ContentEntityBase should implement the interface.
The merge should be discussed after the new interface has been committed in a different issue.
From my point of view it's just a nice to have.
If someone currently implements the TranslatableInterface directly without extending ContentEntityBase, I bet he's not tracking the translation state in his implementation ;-)
The feature itself is very useful for us and we should get it to a committable state quickly :-)
Comment #27
hchonovNow only ContentEntityBase implements the new interface.
Comment #28
Gábor HojtsyYay, looks all good now.
Comment #29
catchThe patch had more change than I anticipated, so just want to clarify exactly what's happening:
1. Right now there's no way to get this information (except in subclasses of ContentEntityBase maybe)
2. If we just add a way to get the information, it will be wrong in some cases (presumably these are cases ContentEntityBase itself does not care about).
3. So the patch both fixes the statuses, and adds the way to get them at the same time.
I don't see a way to disentangle these, tried an issue re-title to clarify what's happening a bit more.
Comment #31
Gábor HojtsyPasses again.
Comment #32
catchComment #33
Gábor Hojtsy@catch: any concerns that would stop committing this? :)
Comment #34
catchI don't have anything concrete, but also haven't managed to commit it yet. I pinged xjm and alexpott for a second look just in case.
Comment #35
plachChanges look reasonable to me too, thanks!
Comment #36
alexpottI looked at this and worried about whether the statuses should be set before calling parent::postSave() but looking at the code this is fine because the only that occurs in the parent is:
This looks like this is fixing a bug / changing behaviour? Also I think we should call $this->isNew() instead of
isset($values[$this->getEntityType()->getKey('id')])
since if we're enforcing newness then the status should be TRANSLATION_CREATED no?Comment #37
hchonovThis is what we remove and we introduce the following
where if the id entity key "id" is present in the "values" then the translation status is "existing" and otherwise "created".
I think I got problems when calling isNew.. but I'll give it another try.
Comment #40
hchonovThere we see we cannot use $this->isNew inside the constructor. So the patch from #27 still applies.
Comment #41
mkalkbrenner@hchonov: In this case, patch #27 should be uploaded again to be the latest one to not confusing people.
Comment #42
hchonov@alexpott I've somehow got your question wrong.. Yes, we are fixing a bug, so if the entity is new, then the status should be TRANSLATION_CREATED instead of TRANSLATION_EXISTING.
And like we've seen in #37 the usage of $this->isNew is not possible in the constructor, so I am re-uploading the patch from #27 like mkalkbrenner suggested.
Comment #43
Gábor HojtsyAs good as it was in #31 :)
Comment #44
catchSorry to knock this back again, but let's add a comment in the constructor explaining that we can't use isNew() there, then no-one will wonder next time they look at it.
Comment #45
hchonov@catch, sure it makes sense :).
Comment #46
Gábor HojtsyThanks for the fix!
Comment #48
catchCommitted a469aa2 and pushed to 8.3.x. Thanks!
Comment #49
Gábor HojtsyYay thanks, made a minor modification to the change notice and published at https://www.drupal.org/node/2789903.
Comment #50
hchonovThanks everyone for the reviews and accepting the feature into the core.