Do you differenciate router context and business context?

We encourage users to post events happening in the community to the community events group on https://www.drupal.org.
pounard's picture

Do you differenciate router context and business context?

What I mean here is that the router context (in the MVC pattern) would give us the dispatcher to use. When I mean context this way, it means "AJAX" or "Normal page" or "RSS".

Then you could have only one of these contextes.

The business context is more like "OG context" or "User role context" or "Site context" (site as in global database subset, some nodes only) these context can be more than once together (and don't even have to know themselves).

This is important to make this differenciation, you can only have one dispatcher, therefore you have only one "http query" context. But, as any module can do any business out there, the business contexts can coexist.

I think that if it isn't differenciated, you will experience problems, for implementation, and at runtime as well.

EDIT: The menu router, which is the MVC controller of Drupal could tell both the router context and one or more business context. An OG AJAX menu entry could tell the router to instanciate both.

EDIT March, 18th: This post went to a flame, and I must admit I have my own instability when talking. But, whatever is Drupal, HMVC or MVC, this is not the point (we could argue days and nights this way), the point is, as donquixote said: But noone jumped into this thread to reply with explicit links to older stuff, and he got the real point here.

What I meant creating this post was exactly this, the whole Butler project seems interesting, really, and a lot of people seems to have made really long thoughts about it, but no one really did some kind of abstract.
My point was all about this, and even the most involved people wasn't able to answer to this simple question, I read most of the posts in this group, and not any seems to be naming things as they should, and people always get to reinventing the wheel by writing thousands of lines instead of doing a nice schema and using well-known words.

So please, and Crell is probably the most able person to answer to this in this thread: How do you differenciate a business context, and the request context? Do you plan to, at least?

PS: Just for fun (I love flamewars): Zend, using its ActionStack is probably the best HMVC implementation I ever saw using PHP code. Drupal blocks are not even close from HMVC, they have no controller, they don't pass their model data to any kind of view renderer, the final page build does that. What I would really like to see with Butler project tremendous topic is that they'll become part of a good HMVC pattern, which would implicitely make them ESI ready.

Comments

"router context" and

sdboyer's picture

"router context" and "dispatcher" context aren't words I've used, but the thoughts I've had and posted here generally endorse such a distinction. IIRC we've tended to talk more about early phase/late phase context.

Only problem with what you're saying here is "therefore you have only one "http query" context." -- you might not have that at all, if (say) you're running drush.

"HTTP query context" were bad

pounard's picture

"HTTP query context" were bad words, I mean whatever is the source of the query over the framework, you have to use a dispatcher to select a different view rendered depending on the caller context or on what he asks, this what I abusively called the "router context". But still, Drush or not, you can only have one at a time.

Pierre.

Ah. In that case, yes.

sdboyer's picture

Ah. In that case, yes. There's clear agreement that there's an early-on point at which a controller needs to be selected that'll governs the type of response. And there can only ever be one of those.

I've got Larry's characterization of this from yesterday in my ears still, where he presented only one of what I think will need to be multiple paths here - I'm still of the opinion that we actually ought to separate that into a 'Response controller' and then optionally a 'Display controller' beneath that - but that still doesn't violate the basic principle that there can only be one of these for a given request.

Yes, that sounds good. IMHO

pounard's picture

Yes, that sounds good.

IMHO we should use well known words for those concepts, I really like classical MVC terminology:
- Controller : Choose the action to execute (play with data from the model), view and response agnostic.
- Dispatcher : Route the controller response using various view renderers (sometimes, the dispatcher is known for a later intervention, depeding on framewords), what you called the "Response controller". This is where you instanciate one and only one component related to the "query context"
- View: independant component instanciated by the dispatcher, able to format the response returned by the controller.

I don't really like calling things "Some stuff controller" because it removes the sens of the word "controller".

Pierre.

Disclaimer: I do not care

sdboyer's picture

Disclaimer: I do not care about alignment (or lack thereof) with MVC. It's been the source of more utterly academic, e-peener-waving discussion about Drupal than any other topic. Which isn't to say there isn't value there - just to say that for me personally, MVC is gonna have to prove itself useful in practical ways before I'll stop basically just tuning out anything after "MVC".

That obnoxiously said... :)

Controller: wfm.
Dispatcher: wfm.
View: not gonna happen in a million years. that namespace is thoroughly taken.

Views is an ulgy module, and

pounard's picture

Views is an ulgy module, and because Views is a module does not means that a DrupalViewRendererInterface cannot exists. Your argument is invalid.

