Fixed
Project:
Display Builder
Version:
1.0.x-dev
Component:
Code
Priority:
Major
Category:
Task
Assigned:
Unassigned
Reporter:
Created:
1 Oct 2026 at 09:05 UTC
Updated:
4 Nov 2026 at 14:09 UTC
Jump to comment: Most recent
Child 3 of #3624396: Plan: Harden InstanceInterface, IslandInterface and DisplayBuildableInterface as public API before RC1, split in two: this one is InstanceInterface, child 3b the other interfaces. Needs #3627028: InstanceInterface: one failure style for the tree methods.
setNewPresent(), a history term with two boolean flags, which returns nothing. Seven callers outside the instance, the submodules among them, use it, and each one calls save() right after, though it already saved.isPublishedPresent() compares hashes that count node IDs, so it can answer "not published" where hasUnpublishedChanges() finds no change.getUniqId() and getContentHash() are static methods on the interface. The hash is written in three places, and the storage recomputes it on every save anyway.ensureCurrentTranslation() saves and may return another object, and says neither. Its comment names one translatable field, there are three.Instance::$currentUser is public, for one test.setSources(array $sources, string|\Stringable $log_message = ''): static on InstanceInterface, normalizing and skipping an identical tree as setNewPresent() does with its default flags. setNewPresent() is removed from HistoryInterface, and its seven callers move to setSources(). The mutators share a private method that holds the two flags.save() after a mutator or setSources(), in the controllers and the submodules. Tests: one change, one revision.isPublishedPresent() returns !$this->hasUnpublishedChanges(). Test: an instance whose published sources lack the draft's node IDs is published present.getUniqId() and getContentHash() leave the interface: they are static methods on a contract. They stay on the Instance class, internal, for the module's own callers. The storage alone writes the hash: postCreate() and createDisplayBuilderInstance() stop. getHash() and getPublishedHash() say what each hash counts and when to use it. InstancesPanel::hasUnpublishedChanges() gets a @see to the instance method, and a test for a reference whose instance is gone.ensureCurrentTranslation(): static, with an @return, and a docblock saying it saves and may return another object. Its comment names the translatable fields. Which language it should follow is its own issue, under #3615902: Plan: Translation support.Instance::$currentUser private. InstanceHistoryTest switches users with the test trait's setCurrentUser().display_builder_ai and the dev tools to setSources().None.
InstanceInterface::setSources(). HistoryInterface::setNewPresent() removed.getUniqId() and getContentHash() removed from InstanceInterface.ensureCurrentTranslation() returns static.Instance::$currentUser is private.isPublishedPresent() ignores node IDs.None.
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 #2
mogtofu33 commentedComment #3
pdureau commentedIf it is a RC blocker, it must be moved to next beta.
Comment #4
pdureau commentedComment #5
pdureau commentedProposal analysis
So
InstanceInterface::setSources()is becoming simpler replacement ofHistoryInterface::setNewPresent()without thecheck_hashandindexflags, and performing the entity save?It is great to simplify the
HistoryInterface, removing the last leftover of the old "steps" system.⚠️ However
HistoryInterface::setNewPresent()is already never called using thecheck_hashandindexflags outside ofInstanceentity internal calls and the relatedInstanceHistoryTest.So, before renaming/moving any public method, let's try to simply move
::setNewPresent()logic into a private::saveTree()method and remove the flags from the public API.⚠️ Also, it is a bit "disturbing" to make the entity save itself, especially in a "set field value" method. In Drupal, methods that change an entity normally just change it in memory and return $this; the caller decides when to call save(). So, this proposal go against the convention. I know we already do that 2 or 3 times in thge codebase, but is this the opportunity to remove them instead of consolidating them?
We can also remove comment(s) about https://redux.js.org/usage/implementing-undo-history because it is not the case anymore, since we moved to a revisionable content entity.
👌 OK. But do we still need keep both methods in this case ? I would advocate removing one.
👌 OK for removing
InstanceInterface::getUniqId()andInstanceInterface::getContentHash(), and leveragingEntityInterface::postCreate()and/or the Entity Storage. It looks more Drupally.⚠️ However,
::getContentHash()is also called from buildable plugins andInstanceRowStatus, so how can we do that without extending the storage interface? ASourcesHashTrait? It may also be the opportunity to removeInstanceRowStatuswhich is a curiosity only used by InstancesPanel.👌No opinion about that.
It would be nice to remove
InstanceInterface::ensureCurrentTranslation()all together, it looks a bit odd and I am surprised there is no existing mechanism in Drupal Core doing this kind of job. But I guess it is not possible.It is already the case as far as I know.
👌 Just a docblock change? OK.
Work
I will give a try.
Expected breaking changes:
HistoryInterface::setNewPresent()(or replace it byInstanceInterface::setSources())InstanceInterface::getUniqId()InstanceInterface::getContentHash()InstanceInterface:isPublishedPresent()or:hasUnpublishedChanges()?Instance::currentUserDisplayBuildableInterface::ensureCurrentTranslation()phpunit changes must be limited to:
Playwright tests must not be changed, not at all.
Comment #7
pdureau commentedFirst commit pushed.
Done
Done with
SourcesHashTraitused in:Instance::hasUnpublishedChanges()entity. Is it something to move toInstanceStorage?InstanceStorage, as expectedInstanceRowStatus, finally keptDisplayBuildablePluginBase, as expectedNext steps
There are 2 decisions to take:
HistoryInterface::setNewPresent()withInstanceInterface::setSources(), knowing it will add around+100/-100changes in an already heavy MR.I would advise to remove
::hasUnpublishedChanges(), because less usage in the codebase:Comment #12
pdureau commentedNew MR: https://git.drupalcode.org/project/display_builder/-/merge_requests/538
I played it safe, picking the last disruptive naming change options (
::setNewPresent()instead of::setSource(),::isPublishedPresent()instead of::hasUnpublishedChanges()) while targeting the goals of the ticket (as far as I understood them):Interface changes:
HistoryInterface::setNewPresent()InstanceInterface::getUniqId()InstanceInterface::getContentHash():hasUnpublishedChanges()Instance::currentUserDisplayBuildableInterface::ensureCurrentTranslation()Comment #13
csakiistvanComment #14
csakiistvan✅ Tested and works — rebased MR !538 locally on 1.0.x (no conflicts), applied it, rebuilt caches and re-ran the publication check on an instance whose draft and published sources differ only in node IDs:
isPublishedPresent()went fromFALSEtoTRUEand the status reads "Published". The contradiction between the two publication checks is gone.Code review - No caller outside the instance still calls
save()right aftersetNewPresent().Comment #15
pdureau commentedThanks István.
Jean, what do you think of the stance I took in #12 ("I played it safe, picking the last disruptive naming change options")?
Comment #16
mogtofu33 commentedI agree with changes from #3627284-12: InstanceInterface: setSources() instead of setNewPresent(), one save per change, the hashes, rebased on 1.0.x, which removed two more save() calls in the hooks that replaced EntityViewDisplayTrait.
Change record for next release in my list.
Thanks for the work.