Problem/Motivation
In the UI Patterns ecosystem, including Display builder, there is a recurrent needs of rendering any single source, or a list of source. Because the source tree has not always a SourceComponent as a single root node.
So, the community do weird tricks like:
foreach ($sources as $source_data) {
$fake_build = $this->componentElementBuilder->buildSource($fake_build, 'content', [], $source_data, $contexts);
}
$build = $fake_build['#slots']['content'] ?? [];
$build['#cache'] = $fake_build['#cache'] ?? [];
// The render array is built based on decisions made by SourceStorage
// plugins, and therefore it needs to depend on the accumulated
// cacheability of those decisions.
$cacheability->applyTo($build);
where a single public function buildSources(array $sources, array $contexts = []): array may be enough.
Proposed resolutions
- Add a
SourceTreeRendererInterfacewith a::buildSources()method - Implement it:
- in
ComponentElementBuilder: to keep things simple.SourceTreeRendererInterfacenamespace can become a service alias.ComponentElementBuilder::buildSource()can be deprecated and become protected or private later. - OR in a brand new service, using
SourceTreeRendererInterfacenamespace as service ID. This will allow a cleaner implementation and to also addSourceTreeRendererInterface::buildSource()later.ComponentElementBuildercould become @internal or deprecarde.
- in
- Use
SourceTreeRendererInterface::buildSources()everywhere a "fake component' is used:- modules/ui_patterns_field/src/Plugin/Field/FieldFormatter/SourceFormatter.php
- modules/ui_patterns_field_formatters/src/Plugin/Field/FieldFormatter/SourceFormatter.php
- modules/custom/ui_patterns/src/Plugin/UiPatterns/Source/LayoutSource.php
The implementation in ComponentElementBuilder can be naive and keep the "fake component" mechanism (at leas, it is centralized in a single place now):
/**
* {@inheritdoc}
*/
public function buildSources(array $sources, array $contexts = []): array {
$cacheability = new CacheableMetadata();
$fake_build = [];
foreach ($sources as $source_data) {
$fake_build = $this->buildSource($fake_build, 'content', [], $source_data, $contexts);
}
$build = $fake_build['#slots']['content'] ?? [];
$build['#cache'] = $fake_build['#cache'] ?? [];
// The render array is built based on decisions made by SourceStorage
// plugins, and therefore it needs to depend on the accumulated
// cacheability of those decisions.
$cacheability->applyTo($build);
return $build;
}
Or being more ambitious, like the implementation in a new service would be.
Remaining tasks
Follow-up: in Display Builder, use SourceTreeRendererInterface::buildSources() in:
- EntityViewDisplayTrait
- DisplayBuilderPageVariant
- PreprocessViewsView
- BuilderPanel
- PreviewPanel
- Maybe more...
Issue fork ui_patterns-3576385
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
pdureau commentedComment #3
just_like_good_vibesvery nice idea, thanks !
maybe we could discuss the naming. let's do this during the next weekly
Comment #5
pdureau commentedWork started and first commit pushed in MR.
Tested with #3576386: Simplify single source rendering in Display Builder, it seems to work OK.
However, the first commit is very naive and straightforward, and
::buildSources():::buildSource()ComponentElementBuilder: the source tree rendering is bigger than SDC components and most of the logic may be a moved trait in the ComponentSourceComponentElementBuilderas deprecated::buildSource(): SourceFormatter and UIPatternsSourceFieldPropertySource