For the "e-peener-waving" I'd say that, if you speak more than one language, you have to use common concepts to understand other people. MVC is a pattern that means something to a lot of people, and yes, Drupal already does MVC since D5 (maybe even before, but I started D5), but a lot of people seems to ignore that fact.

Pierre.

Not MVC

Crell's picture

Drupal is not MVC. It is not even close to MVC. It is neither actual MVC nor is it the bastardized "web MVC" BS that rails-style frameworks claim to use, which is not related to true MVC at all.

If you want to use a common vocabulary, you want to actually understand your terms. See also:

http://www.garfieldtech.com/blog/mvc-vs-pac

Drupal-as-MVC is extremely off-topic for this discussion.

This article links to loads

pounard's picture

This article links to loads of external pages, except about the MVC pattern itself. Looking on Wikipedia (being used as source in the article to illustrate some concepts) you'd see this:

MVC is often seen in web applications where the view is the HTML or XHTML generated by the app. The controller receives GET or POST input and decides what to do with it, handing over to domain objects (i.e. the model) that contain the business rules and know how to carry out specific tasks such as processing a new subscription, and which hand control to (X)HTML-generating components such as templating engines, XML pipelines, Ajax callbacks, etc.

Which fits very well to the actual Drupal code.

When reading a lot of different sources, you'll see that many refers to the PAC as almost similar to MVC, then describe some PAC variants which actually are named *MVC. The difference is so small that I don't see why we couldn't talk about MVC here.

Pierre.

I would argue that with a

donquixote's picture

I would argue that with a definition of MVC that is as vague/permissive/abstract as this, the terms would hardly help us to explain anything. Imo we should use pattern names if they allow to make a very good and confident guess about the implementation. Otherwise the names would not rather confuse than they help.

I don't think so, patterns

pounard's picture

I don't think so, patterns are supposed to be well-defined so every one will understand them, but implementations often are mostly chunks of hacky code. But if you say to someone that Drupal is a variant of MVC, they will understand the menu router. If you say to them Drupal is PAC, they probably understand MVC too since it's a derivative concept, different, but which as its lot of similarities.

Pierre.

(sidetrack) About using

donquixote's picture

(sidetrack)
About using "known pattern names".

Using "known terms" can have advantages, but it can also cause damage and misunderstanding.

Because even if you own a book with a good definition, or if a lot of professional people do agree on the meaning of a term, it is quite bold to assume that a majority of Drupal people see it the same way. And even if, then this theoretic meaning will be different from whatever implementation we will see in Drupal. As a result, we will see a lot of confusion, and people discuss about what is the correct implementation of a pattern and what isn't.
Especially MVC and its terms have been used in too many different ways so far, that using these terms could hurt more than it helps.

That said, using uncommon terms like "Butler" is probably not much better.
Using some pattern names is generally a good idea, let's just be careful with it, and not expect that a pattern name will explain everything.
(/sidetrack)

- Controller : Choose the

donquixote's picture

- Controller : Choose the action to execute (play with data from the model), view and response agnostic.

Do you want one object/class called "controller" for the entire drupal site, or separate controllers per request type (json, html, etc), or per page callback?
(same question for "dispatcher")
And what is your "model"?

Model usually refers to the

pounard's picture

Model usually refers to the database, more generally to the storage engine business layer.

Controller is an abstract concept, it's a component (class or procedural code doesn't not matter here) that handles incoming requests and chooses what to do with it, then gives the response data to the dispatcher. It's classical MVC. It's only a tiny nevertheless important component in the MVC pattern. That IS ACTUALLY the menu router, since maybe D5. Yes, Drupal does MVC since a long time.

Usually there is only one controller, refered as the "Front controller" in Zend framework, but technology and implementation does not matter is.

Dispatcher can be sometime merge to the controller, but it's the component that is able to redirect the response into the right view renderer depending on what told him the controller, or making decisions using other context elements it can use.

Pierre.

Model usually refers to the

donquixote's picture

Model usually refers to the database, more generally to the storage engine business layer.

Yes, obviously. The question is rather what aspect or materialization of the "model" is exposed to the controller, and later to the page callback (that would be the view renderer, I guess).


Let's have a look at our current components:
menu_get_item() is a single function that does the routing. In comes a path, and out comes a router item - a bundle of instructions for building a page. Then these instructions are executed, and the page is returned.


In fact it's a bit more complex (current situation + a tiny bit of forward thinking):

Before menu_get_item(), the (non-aliased) "normal path" is determined from the original $_GET['q'] coming from .htaccess + mod_rewrite. The session data is loaded based on http request data (php does that by itself). The user object is loaded based on session data, and the result goes to global state.

