Postponed (maintainer needs more info)
Project:
Display Builder
Version:
1.0.x-dev
Component:
Code
Priority:
Normal
Category:
Task
Assigned:
Issue tags:
Reporter:
Created:
5 Sep 2026 at 15:34 UTC
Updated:
19 Sep 2026 at 11:18 UTC
Jump to comment: Most recent
Comments
Comment #2
pdureau commentedComment #4
pdureau commentedMy first AI-accelerated MR :)
What do you think about isolating the logic there? It make sense in my humble opinion, but i am not familiar with
PluginWithFormsInterfaceso I am not 100% sure.We are not using
PluginFormInterface::submitConfigurationForm()but this is opening opportunities we can use in #3594228: Remove explicit references to Layout Builder for example.Comment #5
mogtofu33 commentedComment #6
mogtofu33 commentedReviewed MR !381 with contrib in mind:
DisplayBuildableInterfacenow extendsPluginWithFormsInterface, so the form becomes an extension point other modules will rely on. Worth getting right before beta8 freezes it.getFormClass()ignores its argument. It returnsDisplayBuildableForm::classfor any operation and never reads the plugin definition, so a contrib buildable has to override the method to change the form. The docblock saysPluginWithFormsTraitcannot work because definitions are attribute objects, but they are arrays:DisplayBuildableextendsAttributeBase, whoseget()returns an array, andDisplayBuildablePluginBase::label()already reads$definition['label']. Adding aformsproperty to#[DisplayBuildable], withDisplayBuildableFormas the default, lets the core trait do the job, and letshook_display_buildable_info_alter()swap the form without a subclass.createInstance($this, '')works, but a named operation is whatformskeys on and what a contrib module would target. The class docblock example already usesDisplayBuildableInterface::INSTANCE_FORM, which does not exist.assert()limits the swap to subclasses.buildInstanceForm()asserts$form_object instanceof DisplayBuildableForm, then callssetMandatory(),setTitle(),setLink(). Any form class that does not extendDisplayBuildableFormfails there, or on the setter call when assertions are off.buildInstanceForm(bool $mandatory, ?TranslatableMarkup $title, bool $link)keeps its signature, and the three flags also become three setters on the concrete form, documented in its class docblock as the way to use it.EntityViewDisplayFormTraitalready does: it repeats the factory call through\Drupal::service(), theassert()and the setters, where$buildable->buildInstanceForm(FALSE, $title, FALSE)gives the same result. #3624396: Plan: Harden InstanceInterface, IslandInterface and DisplayBuildableInterface as public API before RC1 replaces the flags with a documented$optionsarray across the public interfaces; fine to leave that part to it, as long as this MR does not make the setters the contract.new FormState(),validateConfigurationForm()never runs, is empty, and the host form readsswap the form without a subclass.createInstance($this, '')works, but a named operation is whatformskeys on and what a contrib module would target. The class docblock example already usesDisplayBuildableInterface::INSTANCE_FORM, which does not exist.assert()limits the swap touildInstanceForm() asserts$form_objectinstanceof DisplayBuildableForm, then callssetMandatory(),setTitle(),setLink(). Any form classthat does not extend
DisplayBuildableFormfcall when assertions are off.keeps its signature, and the setters on the concrete form, documented in its classdocblock as the way to use it.
EntityViewDisplayFormTraitalready does: it repeats the factory call through\Drupal::service(), theassert()and the setters, wheregives the same result. #3624396: Plan: Harden InstanceInterface, IslandInterface and DisplayBuildableInterface as public API before RC1 replaces
$optionsarray across the public interfaces;fine to leave that part to it, as long as this MR does not make the setters the contract.new FormState(),validateConfigurationForm()never runs,submitConfigurationForm()is empty, and the host form readsprofile,override_statusand t state. A replacement form can change the markup, but must keep those element names: they are the real contract, not the form class.isAllowed()on the override buildable, whosegetProfile()returns theoverride profile. Arguably more correct, but a user alloe other now sees the opposite of before. Intended?
DisplayBuildablePluginTestonly callsbuildInstanceForm()with defaults. Theoverride sub-form,
getOverrideField(), and point 6 are uncovered.BC, fine in beta but worth a release note:
buildSek(),buildDisabledSelect()andisCurrentUserAllowedToAdministrate()leaveDisplayBuildablePluginBase,buildOverridesForm()and its helpers leaveEntityViewDisplayFormTrait, andDisplayBuildableOverrideInterfacegains two methods.Minor:
DisplayBuildableFormis neitherfinalnor@internal. If contrib is meant to extend it, then it is API and should be documented as such. If not, mark it.Two ways to get this in:
buildInstanceForm(). Point 5 still limits it to the build.Plu build the form in <code>buildInstanceForm()withnew DisplayBuildableForm($this->moduleHandler, $this->currentUser)thensetPlugin($this), have the trait callbuildInstanceForm(), and markDisplaynaland@internal. Leave the extension point to #3624396: Plan: Harden InstanceInterface, IslandInterface and DisplayBuildableInterface as public API before RC1. Smallest change, keeps the extraction.Given point 5, I would take the second.
Comment #7
pdureau commentedHi Jean,
I see there are many parameters to consider here.
If it is complicated, we can simply do nothing here. This proposal was only to avoid adding
<violation rule="PHPMD\Rule\Design\WeightedMethodCount" file="/builds/project/display_builder/src/DisplayBuildablePluginBase.php"/>in
.phpmd.baseline.xml, which was done in #3555110: Symmetric translationWe can totally live with that and close this ticket if you are OK.