Phase 2: Response controllers

Events happening in the community are now at Drupal community events on www.drupal.org.
You are viewing a wiki page. You are welcome to join the group and then edit it. Be bold!

After discussing with Dries the concept of Display controllers, he indicated that they really should be a more generic "response controller". That makes sense, and also, based on discussion in this thread, ties into the ability to specify out-of-band responses (HTTP headers, CSS and JS files, etc.) in a clean fashion. I have therefore expanded this phase to be more generic and offer additional functionality. There's still lots of discussion to have here, though.

This phase is an evolution of the delivery callback present in Drupal 7, providing a more robust set of options and operating far earlier in the page request process. It involves splitting the concepts currently handled by hook_menu() and menu_execute_active_handler() into separate, pluggable Dispatcher and Response objects. This means that we can completely swap out hook_menu for something else if appropriate.

The Panels, Services, and Context contrib modules should serve as example models for this phase.

The general mechanism is as follows:

  1. Drupal builds the context object as above.
  2. The context object is passed to a Dispatcher routine that, based on the context, selects the appropriate Response object and configuration for the Display Controller. The Dispatcher may leverage a variety of information made available by the context object as necessary, including GET parameters, URL fragments, HTTP headers, and the ReST standard.
  3. Drupal will call the appropriate method on the Response object to generate a string, which will be returned to the requestor. This may be a full HTML page, HTML fragment, JSON object, SOAP response, ReST response, or any other response type. The Display Controller may also specify additional HTTP headers to be included in the response via another appropriate method.

Each Response implementation is responsible for generating the appropriate response based on the supplied context and configuration.

Initially we will need a Response object that returns a complete HTML page based on hook_menu information. It is essentially the current system replicated in an object. The Response object will also pass itself and the context object to sub-components to allow them to add additional side-band data (CSS and JS files, HTTP headers, etc.) in a clean and mockable fashion.

As a side effect of the dispatcher, the bulk of the services module becomes supported directly in core. Use of ReSTful mechanisms here is recommended.

Core should support the following additional display controllers that may be developed in parallel once the system is in place:

  • Form submission handling based on POST data.
  • JSON object response. (Necessary for auto-complete functionality.)
  • RSS/ATOM

The exact interaction and separation between the Dispatcher and Response object requires further investigation.

Providing clean support for tools like AtomPub, PubSubhubbub, CSV output, and so on becomes an exercise for contrib, or for Tim O'Reilly.

Discussion points

  • Where are the lines between Dispatcher, Display Controller, and Response object?
  • Are the latter two really the same thing? How does the Display Controller handle swappable layouts (the Blocks TNG phase)?

Comments

Note. Braindump below. Some

dmitrig01's picture

Note. Braindump below. Some thoughts might not make much sense and it's 11PM and I'm tired (teenagers need sleep more than anyone).

"1. Drupal builds the context object as above."
Isn't the whole point to lazy-load everything - meaning we *don't* build the context object?

This is where it gets sticky. We need to load as little information as possible. But if we follow the current block visibility pattern instead of a more page manager-esque pattern, if you have 50 blocks which each need a different part of the context you're going to end up having to load the entire context. Not good.

That's why the page manager solution is good - to recap: the page manager (at least currently) starts with a path and says "ok, there are five different pages we could potentially show at this path (the current "page" would be a given unique set of active handler + any blocks). I need foo, bar, and baz to be able to determine exactly which page to show". That way we only end up loading foo, bar, and baz instead of the whole context.

Number 3 looks extremely interesting. Does this pretty much mean that every page callback will become a class which has different methods such as ->returnHTML or ->returnJSON? That would be really, really awesome.

Potential problem: how do I use my module to add JSON callbacks to page callbacks provided by system.module. Simple answer: override the page callback and set it to a class which extends the system module. However, what if two modules want to do this?
Maybe we should go for the "fake objects" that are used in panels.

---

To address the discussion points:

Here lie the lines:
The Dispatcher will be swapped out once in a thousand years. It does need to be pluggable (install.php, update.php, authorize.php, etc), but almost no contrib should override it. Thus, it needs to be robust enough to not need to be overridden. It can't, for example, deal with RSS v. JSON. That needs to happen on some other level. If we go to above, maybe that's got to do something with the page manager-type-thing? This page returns JSON and this one RSS but they are all permutations of this other basic page. We determine, for example, based on HTTP headers whether it's JSON. That would need to happen at the level of the Response object.

Maybe the following happens (this ties into blocks TNG). Each block type object creates it's own response object (excellent for caching).

The Dispacher selects a set of display controllers. They fight it out to determine who gets to display. Each display controller can contain it's own set of response objects (blocks). The "main" page callback is essentially nothing - there's nothing "main" about it.

If the display controller needs JSON, it will ask it's response object(s) to return in a JSON format. If it needs RSS, same (typically an RSS display controller would contain only one response object).

---

Thus, if we go back to the page manager model, it seems like the Dispatcher is what's currently menu_execute_active_handler. The display controller is a "page" (not sure the specific page manager term). A response object is a block.

We probably need better names.

---

Phew, that was a lot. Hope that made sense. Probably didn't. I'm tired ;-).

[edit: fixed formatting]

Interesting thought

dmitrig01's picture

think about batch. the "response" isn't so much of a response. It does some processing and spits out "x out of y done".

Maybe it's not a response. Maybe it's some sort of other layer of abstraction instituted by batch api and for all of batch API there's just one response object. However, that gets confusing because ultimately the thing called "response" ends up calling the thing called "data processing" which seems just wrong. But maybe that's not such a bad thing, maybe we're just looking at (naming) it wrong.

Still a response

Crell's picture

Batch API is still a response object. It's just a very lightweight response. The return is just a string encoded in a JSON envelope. Arguably it could even be just the JSON response object (or possibly a sublcass thereof). Similarly, the "Form submission" response object is activated when there is a POST request (or something), loads up the right form object, does the submit routines, and returns nothing but an HTTP redirect. The entire rest of the page processing system is short circuited.

Mostly same page

Crell's picture

The context object is "build as above" meaning that it's created. Whatever else it does or doesn't do at that time should be irrelevant to the Dispatcher. Also, yes, lazy loading++. I'd probably make it a little bit dumber, and rather than the response object figuring out "I need info X, Y, and Z" it just waits for a Block to ask for X and Y and the context figures those out at that time. If no one ever asks for Z, the code to figure out Z never happens.

As far as the Dispatcher vs. Response, I'm still not entirely sure what goes on there. I suspect the end result will be a tiny amount of very simple code that takes us a year to really figure out. :-) You're right that there's a third moving part that corresponds to what in the past I've called the "envelope".

For instance, what's the difference between "load a node and display as a page", "load a node and display as an HTML fragment'", "load a node and return HTML fragment wrapped in JSON", and "load a node and return the raw node as JSON"? How much code do we share between those? I am not entirely sure yet.

My thinking is that the Response object knows the envelope/delivery method. So you would have a "full HTML page" Response object, and the Blocks within it know how to get $context->primaryNode() and display the relevant bits of it. You'd also have a JSON response object, which would consist of only a single Block that just does more or less:

<?php
return json_encode($this->context->primaryNode());
?>

And yes, one of the early debates I had with sdboyer about this setup is how much difference there really is between Blocks, Regions, and Display Controllers / Response objects. The more I chew on it, the more I'm convinced that BlockInterface is the top-most interface, RegionInterface extends from that and adds "can have a layout" functionality, and the Response object extends that and adds "can be called from the dispatcher" functionality. So yes, the response object is a block, for sufficiently abstract definitions of block. :-)

Tip of the iceberg

sdboyer's picture

First, +1 to Dries' note that these are really Response Controllers. At least, that's the outermost wrapper that we should be concerned with.