menu_get_item() uses the router table and wildcard loaders, which need access to persistent data in the menu_router table (do we already talk about "model" here?). menu_get_item() can directly or indirectly look into global state again (the user object, remember?), for access checking and whatever.

The router item contains a page callback, and also instructions on the type of request: Is this json, xml, html, should the page be decorated with a title, etc.

The crumbs module can now determine where the router item fits into the site hierarchy. This data can be used to build a breadcrumb, and to decide which menus to expand or collapse. Some of the crumbs plugins may need data from the database (menus, taxonomy, etc) or the

A theme is chosen and initialized based on various criteria: Data from the router item, the crumbs trail, the user object, mixed with configuration from the database. Actually this happens a bit earlier, but I think it best fits in this position.

We could now do some logging and statistics, to count page views and track online users. In fact some of this could be done earlier and some needs to be later - whatever. I guess we just need a way to let components react to events that are relevant for statistics.

The page callback needs a lot of stuff from the database. Usually very specific parts of the database (can we say "persistent business data"?), but very often cross-module, and actually it could just ask for any data it wants. Ideally the page callback would declare what data it needs, so it can be injected and does not need to be pulled from global state.
The page callback needs the theme chosen for this request, and the user object that was loaded for this request.

The result from the page callback is usually just a piece of html. This can be a rendered node, a rendered list (views.module), or a mix of different things (panels.module, or some other tool that allows a composite main content).

Many page callbacks do more than that: They call drupal_set_title(), drupal_set_breadcrumb(), or some module-specific mymodule_set_something() functions. The purpose of these functions is to send some data into global state, where it can later be picked up by other components that want to decorate the page. Imo it would be better if this data would not go to global state, but rather to a container that is made available to the page callback.

In case of a regular themed page, we now have to decorate the page with blocks, menus, breadcrumb, messages etc. In D6 this was all stuffed into hook_preprocess_page, but afaik D7 is a bit different already.
The components that do these decorations need data from the database, the user object, the router item, and some will want to pick up data that was generated during the execution of the page callback. Such as a title that was set with drupal_set_title(). Ideally this data should be made available to those components in a container to be passed around, so they don't have to pull it from global state.
In addition, the decoration components need the current theme to render all their stuff.

Regular themed page or not, we now want a tool that builds a http response from the result of the page callback, and the decorations.


Variations / Flexibility test.

As a flexibility test, let's think of some variations of this process. A system that can deal with these variations will be easier to test and can be assumed to be less tightly coupled, etc.

Drush command: 10 requests in one php call.

Given 10 different http requests from a db table or configuration file, build 10 http responses, and store them all in the database. Do all of this in one single PHP call, with a shared global variable space. The 10 http requests can have different session data (and thus different users being considered "logged-in"). They can have different themes, etc.

Now do this stuff in parallel: First load the 10 router items, then execute the 10 page callbacks, execute the 10 page decorations, etc.

Drush command: Partial execution.

Execute the page callback from a made-up router item.
Execute the same router item with different themes.
Load a router item, but don't execute or render it.
etc


Putting the pieces together.

"persistent data" (db and file storage)
"request data".
"current user" built from request data + persistent data. (*)
"router item" built from request data + persistent data + user object.
"chosen theme" built from router item + persistent data + user object. I think the theme should be stored as part of the router item! Because if in the same php process we execute a different router item, this other router item could have a different theme.
"page data" (rendered page + decoration data from page callback) built from router item (incl theme) + persistent data + user object
"response data" built from router item (incl theme) + page data + persistent data + user object

(*) In some cases we want to make modifications to the user object, based on the router item, for access checking hacks. This is ugly, but some modules just work this way.

The list tells us how we can package the information in separate arrays, containers or whatever. I think we should really stick with simple nested arrays, until we have a better understanding of the requirements. Let's not try to be smart.

So, we have:
- components that do things: menu_get_item(), the page callback, the page decoration system, etc.
- packages of data that are passed from one component to another.


Local is better than global, but ...

If we think about the above tests, we absolutely want to have current user, current request, current theme as objects to be passed around, and that don't live in global state.

We could think of other tests where we need non-global representations of database and all persistent storage.

I think we can afford to make this the last step. Let's first get the request-specific things out of global state, and when this is done, we can do the same for the db connection.


Eh... you say "global state" all the time.

