This issue was the result of a 'hard problems' discussion at DrupalCon Amsterdam, seefor the meta issue.
HtmlFragment was added to represent an HTML string and associated metadata, however, it did not take into account Drupal's render caching in the Render API, which was introduced in Drupal 7.
#post_render_cache (added in Drupal 8) implies that it must always be possible to cache HTML strings with placeholders, with the
#post_render_cache callbacks run on the string retrieved from cache, before it's returned to the user. This is true except for the single exception of the page cache, which currently deals only with raw HTML strings.
HtmlFragment contains a fully rendered HTML string, as returned from
This makes it the final representation of that string, after all caching/
#post_render_cache callbacks have run.
HtmlFragment as the final representation of an Html string + attached stuff was fine as an interface. However, being the final representation of a string, means that it's not possible to have an
HtmlFragment inside any lower level than the entire page response.
HtmlFragment be the return value for blocks. Blocks however, can be nested — the most obvious (but not only) example is mini panels. Having blocks return an
HtmlFragment would make it impossible to implement caching for an entire mini-panel (unless we added
#post_render_cache support to
HtmlFragment which would require duplicating further functionality from render arrays).
HtmlPage, as the final representation of a page was also fine in theory. However, in practice this creates confusion with , where the page object is passed into
Page templates are not fundamentally different from other templates in Drupal, so
template_preprocess_page() should have the same ability, and restrictions, that other preprocess functions do (for 8.x this should ideally mean adding dynamic classes/attributes, and assets that specifically relate to the template, and not much else) — the reality isn't there yet, but it's slowly getting closer.
HtmlFragment to represent the concept of a page region (as opposed to a block), would have provided a structure object to interact with at that level, with the benefits of an interface that render arrays don't have. However, there are only a handful of modules that ever deal with regions, so the DX benefits would not trickle down in any way to most contributed and custom modules.
Additionally, there was no goal to replace render arrays with
HtmlFragments, so even with a clearer DX (but, again, only for the very narrow case of building pages, not other templates), they represent a duplication and additional layer of abstraction on top of render arrays — adding to the complexity of the overall system rather than reducing it.
Additionally, arguments such as the page array being 'huge', while true for Drupal 7, are no longer the case for Drupal 8. Render arrays returned from blocks are very small, structured pseudo-objects with
#pre_render equivalent to a
build() method (which is in fact how blocks are implemented). This is a result of the move to render caching of both entity and block views, which represent most HTML on the page.
After talking through all of these various issues, the conclusion was to remove the concept.
To improve the DX of render arrays at all levels, we will continue to add interfaces such as:
This takes the functionality of providing
#keys for render arrays, which is often the most confusing part, and puts it into a nice interface with methods that can later be used to extract a valid render array.
The advantage of this bottom-up approach is that the system as a whole is only dealing with a single class of object (the render array), not a combination of both
HtmlFragment and render arrays at different levels.
The patch in #78 was profiled, see #81.
Get child issues committed, to make the patch on this issue more reviewable:
- Build consensus for this patch
User interface changes
_drupal_add_feed(). (Split off to .)
drupal_get_feeds(). (Split off to .)
drupal_render()'s signature. [DRUPAL_RENDER]
\Drupal\Core\Ajax\AjaxResponseRenderer, is now part of
\Drupal\Core\Controller\AjaxController, shared functionality lives in
\Drupal\Core\Block\MainContentBlockPluginInterface(for blocks rendering the main content). [PAGE-VARIANTS]
\Drupal\Core\Controller\MainContentControllerInterface(interface and base class, respectively, for
\Drupal\Core\Display\Annotation\PageDisplayVariant: annotation for the next item. [PAGE-VARIANTS]
\Drupal\Core\Display\PageVariantInterface, subclass of
\Drupal\Core\Display\VariantInterface, specifically for "page variants" (it adds a
\Drupal\Core\Page\FeedLinkElement(Split off to .)
\Drupal\Core\Render\BareHtmlPageRenderer, successor of
\Drupal\Core\Render\BareHtmlPageRendererInterface, see previous item. [HTML-RENDERING]
\Drupal\block\Plugin\DisplayVariant\FullPageVariant: renamed to
BlockPageVariantand now implements
\Drupal\system\Event\SystemEvents: to add the "page display variant selection" event. [PAGE-VARIANTS]
\Drupal\block\Plugin\DisplayVariant\SimplePageVariant: to encapsulate the default behavior of
drupal_prepare_page(), i.e. to be used when Block module is not installed, and
BlockPageVariantcannot be used. [PAGE-VARIANTS] + [MAIN-CONTENT-CONTROLLERS]
|#96||untangle_page_rendering-2352155-96_review-do-not-test.patch||141.36 KB||Wim Leers|
|#96||untangle_page_rendering-2352155-96.patch||192.28 KB||Wim Leers|
PASSED: [[SimpleTest]]: [PHP 5.4 MySQL] 81,195 pass(es). View
|#67||d8_render_pipeline.png||321.45 KB||Wim Leers|
|#67||d8_render_pipeline.graffle.zip||12.46 KB||Wim Leers|
|#67||d8_render_pipeline.svg_.txt||81.8 KB||Wim Leers|