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

Command icon 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

jurgenhaas created an issue. See original summary.

jurgenhaas’s picture

Status: Active » Needs review
smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs Review Queue Initiative, +Needs tests

Seems the change is causing failures so may need to relook at that.

But this will need a test case also.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

lauriii made their first commit to this issue’s fork.

berdir’s picture

As 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.

jurgenhaas’s picture

Thanks 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?

berdir’s picture

See #103 there and the changes in ContentEntityStorageBase and SqlContentEntityStorageBase.

jurgenhaas’s picture

Ah, that's what I missed:

// Use the loaded revision instead of default if this is not the default revision.
if (!$entity->getOriginal() && !$entity->isDefaultRevision()) {
  $entity->setOriginal($this->loadRevision($entity->getLoadedRevisionId()));
}

I wonder if that if-statement shouldn't also verify $entity instanceof RevisionableInterface and 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:

// Use the loaded revision instead of default if this is not the default revision.
if ($id_exists && !isset($entity->original) && $entity instanceof RevisionableInterface && !$entity->isDefaultRevision()) {
  $entity->original = $this->loadRevision($entity->getLoadedRevisionId());
}

And #2839195: Add a method to access the original property would be about the property original.

Let me see if that gets tested properly.

berdir’s picture

It'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.

bburch’s picture

I 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?

jurgenhaas’s picture

@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?

Anchal_gupta made their first commit to this issue’s fork.

bburch’s picture

Got 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"
}

jurgenhaas’s picture

@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\ConfigEntityStorage into an abstract class because some methods are missing from the RevisionableStorageInterface. Not sure what your intention was?

makbay’s picture

I think it's about this:

Call to deprecated method loadRevision() of class               
         Drupal\Core\Entity\EntityStorageInterface:                      
         in drupal:10.1.0 and is removed from drupal:11.0.0. Use         
         \Drupal\Core\Entity\RevisionableStorageInterface::loadRevision  
         instead.   

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

lukus’s picture

This patch is now causing our build test runner to fail when trying to install our site from scratch.

jurgenhaas’s picture

Status: Needs work » Closed (won't fix)

@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.

lukus’s picture

> @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?

jurgenhaas’s picture

Status: Closed (won't fix) » Closed (duplicate)

In 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.

janpongos’s picture

I think the deprecation issue mentioned for !$entity->isDefaultRevision() is to use $entity->isDefaultRevision(FALSE)

That fixes it. Tested with Core 10.3.2

janpongos’s picture

ok so this is a dangerous update - MR 9272
I misinterpreted the method as if it's a query.
Please ignore that MR.