By global state I mean literal $GLOBAL['something'], static function variables, static class variables, and state/mode information stored in PHP itself (such as php's current db connection). "Pull from global state" means to either access these variables directly, or to call any function or static class method that directly or indirectly reads or uses a global state variable.

Example: db_query(), menu_get_item(), user_access(), etc, all these need to read from global state variables, and their behavior depends on the current state of these variables.

It's a different story if the "pull from global state" is wrapped into an object that was injected. The component that uses this wrapped thing is freed from the blame.

I'm not going to answer the

pounard's picture

I'm not going to answer the full post right now, you took the time to write all this down and I need a bit more time to read it. Nevertheless, I saw some stuff that you are saying that I don't see this exact same way:

Yes, obviously. The question is rather what aspect or materialization of the "model" is exposed to the controller, and later to the page callback (that would be the view renderer, I guess).

The view rendered is mostly the theme delivery callback. The biggest part is run via drupal_render() after the page has been built (at least using D7). This means that the page callback only compute data and does not really renders anything (if it does, it's crappy coded). Node module let the page build render the node itself, it only did build the render array.
So, the page callback, IMHO is more the "controller action", the piece of glue that requests the model, fetch the data, compute some other data, then pass it back to the controller (them menu_active_handler_...() function).

The real workflow here is:

  1. Application bootstrap (it's PHP..)
  2. menu_active_handler_cantremmeber() and menu_get_item() - check for the path, get the right page callback - the controller
  3. page callback() - play with the model, return response data - the controller specific action
  4. gets back to menu_active_handler_cantremember() - back to the controller
  5. .. which checks for a delivery callback and runs drupal_deliver_page() - the dispatcher
  6. the delivery callback (default is drupal_deliver_page() if I remember well) - this is the view rendered

The theme itself, in this workflow is only a skin, pure theming and visual, no business stuff here. It should normally be used only once it gets to drupal_deliver_page() (using the rendering API), most core modules works right this way and does not really render anything before it gets to page build time (page build is the view rendered, not the theme).

Pierre.

basic principle that there

donquixote's picture

basic principle that there can only be one of these for a given request

Beh. Disagree, somewhat.
In the usual case, one per request. Ok.
But it should be possible to kick off an arbitrary number of these things from a local context, for whatever purpose. This will be possible as long as these things get their dependencies (current user, request path, wildcard loader results) injected, instead of depending on global state.

Some other things when talking about MVC:

  • I never really saw the point of having classes like BlogController, ForumController, etc, with methods that act as page callbacks (often called "actions"). Often these classes have not much to share, except stuff inherited from a more general godly BaseController or something. I very much prefer the concept of page callback functions. If we want to organize them, let's use namespaces, not classes. If they should get some more dependencies, let's use function parameters. The godly parent class can instead be a wrapper object that is injected.
  • I like that in Drupal you can use menu_get_item($path) without actually calling the page callback. We should keep this possibility alive, even improve it, so you can do something like that with or without wildcard loading, or even lazy-loading wildcard loaders. Atm menu_get_item() has to pull from global state, but the same could be done with an injected $something->getRouter()->loadRouterItem($path).
  • We need to consider that related page callbacks (that in some "MVC" application would all be in "BlogController") can be implemented in different modules that do not know about each other. We even have to consider that different parts of a page (panel elements, blocks, views handlers) are provided by different modules, that again don't know about each other. This is why we have to be very careful about locking things together in classes.

Exactly why I wish we would

sdboyer's picture

Exactly why I wish we would stop talking about MVC. Can we solve our problems, please, and bring up external concepts only insofar as they actually relate to the topic at hand?

More importantly, you've shifted scope here. The sort of things you're mentioning here as exceptions are waaay further down the logical path than an early-stage "there-can-be-only-one" object. Two quick points, though:

  • menu_get_item() is handy, and it may have to go away. I've thought about that a fair bit. Unfortunate, but maybe we can come up with a good replacement.
  • I'd encourage you to go back to the early discussions in the group and read about the primacy of blocks within the rendering model, and seeing how those become the primary renderable-thing that what we've been calling "Display Controllers" work with.

You really didn't understand

pounard's picture

You really didn't understand in fact. MVC is a pattern, not some kind of OOP joke. Drupal already does MVC.

I'm not speaking about classes, and yes, MVC is actually your problem. That same problem that is being discussed in another threads (like just http://groups.drupal.org/node/67588 there for example). In fact because Drupal already has an incomplete MVC implementation, and because what they call "reponse controller" (why do you use the "controller" word everywhere? it does not make any sense at all). You should be worried about this pattern.

Rendering model, primacy of blocks, and such are the whole goal of designing a good MVC pattern, you cannot render a block using AJAX/JSON/WhateverOtherFunSerializationFormalism without a dispatcher able to give the adapted response depending on the user request or the context, this why the MVC pattern was born into books, following a generic solution that already is partially into Drupal.

In fact, D7 is actually almost full MVC since you can specify "delivery callback" which refers more to the view renderer than a "delivery callback" that does not mean anything (callback I can understand, delivery may have a meaning here, but the right word is "formatter" or "renderer" or just "view").

Pierre.

Different types of context

donquixote's picture

http://groups.drupal.org/node/96924
"Context components with different scope/lifetime"

I think this one goes in a similar direction. Just the title is not the best.

I see two things we need to solve:
- Let different modules contribute different parts of the context. Some of this loaded on startup, other stuff lazy-loaded when it is needed.
- Wrap this stuff into objects that can be passed around to page callbacks and other consumer components, which again live in different modules. This wrapper can be a passive container or an active one.

And btw, I think the best

donquixote's picture

And btw, I think the best progress can be made on small-scale refactoring, where the consequences can be more easily assessed.
It is already possible in D6 to organize individual modules in nice and small dependency-injected classes. There still needs to be some "pull from global state", but this can be separated from the actual logic.

That said, in this group I guess we want to focus on macro architecture..

When talking about contexts,

jmccaffrey's picture

When talking about contexts, we should really think of them I two dimensions. Context data is available in a PLACE in your code at a TIME.

Possible context lifespans (TIME):
* A single user request
* A single user session
* Globally for this server
* Globally across the cluster

Have you guys thought about the temporal nature of contexts, and how this could be used?

Closing this thread

Crell's picture

OK, this thread has gone far afield.

1) Drupal is not MVC. It's not even close to MVC. Butler, if successful, won't really make us MVC either. That's because MVC does not exist on the web, only a bastardized lie that gets called MVC by Sun and the Rails people. Not to be too forceful, but please do not bring MVC into Drupal discussions until you've read this article multiple times and grokked it:

http://www.garfieldtech.com/blog/mvc-vs-pac

2) I think we've already gone over most of what is in this thread a while ago. Please go back in this group to see some of the earlier discussions.

Honestly I don't think this thread is adding anything to the discussion at this point that has not been covered already, so I am going to just close it.

Crell, it seems that Drupal

pounard's picture

Crell, it seems that Drupal core developers implemented MVC without even knowing it, which seems quite fun in the end. But Drupal is almost a full MVC implementation, except that the controller is also the dispatcher (which is not always present in MVC implementations), gets the response and gives it to the view. Seems like a well known variant of the MVC pattern.

The post you linked is well written, and shows some intelligence and brain storming, but actually that doesn't change the fact that Drupal actual implementation almost is a full MVC.

EDIT: You can close the discussion, if it makes you feel powerful, but it won't change what Drupal actually is.
Re-EDIT: Typo.

Re-EDIT: @Crell: I might apologize a bit, fully reading the PAC description indicates that the main change between what you call a real MVC system and PAC itself is the hierarchical aspect. I tend to think that using D7, this hierarchical model tends to flatten because of the drupal_render() heavy usage in core, where nothing is rendered at all (even if there are many helpers for rendering the data, in fact each one of them are being rendered only when the final page si building, if you follow the actual tendancy and documentation). You can see the drupal_render() build array as hierarchical data sets which are being rendered only once, when building the final response, which makes it pretty flat in the end.

By the way, did some readings:
http://en.wikipedia.org/wiki/Presentation-abstraction-control
http://www.dossier-andreas.net/software_architecture/pac.html
http://www.vico.org/pages/PatronsDisseny/Pattern%20Presentation%20Abstra/

Pierre.

I think we've already gone

donquixote's picture

I think we've already gone over most of what is in this thread a while ago.

Maybe we have (or the people who have been more active in this group).
But noone jumped into this thread to reply with explicit links to older stuff.
This could indicate that the stuff that has been said in the past does not contain enough material that could be used to answer specific questions..

I for myself have read all or a lot of the discussions in this group (some already faded out of my memory), and still my picture of the butler is quite vague.

As far as I understand, the butler will help us with the "information bundles to be passed around", so they don't have to be read from global state (as in #comment-432414). So it would be some kind of smart container. But still not sure if this assumption is correct.

But you are right about one

pounard's picture

But you are right about one thing, Butler is not all about this, and my original question was a real question. You are making what is sometime being called Drupal the next generation, and some concepts you attempt to highlight in the whole Butler project are still ambiguous.

Pierre.