The “Named Content Views” Pattern
Here’s another pattern of content management: content objects have “views.” This means that content objects have several predefined ways of being viewed across your system.
For example, here are (the) two big ones using an “article” as an example.
Individual: The view of a single content object – how your article appears when you view it by itself on an “article page.”
Index: The view of a single article in a list of other articles. So when you display the “Politics” category, this is how a single article gets rendered in the list.
Regardless of what type of object you’re looking at (article, survey, staff bio, etc.), they can probably be viewed in both of these ways (with some exceptions – if an object isn’t meant to be URL-addressable, then perhaps there isn’t an Individual view, but this is relatively uncommon).
So here we transcend the notion of specifically how an article looks in an index of articles, and we concentrate on the fact that we just need to define this is as a “view” of the article. The formatting it uses and properties it exposes in this view don’t really matter, what matters is that the article can render itself according to this named view.
What’s handy is when you can define these views, and then tell the content objects to render themselves in each instance. This means the template code for the “Politics” page of your magazine looks something like this (if it’s not obvious, this is completely made-up template code meant to represent templating systems in general):
[fetch Articles] [foreach Article in Articles] [Article.RenderView("Index")] [next]
This is good because you can define the same view across multiple content types. For instance, if you have a “Survey” object that’s also in your Politics section, you can do this:
[fetch Everything in the Politics section] [foreach Object in Everything] [Object.RenderView("Index")] [next]
What’s neat here is that you’ve retrieved a heterogeneous list of content types and you’ve rendered them all in a list. It doesn’t matter that they’re different types, because they all have an “Index” view, and they all know how to render themselves for that view.
In the end, you’re really just stringing together a list of Index views. Those views just happen to have content objects behind them.
Say you want a site map with a big table of stuff in your site. No problem – just create another view for “Table Row” and “show” all your objects how to render themselves for this view. This example actually gets a little complicated because you have to make sure all the objects render the view with the same number of columns, but so long as you pick a number and stick with it…
The Table Row view for an article may do this:
While the Table Row view for a survey might do this:
[Title]Open: [Opened] [if Closed], Closed: [ClosedDate] [/if]
Again, so long as you make sure that every content object has a “Table Row” view, then it doesn’t matter what content types you try to render down the list.
Another one is a “Title” view, which refers to how your object appears when “viewed” in the TITLE tag of the page. This is simple, but you’ll see why it’s handy in a second.
For an article:
For a survey:
Survey: [Title] [if Closed] (closed) [endif]
Here’s how this can really be used:
Here you have a page template that can render any object type in the system. It doesn’t matter what object type it is, because the template is just calling views. So long as those objects know how to render themselves in those views, then it works.
(Obviously, this has the added benefit of keeping your template code in one place. If you have articles listed all over your site with an “Index” view, and you change that view, then it changes everywhere. Of course, you may or may not want this…)
Now, a good system doesn’t stop you from just rendering content manually in some cases, like this:
[fetch Articles] [foreach Article in Articles] <h2>[Article.Title]</h2> [Article.Summary] [next]
In fact, if you have a one-off rendering somewhere, then this is a lot easier than defining a global view for this one instance.
Now, before you go thinking that I’m amazing for thinking this all up, let me tell you what you should have probably known already if you’ve hung around this blog long enough: eZ publish does everything I’ve described here. So, yes, I tricked you into a little more eZ publish theory.
(It needs to be said, too, this is just an extension of object-oriented programming itself. Think interfaces.)
On top of what I’ve described here, eZ publish has the fantastic ability to let you selectively override a view. You can redefine a view based on things like the specific object ID (so a single object can have its own view) or the branch of the content tree in which the object resides (so the “Individual” view of articles in the “Politics” section can be different than articles in the “Sports” section).
Furthermore, you can “fall back,” meaning you can define a set of standard views to use in the absence of anything more specific. So if you haven’t defined an “Index” view for a specific content type, the system will fall back to the standard “index” view.
(Of course, this view shouldn’t use anything but the properties common to all objects since it doesn’t know what object it’s going to be rendering. It should use the stuff on the envelope, not inside of it.)
(For the record, eZ publish calls the “Individual” and “Index” views, “full” and “line,” respectively.)
Also, since – as we’ve discussed before – eZ publish’s admin side runs the same way its public side runs, you can get an “edit” view as well, which renders the content object in an HTML form, ready to be edited. (Note: I may have the view name wrong here, but the theory is right.)
I actually had this theoretical model in my head years ago, but eZ publish was the first system in which I saw it fully implemented. It works extraordinarily well, and it’s another reason why eZ publish remains the most flexible content management system I’ve ever worked with.