So I'm trying to think through this, and I think we're getting really muddy on what's the Response Controller, Display Controller, what the block does, etc... Basically, I think there's a very large number of permutations here, and some cases need tight coupling of an RC/DC/block concept, while it's completely irrelevant for others. Let me give some dummy examples to illustrate:

  1. A standard, browser-based request to a site's homepage
  2. A standard, browser-based request to node/%, on a site which has different display controllers for different node types
  3. An ESI-ish request made by an intermediary caching agent for the HTML representation of a specific block on the node page from the last item
  4. An AHAH request looking for HTML to replace into a form in order to reveal a dependent widget
  5. An AJAX request looking for a JSON object with which to play
  6. The same sort of AJAX request as in the last item, but with instructions in POST to package in some additional information
  7. An request seeking RSS/ATOM output
  8. A SOAP request that updates some server-side data
  9. A 403
  10. A batch API processing response
  11. A form submission

This variety of requests encompasses the major axes of variation that I think we need to be cognizant of. Lemme dig in on what differentiates these:

1 & 2 are a well-understood Page Manager phenomenon right now; they'll probably only be differentiated in that the Dispatcher will be able to arrive at the DC & its conf using only $_GET['q] context data for 1, whereas 2 will require dipping into node context data. They are both generating a full HTML response, both using the standard Dispatcher, both need the theme system, and (let's assume) aren't dipping into the form cache. They both have a set of blocks that they need to arrange into a layout, and need to take care of attaching 'out-of-band' information. The role of the display controller here, if we're assuming it's something like Panels, is basically to facilitate the ordered placing and rendering of the blocks/components into their appropriate locations within the determined layout by interfacing the data objects with the theme system in an appropriate way.

3 is actually very similar to 1&2, but has some key differences: it's only generating an HTML fragment, and it needs to be able to set special HTTP headers for the caching agent. What makes it quirky is that there's a strong argument to be made for letting it cohabit paths with the whole page of which the requested block is a part - the reason for this being that it's easy enough to have a specialized, ESI DC that takes the loaded conf for the entire page, but then chooses to render only the element specified in the request.

4 is similar to 3 in that it is also an HTML fragment generator, but otherwise departs in some respects: it needs to interact with the form cache, it's only generating an HTML fragment, it's not assembling multiple 'blocks' together into a coherent whole (in fact, I think blocks are architecturally irrelevant to this case), and in 99% of cases, I don't think there's out-of-band information to attach. It's perfectly easy and reasonable to put these at their own request paths, though, which is another crucial difference from 3, and makes these much easier to handle.

5 starts getting us into clearly different territory, because it's the first of the non-HTML responses. The goal of the response controller here is no longer string formatting, but data object assembly, so the theme system is probably not needed. Fortunately, these requests probably don't need to cohabit with other different response types. But if these request types are still to utilize the same sort of block/component-based pattern that the other responses use, then the controller's job is to assemble data bits from each block and (let's say, for simplicity's sake) stick them into an associative array. Maybe we use a POST parameter to indicate the request as AJAX so we can do some early-phase switching, but that's why I included 6. 6 is generally very similar to 5, except that it presumably contains POST parameters that are irrelevant to routing in addition to the AJAX-indicating POST parameter. Not a big problem with a good implementation of context, I think, but the use of POST as both a container for data to be used by the application and a container for data used by routing logic gives it some architectural similarities to a form submission.

7, for me, sits instinctively between the JSON response and HTML responses; it's sorta data assembly, but still also sort of string formatting. I lean towards the former because we still don't need the theme system, as the output is primarily intended to be robot-readable. Also, similar to 3, we're in a situation where we're basically looking to output the content of what amounts to a single block. What makes this interesting is that RSS & ATOM are a particular form of XML - so it can't be as simple as calling ->returnXML(). IMO, this is a case where the role of the block is to either spit generic XML or a data object, then the RC transforms that output into the desired format. But there's relatively little use for a DC, here.

8 is cool because we can make a really early, definitive decision about the type of response we're processing based on the Content-Type header; this could even be a case where we switch out the standard Dispatcher. Assuming we stick with the normal Dispatcher, though, we're in a situation similar to 5&6, where breaking things up into a DC and blocks doesn't really make much sense, except possibly as a way of sticking data into an associative array to be rendered into XML. Also, this request is intended to accomplish a server-side write operation as well as generate a string response, which makes it a hybrid operation that a conventional read-focused DC isn't well-suited to. It also splits the system between potentially cacheable (read/response) segments and non-cacheable (write/process) segments. Bottom line - I think this is a case where it makes sense to have a robust RC that is itself, basically, a SOAP server.

9 is a bit of a trick because it should be a reminder that, in introducing all these different ways that Drupal CAN respond, there's an in-tandem requirement that our 403s vary their form based on the response controller that's handling the request. Some food for thought, there :)

