Problem/Motivation
DisplayBuildableInterface extends ContainerFactoryPluginInterface, and DisplayBuildablePluginBase::create() already injects entity_type.manager, current_user and module_handler. None of the four buildable plugins used them. They reached for the container statically, and resolved their entity in the constructor with EntityViewDisplay::load(), PageLayoutEntity::load() or View::load().
The cause is ordering: create() does new static(...) first and assigns the services after, so nothing the constructor needs can be injected. Each constructor carried a "No dependency injection in plugin constructors" comment, which describes the problem rather than justifying it.
This matters beyond tidiness: a contributed module writing its own integration will open EntityView and copy it. It is the reference implementation whether we call it that or not.
Proposed resolution
- Constructors resolve nothing. They only normalize
::$configuration, keeping the identifier the plugin was built with and stashing an object the caller passed in. - Each plugin resolves lazily through
$this->entityTypeManager:EntityView::getDisplay(),EntityViewOverride::getDisplay()and::getField(),PageLayout::getEntity(),ViewDisplay::getExtender(). - Extra services come from an overridden
create()that callsparent::create()and assigns on the result:sampleEntityGenerator,dataConverter,time,displayBuildableManager,currentPath,pluginCacheClearer. - Removed the seven lazy
\Drupal::service()accessors inEntityViewOverrideandPageLayout, plusEntityViewOverride::entityTypeManager(), which shadowed the injected property and had no callers. - The base
create()docblock now documents the extension pattern and why a constructor must not resolve its display.
| Plugin | \Drupal:: before |
After |
|---|---|---|
EntityViewOverride |
10 | 5 |
EntityView |
5 | 3 |
PageLayout |
5 | 2 |
ViewDisplay |
2 | 2 |
Every remaining call sits in a static method the interface mandates: collectInstances(), checkAccess(), getUrlFromInstanceId() and their static helpers. Those have no $this to inject into and cannot be fixed without changing the contract. Nothing in instance context reaches for the container.
Remaining tasks
- Review.
User interface changes
None.
API changes
PageLayout::$entity was public and is now protected, read through ::getEntity(). It had no callers outside the plugin. Everything else is protected or private internals. Plugin IDs, stored configuration keys, DisplayBuildableInterface and DisplayBuildablePluginBase::create() are unchanged.
Data model changes
None.
Issue fork display_builder-3616225
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 #2
mogtofu33 commentedComment #3
mogtofu33 commentedComment #5
mogtofu33 commentedComment #6
mogtofu33 commentedIt is pretty safe with tests, so ok to auto merge.
Comment #9
pdureau commentedIndeed, discussed in weekly, it was ok 👍