Follow-up from #2218117: Bring back metatag support for the HtmlPage object.
HtmlControllerBase contains a fair bit of code for turning a render array into an HtmlFragment object. That's a worthwhile enough task that it should be its own service.
This issue is to just create a RenderHtmlFragmentConverter service and refactor HtmlControllerBase et al as needed. That includes:
1) The contents of the render array should be stringified and put into the fragment.
2) Any #attached data on the render array should be extracted and put into the fragment as the appropriate head tags. (LinkElement, MetaElement, FeedLinkElement, etc.)
3) A #title on the render array should be set as the title of the fragment, if any.
4) Any cache instructions on the render array should be transferred to the fragment.
Comment | File | Size | Author |
---|---|---|---|
#52 | render_fragment-2256365-51.patch | 9.92 KB | Wim Leers |
Comments
Comment #1
dawehnerThis services converts a render array to a html fragment, so what about simply go with RenderHtmlFragmentConverter?
Comment #2
Crell CreditAttribution: Crell commentedAbsent a better suggestion I have no problem with that name.
Comment #3
Crell CreditAttribution: Crell commentedComment #4
Crell CreditAttribution: Crell commentedFirst stab at this. Right now I'm not transferring anything but feeds and cache because that's all that's being transferred by HtmlControllerBase now. If someone can point out what the structure of #attached is documented so I even know where to FIND anything else to transfer please do so. :-)
Comment #6
dawehnerNice a patch from crell and just 1000 problems.
Render is such a generic term, that I did not know what this class is all about, maybe go with RenderToHtmlFragmentConverter?
Comment #7
Crell CreditAttribution: Crell commentedWell the other objects in the pipeline are called HtmlFragmentRenderer and HtmlPageRenderer. The consistent name would be... RenderHtmlRenderer.
Comment #8
dawehnerOkay, this seem to make sense, though you still need to fix the actual patch.
Comment #9
Crell CreditAttribution: Crell commentedThis should take care of most of the fails, I think.
Also, I really like PHPStorm's refactor command... Damn it's complete.
Comment #11
Crell CreditAttribution: Crell commentedThat's much better. This should cover the last test, and splits out an interface, too.
Comment #13
Crell CreditAttribution: Crell commentedAnd by that patch, I mean this patch.
Comment #15
Crell CreditAttribution: Crell commentedRemind me to stop coding after 10 pm. I'm getting old.
Comment #16
dawehnerThe patch is indeed not complex on the architectual level, just extract logic into a proper place.
Though at least it should be possible to take the time and actually document it a little bit. Maybe someone here at the sprint will
find the time to work on it.
Let's just always document the interface and not use the class which is not needed.
At least one line could be really done here.
Comment #17
effulgentsia CreditAttribution: effulgentsia commentedCrell asked me to take a look at what's being done in RenderHtmlRenderer::render() to see if it's complete, so here's some feedback about that, but since this patch just moves code from the existing base class, these are all pre-existing conditions in HEAD, so addressing them can be done in followups, IMO.
I don't think we need the 'main' wrapper any more. I think that's a legacy from when this was being returned into a $page array, and we wanted to put the main content into a region named 'main', but that's no longer the case, as we're now rendering this into a string directly and letting the caller decide where to put that string.
If we're dealing with cache tags, we probably also need to deal with #post_render_cache. There's separate work related to that in #2273277: Figure out a solution for the problematic interaction between the render system and the theme system when using #pre_render and possibly other issues. Wim Leers is our current expert on all matters related to #post_render_cache :)
This is running after we've already called drupal_render() earlier in the function, so we shouldn't need to collect. Rather, the info should already be fully collected and merged in $elements['#attached'].
In addition to drupal_add_feed(), we also have drupal_add_http_header(), drupal_add_html_head(), and drupal_add_html_head_link(). They probably all need some refactoring.
Comment #18
Crell CreditAttribution: Crell commentedAttached patch addresses #16 and #17 to the extent possible, which includes a few @todos.
I think this is enough for now; there will be more issues to come but this gets us the first step of refactoring.
Comment #19
Crell CreditAttribution: Crell commentedComment #22
Crell CreditAttribution: Crell commentedOopsies. Rerolled. Interdiff is only after the reroll.
Comment #23
dawehner:(
CS talks about using full namespace and even one liner docs for silly service properties. I know you know that ...
+1 for using short array syntax
CS says empty line here
Comment #24
Crell CreditAttribution: Crell commentedI don't have that nice button you have to make PHPStorm auto-generate the long versions. :-(
Comment #25
dawehnerSeem fine (even if you ignored the one line description)
Comment #26
Wim LeersHow does this relate to #2327277: [Meta] Page rendering meta discussion? Will it help solve that?
Comment #27
tim.plunkettAlso can you fix this */
I think he meant here?
Comment #28
Wim LeersStupid docs typo :)
This makes me think this is an alternative to
drupal_render()
. I think this would benefit from a better description.Missing description.
s/Generator/generator/
Why not typehint?
:)
What's "the return"?
Why also accept a string? This feels like a concession to accommodate the existing codebase. It also conflicts with the intent and documentation of the interface.
Shouldn't this be typehinted on the interface?
Also, there should be a newline before
@return
.Missing a newline.
Comment #29
Crell CreditAttribution: Crell commentedTo address points 5/8: Currently, a string is a nominally legal render array. Controllers can return "render array or string". Somewhere that needs to be up-converted to normalize them. Previously that was done in HtmlControllerBase. I retained that same logic when moving that code to this service. I am not defending the practice of a string being a nominally legal render array, just accounting for it. :-) Breaking that "feature" of render arrays would, IMO, be a separate issue as this one is trying to just be a "move code around" issue.
For point 2, I could actually see this service eventually subsuming/replacing drupal_render(). If nothing else I think it deprecates #2168111: Allow drupal_render() to pass up #attached to parents. How far we go down that path in D8 is a debatable point, but this could be seen as the start of "drupal_render as a service". I'm not sure how exactly we want to document that in particular.
The rest are all doc/formatting issues I will try to look into tonight unless someone wants to beat me to it.
Comment #30
Crell CreditAttribution: Crell commentedFollow-up to point 2: The interface goes into more detail. The class is simply the default/most common implementation of the interface.
Other noted doc issues addressed, including one or two others that PHPStom found for me.
Comment #31
dawehner:( Please let's use absolute full namespaces for now.
Let's also document interfaces.
This could be certainly used also to convert blocks into fragments, at least indirectly.
Comment #32
Crell CreditAttribution: Crell commented1, 2: Sigh
3: Yup, you betcha.
Comment #33
dawehnerSorry for being annoying.
Comment #34
Wim LeersIs it?
drupal_render()
doesn't support strings at all (you'll get a fatal if you try).The fact that controllers may return strings doesn't matter — that's unrelated to "converting a render array to a HTML Fragment", which is what this new service is all about (or at least, that's what its docs say).
If this (supporting strings *and* render arrays) is what you want, then the interface is named incorrectly. But I think what you really want is to have this new interface to be solely responsible for converting a render array (and hence also type hint on
array
) to aHTMLFragment
, and to then have the following code inHtmlControllerBase
:I'm sorry, but I find the current patch very confusing. I think it should be much more explicit. I think my proposal would make it much more explicit: make this new interface (and service) have a very specific purpose, which will make the interface docs actually truthful (rather than misleading lies) and have a very clear conceptual purpose:
, which would indeed be a very valuable component in the process of rendering blocks asHtmlFragment
s.You could then also make the interface and service name much clearer:
RenderHtmlRenderer
is… rather vague. I thinkRenderArrayFragmentConverter
or evenRenderArrayToFragment
orFragmentizer
(:P) would be clearer.This should be the interface.
This is still a very confusing, extremely abstract description.
s/Url/URL/
Missing newline.
Inappropriate capitalizations?
Comment #35
msonnabaum CreditAttribution: msonnabaum commentedThe basic idea here of extracting code into a new object sounds reasonable enough, but this patch is *really* confusing.
The language here shuts off my brain.
Is it rendering or converting? Shouldnt this just be RenderArrayToHtmlFragmentConverter? Or if its creating new HtmlFragment objects...HtmlFragmentFactory::createFromRenderArray?
Comment #36
Crell CreditAttribution: Crell commentedMark: This is part of the chain of "objects that map stuff to a higher level object": RenderHtmlRenderer, HtmlFragmentRenderer, HtmlPageRenderer. Every one of them has a render() method that takes an object at one level and returns the next level up: render array -> HtmlFragment -> HtmlPage -> Response.
See #6-#8 for how we ended up with the name. I agree "RenderArrayRenderer" sounds weird but that's what happens when we use a verb to describe an array.
Are you suggesting we scope creep this issue to renaming those other services (classes, interfaces, and service names)? If not, suggestions for better doc text welcome.
Wim: I'm skeptical about moving the string handling back to HtmlControllerBase, because longer term I want to also deprecate HtmlControllerBase entirely and call this service from a view listener instead. That moves us closer to what webchick suggested here: #2092647: Consolidate _form, _controller, _entity_form, etc. into just _controller?. But as long as controllers return strings we would then have to allow strings in the service to get upcasted there.
Comment #37
Wim LeersBut as long as you want the burden of dealing with strings to lie in a "render array to
HtmlFragment
" converter service, you will end up with confusing code.Again: strings are not valid render arrays.
So this in #29 is wrong: . But this part is true: — however, note that "controller return values" is not the same as "render arrays".
If you really want to go this way, then you'll want
ControllerReturnValueRendererInterface
. Which wouldn't make anybody happy either, I suspect.Is there a good reason any controller must still be able to return either a HTML string or a render array? If everything returned render arrays, this patch could be made a lot clearer.
Finally, I don't see how #36 answers #35's question of why we can't have a
HtmlFragment::createFromRenderArray()
static method? If you eventually want to move away from render arrays, then a static method that does the conversion makes a lot of sense to me.Comment #38
dawehnerI do agree, on a academical level, it is not strictly a render array but you know what I express if I talk of academia.
Well, we do support strings ATM. The issue is to extract that logic into a generic service, that is all.
I hope you don't want to just block crell's important work here, which he started a long long time ago.
Note, the talk you complain about is a 3 liner, nothing complex at all. You can easily remove that in a follow up if you wish.
Well a html fragment is just a stupid object, it should not know how to create itself, as this is really not a straightforward operation.
Comment #39
Wim LeersI'm not trying to block this at all, I'm just very concerned that this will make the way Drupal 8 renders pages will become even harder to understand (for an explanation as to why it's already hard: #2327277: [Meta] Page rendering meta discussion).
This patch introduces an abstraction but then uses it in only one place. That's strange.
Furthermore, it doesn't even remove
HtmlControllerBase::createHtmlFragment()
— it just removes most of that function's body.And then finally, all documentation indicates that it's about converting a render array to a
HtmlFragment
, but then it turns out it also deals with strings.Why is this okay:
and not this:
? There already are two
instanceof
checks on$page_content
. Why are those okay but this third (is_string()
) isn't? I proposed exactly this 5 comments ago. A future patch can still remove this if it makes sense then, but for now, that's the only way I see this patch making sense.The problem of this new abstraction/service only being used in a single place would still be true, but at least its responsibility would be *clear*. I'd be fine with that.
In other words: this patch is simply not good and clear enough yet.
Comment #40
dawehnerSee #2334029: Add js/css/libraries/head links to HTML fragments and pass it along to the page.
Comment #41
Crell CreditAttribution: Crell commentedComment #42
Crell CreditAttribution: Crell commentedA much bigger picture writeup of how this fits into the roadmap is now here: #2327277: [Meta] Page rendering meta discussion
Discussion about renaming the "renderers" should be had here: #2334207: Revisit naming of "renderers" in the page building pipeline
Given the roadmap listed I believe we will have to move the string handling back to RenderHtmlRenderer sooner or later. I've put it back in the wrapping controller for now, at least until that wrapping controller gets eliminated. Wim, if we end up needing to move the string handling back to RenderHtmlRenderer I reserve the right to say "I told you so".
Patch needed a reroll; interdiff is post-reroll. Let's see if it survived...
Comment #43
Wim LeersBetter, thank you. Getting closer.
That still leaves:
Point 1 is a trivial fix, alternatives have been suggested for point 2, and I suspect you have a good explanation/reasoning for point 3 but you haven't posted it yet AFAICT?
Comment #44
Wim Leers(Oh and I'm very, very sorry about the terribly slow review this time — I was deep in another set of patches :()
Comment #45
Crell CreditAttribution: Crell commentedPoint 1: Yeah, we just need to add a link. This will need to be rerolled after #2323759: Modularize kernel exception handling anyway so we can address it there.
Point 2: The naming concerns have their own issue: #2334207: Revisit naming of "renderers" in the page building pipeline
Point 3: "Abstraction used in only one place is weird". Um, no it's not. Not even a little. :-) It's a single operation segmented to its own service. That's just good development practice, and Drupal does it all the time. (See also, HtmlFragmentRendererInterface and HtmlPageRendererInterface and... something like a third of the services in core.) It's also been noted above that there are other places we could start using it (eg, Block rendering if it goes that direction). "Create it, use it once, commit, use it elsewhere" is the recommended pattern for many refactors in core, so I am honestly confused why you're even mentioning something that's a common practice in Drupal and elsewhere.
Comment #46
dawehner@Wim Leers @Crell
Can't we just open an issue to figure out which _content controller still return a string? This feels like a really nitpicky discussion though, tbh.
We certainly will have more places where we could use it: blocks to fragment conversion.
Given that @Crell do you want to give this now a new reroll after the exception modernization?
Comment #47
Wim LeersRenderHtmlRenderer
.I won't hold this up further because I want all of us to make progress, but I'm still not really convinced. As long as this is an intermediate step towards something better, I'm fine with it.
In that light, here's another review:
Let's make the service name consistent with the class name.
… I agree that "converter" is a better name. But that's for that follow-up issue. Let's be consistent.
This description definitely needs to be better though.
Two types of capitalization.
Extraneous blank line.
Bizarre capitalization.
HtmlFragmentInterface
Comment #48
dawehnerThank you for your review, wim!
well, it is not only about the overrideability, this is also about dependencies etc. For example that service depends on drupal_render() somehow.
Services also ensures that your code can be somehow reused.j
Oh absolute, just to make this clear, this issue is just one of many steps, with every step an incremental small improvement, with though obviously a lot of still opened problems.
Crell is okay with me taking over that in order to ensure that something moves forward until Drupalcon.
Sure, no big deal.
Oh yeah, what explaining which parts of the render this one takes into account? Better suggestions are highly recommended.
After talking with Crell on IRC we wanted to ensure that we have a immutable version (the interface), see #2339475: Introduce a mutable HTML fragment interface for
adding a mutable interface as well.
Comment #49
Wim LeersAlright, let's get a very fast reroll/review cycle going :) Let's do this!
Just a small few remaining bits… after this it's RTBC.
Still "converter".
s/html/HTML/
Thank you, this is *much* better!
This change is wrong; it's the comment that should've changed from
HtmlFragment object
toHtmlFragmentInterface object
.(I really wish we didn't have to write a description when in cases like these; just the
@return TYPE
is more than enough!)Comment #50
Crell CreditAttribution: Crell commentedFor point 4: The object that comes back needs to be a mutable fragment object so that setTitle() et al work. As dawehner noted HtmlFragmentInterface has read-only methods. HtmlFragment is the only place setTitle() exists right now, so that's what we have to return. We should finish off the interfaces with a mutable interface in #2339475: Introduce a mutable HTML fragment interface, at which point we can change the @return here to specify the mutable interface.
Comment #51
dawehnerOh I thought I got all.
Yeah this change was 100% intended.
@Wim Leers
I tend to prefer using phpstorm for actual in depth reviews as you spot things like that.
Comment #52
Wim LeersAha! That connection wasn't clear to me. That leaves just points 1–2, i.e. only two of the tiniest nitpicks. I think it's okay for me to fix those and still RTBC this patch.
Also linked to #2339475: Introduce a mutable HTML fragment interface regarding point 4.
EDIT: cross-posted with dawehner… but I'm still posting this patch, because it includes that link for #49.4/#50 to ensure we fix it later on. Hope that's okay.
Comment #53
Crell CreditAttribution: Crell commented+1 Crell stamp of approval on #52. Thanks, dawehner and Wim!
Comment #54
dawehnerSee you in #2334029: Add js/css/libraries/head links to HTML fragments and pass it along to the page.
Comment #55
webchickMy spider sense tells me that catch will probably want to look at this one before it goes in. If I'm wrong about that, feel free to unassign.
Comment #56
andypostany reason for xss here? the logic is strange:
1) render page
2) try to set title for rendered fragment?
Comment #57
Crell CreditAttribution: Crell commentedThe usual catch triggers are caching and changes to the render system. This does neither. It just moves code around. Really. Anyone can commit this. :-)
Comment #58
catchI'm not happy with the overall situation regarding HtmlFragment/HtmlPage, and #2327277: [Meta] Page rendering meta discussion has a lot of gaps/hand waving in terms of what the path forward will be. However this patch doesn't make any of that worse, so committed/pushed to 8.0.x, thanks!