10, as Larry earlier pointed out, would format its response as JSON, but is otherwise more analogous to what I blathered about wrt SOAP: a DC makes little sense here, because the role of the RC is just to trigger some server-side processing. Blocks are irrelevant, as is caching, and the only conf this thing wants is enough context to be able to reconstruct where it is in the current batched job to do the handoff.

11 is all POST processing and no rendering, ultimately terminating (maybe) in a redirection. DCs, blocks, caching, all that are irrelevant; all that matters is a thin RC that handles the form processing. An interesting thought on this - might we write a special Dispatcher for handling form processing? Surely we can make that process less complex than that required for a standard page request...

Whew, ok, my brain is starting to give out and I know I'm rambling, but let me try to sum these up into a list of major different axes that we ought to consider here:

  1. What sort of string should be returned - HTML (nos. 1, 2, 3, 4, 9)? JSON (nos. 5, 6, 10)? XML (nos. 7, 8)? Nothing (no. 11)? And at what point, and by what mechanism, do we definitively determine this?
  2. Whether or not this response should be able to 'cohabit' with another, very different sort of response controller at the same basic $_GET['q'] - definitely (1, 2, 3, 7, 8, 9), probably not (4, 5, 6, 10).
  3. Is it at all sensible to utilize a Display Controller/blocks for this type of Response? Yes (1, 2), maybe (3, 5, 6, 7), no (4, 8, 9, 10, 11)
  4. Is the response cacheable? Yes (1, 2, 3, 4, 7), maybe/partially (5, 6, 8, 9), caching is irrelevant (10, 11). And, how cross-applicable are different caching systems to these different types of response systems?

There a a couple things I'm hoping will emerge from this mess. First, and maybe most important, is that I'm coming to be of the opinion that we should have a Response Controller interface & families of classes, whatever, which is the thing that picks up when the Dispatcher is done. That Response Controller handles the scut work of doing things like setting HTTP headers and attaching out-of-band data, triggering processing that needs to occur, and only in cases where it is appropriate, use a 'Display Controller' pattern (or if we can manage it, interface) to assemble together a bunch of different content bits for display. But the DC pattern is optional, and at the discretion of the RC as to whether or not it's appropriate to use. All the DC really does, then, would be to take assemble blocks into an appropriate rendering order, tuck em into a layout, maybe apply some styling, integrate with caching, and follow the renderer's instructions on what kind of output they should request from the block.

And, related to that...I think this direction where we think of the blocks as themselves responders makes very little sense. Maybe there's a very thin responder that interacts directly with the blocks, or a thin responder with a thin DC that interacts with them, but that layer needs to stay simpler: take context, render some stuff. Blocks can, say, render out an HTML string, some kind of generic XML string, a pass-the-buck-on-rendering data object, or some other data format. But that's it. Somebody who just wants to knock out a a quick block that outputs the site slogan should not have to think about RSS. There's a scarier question lurking here, if you want it - if we do want to use the block pattern all over our Brave New Drupal, then can every type of DC use every type of block?

Honestly, having been writing this post for more than four hours now, my recommendation would be at this point that we make a serious effort to map out ALL the different types of requests/responses that we want to be able to make Drupal to handle, whether in core or via contrib. I think it will make further work on the Dispatcher, RC, etc., far easier, as it will give us a much better sense of what decisions to make where.

Clarify: RC v. DC

sdboyer's picture

Larry pointed out that I'm splitting the RC & DC here, which he hadn't been doing before. Let me clarify that a little.

