Problem/Motivation
The \Drupal\Core\Entity\EntityStorageBase::doPreSave loads $this->loadUnchanged($id) into $entity->original when content moderation is being used and if the currently saved entity is not the default revision.
I'm not sure about the impact in core itself, but we see contrib modules working around this with their own additional load mechanisms when using any of the entity hooks where field value comparisons are being made.
Similar issues have been identified and in some cases already being fixed e.g. in #2859042: Impossible to update an entity revision if the field value you are updating matches the default revision., #2833084: $entity->original doesn't adequately address intentions when saving a revision and #2839195: Add a method to access the original property .
Proposed resolution
Load the latest revision if available instead of the default revision:
// Load the original entity, if any.
if ($id_exists && !isset($entity->original)) {
- $entity->original = $this->loadUnchanged($id);
+ $entity->original = ($entity instanceof RevisionableInterface && $revision_id = $entity->getLoadedRevisionId()) ?
+ $this->loadRevision($revision_id) :
+ $this->loadUnchanged($id);
}
Issue fork drupal-3346430
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
jurgenhaasComment #4
smustgrave commentedSeems the change is causing failures so may need to relook at that.
But this will need a test case also.
Comment #7
berdirAs commented in slack, #2839195: Add a method to access the original property now includes a fix for this. The logic for this shouldn't be in EntityStorageBase but ContentEntityStorageBase as EntityStorageBase won't include revision methods anymore with D11. My implementation also avoids to load the original twice by doing it first.
Comment #8
jurgenhaasThanks for the cross-reference @Berdir. I looked at it, and it's great to get an official original property. But I'm not seeing where that is covering the issue stated here, where
$this->loadUnchanged($id)loads the latests default revision, but not necessarily the current revision that's being pre-saved.Example: revision 1 is published, revision 2 is draft and gets edited and then saved. In pre-save the
$this->loadUnchanged($id)loads revision 1, but original should be revision 2.I may have missed that in you other issue, is that really covered?
Comment #9
berdirSee #103 there and the changes in ContentEntityStorageBase and SqlContentEntityStorageBase.
Comment #10
jurgenhaasAh, that's what I missed:
I wonder if that if-statement shouldn't also verify
$entity instanceof RevisionableInterfaceand if so, that part of the fix would be similar to what I had proposed here. So the fix for loading the correct revision would be this:And #2839195: Add a method to access the original property would be about the property
original.Let me see if that gets tested properly.
Comment #11
berdirIt's not necessary to check revisionable because all content entities implement that.
This issue is still a duplicate of the two existing ones and I'd prefer to fix them together, that also allows us to document the changed behavior.
Comment #12
bburch commentedI tried to apply a diff based on the current MR to Drupal 10.2.2, and the patch doesn’t apply.
Where can I get a patch that will apply to the current release version of Drupal core?
Comment #13
jurgenhaas@bburch Sorry, I can't reproduce that. The patch from https://git.drupalcode.org/project/drupal/-/merge_requests/3599.diff does apply here for Drupal 10.2.2 - any chance you have another patch in your configuration which gets applied first and therefore may prevent this one from applying as well?
Comment #15
bburch commentedGot it working -- our mistake:
In the “extra” “patches” section of the composer.json file, we had the array key as “drupal/eca”.
But we aren’t patching the ECA module, we’re patching core. So, it should be:
"drupal/core": {
"fix ECA workflow transitions": "./patches/eca-3599.patch"
}
Comment #16
jurgenhaas@Anchal_gupta what deprecation did you try to fix with your commit to this MR? I'm asking because that change breaks core entirely as it turns
\Drupal\Core\Config\Entity\ConfigEntityStorageinto an abstract class because some methods are missing from the RevisionableStorageInterface. Not sure what your intention was?Comment #17
makbay commentedI think it's about this:
I had on tests for a similar issue here: https://www.drupal.org/project/drupal/issues/3201209
https://git.drupalcode.org/issue/drupal-3201209/-/pipelines/97456
And also, as I mentioned on #3201209, !$entity->isDefaultRevision() does not work correctly when the latest revision is default but the previous one is not: https://www.drupal.org/project/drupal/issues/3201209#comment-15451531
Comment #18
makbay commentedComment #19
lukusThis patch is now causing our build test runner to fail when trying to install our site from scratch.
Comment #20
jurgenhaas@makbay as I mentioned before, the approach in this MR will never land in core since @Berdir stated that this issue is being addressed in 2 other issues already. So, as you already contributed to one of the others, it's probably not ideal to modify this MR here as well. So, I'll revert the latest change, close the MR and also this issue as won't fix.
@lukus without further detail it's hard to tell how the patch from the MR could possibly prevent a re-install. But then, you shouldn't use an MR as a patch source either, as this is always subject to changes that may be causing trouble.
Comment #22
lukus> @lukus without further detail it's hard to tell how the patch from the MR could possibly prevent a re-install. But then, you shouldn't use an MR as a patch source either, as this is always subject to changes that may be causing trouble.
@jurgenhaas - I totally agree. Moving this into a patch file and adding to our codebase for now, while I work out why the patch was included in the first place.
You mention it's being addressed elsewhere, so maybe it's not required?
Comment #23
jurgenhaasIn fact, it's not "Won't fix", according to #11 it's a duplicate.
@lukus whether you require this or any patches from one or more of the related issues depends on your use case. For us, we're currently living with this simple "workaround" to correctly recognize state changes when working with ECA and the workflows from Drupal core.
Comment #24
janpongos commentedI think the deprecation issue mentioned for !$entity->isDefaultRevision() is to use $entity->isDefaultRevision(FALSE)
That fixes it. Tested with Core 10.3.2
Comment #26
janpongos commentedok so this is a dangerous update - MR 9272
I misinterpreted the method as if it's a query.
Please ignore that MR.