Conceptually speaking, we don't always need a DC, if a 'DC' is something like what Panels' render pipeline does now; I think my long post lays out the reasons for that pretty clearly. What we always need is an RC - a class + conf that gets arrived at by the dispatcher, and Drupal calls $rc->doYourThing(). Maybe, based on interfaces implemented by the selected RC, Drupal then calls some additional methods on it. But the essential point is that, because a DC-like concept is not applicable to all types of requests, the concept of a DC should be hidden from un-switchable-outable, critical path Drupal code. It's a pattern, possibly an interface, that RCs can use if they need it.

o_O

gdd's picture

I'm not sure we need to (or can) map them ALL out, mapping out the ones laid out here would get us pretty far. Thanks for an extremely useful exercise in thinking about stuff different ways. Some thoughts that are note very detailed but which I want to throw down

  • We should stop equating form submissions with POST. Forms can and, it can be seriously argued, should be submitted with a number of different request mechanisms. Equating POST and forms muddies the waters.

  • Speaking of forms, lets not forget that in the current world we have this little thing we all 'fondly' refer to as Forms API which quite possibly makes 'Display a form to be submitted' a logical #12 in this list. While we all probably have greater or lesser levels of love/hate relationship with it, I sincerely doubt anyone believes it should go away entirely.

  • Unsure where translation goes but lets not forget it.

  • One question I kept coming up with while reading this was not only 'How do we know what responses go where and do what?' but also 'How are we going to expose this to users in a way that makes sense to them?' Not being a CS-architect-type and having way too many years in consulting, I tend to think at the UI first. 'Users should do this, and the page will look as such. Now how do we make that make sense in the software architecture.' Do we just assume that all responses go to a Panels-like builder and create smart defaults out of the box? Is there a way in the UI to say 'Yeah do that EXCEPT for these bits here, those get rendered by All Text Pink module'. I'm sure many will think I'm off my rocker thinking about IA at this extremely early point, but remember we are application AND framework. Designing only to one of those use cases has bitten us in the ass many times. With great flexibility comes great responsibility.

Panels-y, I think

Crell's picture

I haven't thought through the UI that much, since I'm primarily a system engineer/system architect at this point, not a UI engineer. I was hoping we could attract some UI folks to this discussion as well to help answer that. :-)

My guess is that we would want to have some sort of hybrid of the blocks UI and Panels UI in core. Not as many buttons and dials and switches as Panels has but a nicer interface than the simple Blocks UI page.

We would need some way to define the "default" display, and then a mechanism for creating alternates to use in other places. Core would probably want to ship with, or at least be specifically designed for, a node-type-specific UI. That is, you'd setup your node type and then also go to the "Lay out this node type" tab where you could customize the look and feel, at least to some extent. And yes, we'd need some kind of sane defaults out of the box, probably modeling the standard 3-column layout we use now.

It should be noted that if we implement the core system properly a user-specific dashboard module (MySite) or admin dashboard (as in D7 core) become really really easy to implement in contrib. :-)

I'm tracking these

yoroy's picture

I'm tracking these discussions :) I'm not sure when and where the time is right to add UI thinking into the mix. But do call out for it if/wehn you think it would be helpful.

An idea

mfer's picture

Here's an idea:

  1. For a particular request (or context) you have a callback. For this request you have a default response type.
  2. A hook registers the available response types and maps to a class that can implement that based on a standard interface for all the types.
  3. A response object is built. This is based on either the default type, the registered type, or if something has altered that (e.g., a paramater passed in the url and a module is altering based on that).
  4. All callbacks (blocks, main content, everything) returns data in type independent structure. This could be the renderable arrays or some new and improved version of that is tied less to html.
  5. The response object takes these data structures and turns them into the proper response.

There are some difficult parts to this. For example, what structure works to have responses be in a data structure not tied to html?

Core would handle some basic types (html, rss, json). Contrib modules could implement other types as needed. And, if someone needed to override a type they could. Say, swap out the core html response class for a different one.

For themes

mfer's picture

So, something like this would make the theme system the response object mapped to the html type.

Web Services and Context Core Initiative

Group organizers

Group notifications

This group offers an RSS feed. Or subscribe to these personalized, sitewide feeds: