WordPress as a CMS: Posts as content elements

I started writing a comment for the DevLounge article “Things To Consider When Using WordPress as a CMS” but it turns out I had a lot more to say.  I’ve been working with WordPress for a few years now and I want to share one of the toughest challenges I’ve personally faced when using WordPress as a CMS: jamming content into a complex design and still enabling the client to easily update it.  The operative word here is easily.  The client needs the WYSIWYG editor for all their copy editing.

To achieve this, we need to isolate content elements that make up the complex design.  A “content element” is CMS jargon but in WordPress terms, content elements can be posts.  For example, this page from the GoHave1.com web site I built a couple months ago contains several content elements.  In addition to the “Myths, Fears, and Facts” article (which is a content element itself), there are two content elements in the sidebar with titles “Jo Krombholz’s Story” and “There’s No Excuse.”  The following screenshot highlights all content elements in blue:

If you’re familiar with custom fields, you may be wondering why I didn’t just dump the content into custom fields for these content elements.  There are actually a couple of reasons: a) custom fields are not WYSIWYG-enabled and b) custom fields can only relate to a single WordPress page (or post) while some content elements are related to several different pages.  For example, the content element “Jo Krombholz’s Story” is also featured on its own page in addition to being shown in the sidebar of the “Myths, Fears, and Facts” page.

So, how do you get WordPress posts to behave as content elements?  By using custom fields to create relationships between pages and posts.

We add a custom field named “feature_story” with the value “23” to the page “Myths & Facts.”  The value “23” is actually the ID of the post “Jo Krombholz’s Story”.  Now we can use this relationship in our theme template.

In the WordPress theme template used by “Myths & Facts” we get the value of the “feature_story” custom field.  To do this, we get all the custom fields for the “Myths & Facts” page:

if (have_posts()) :
    the_post();
    $article_custom = get_post_custom();
    // ...
endif;

This will load the value “23” into the variable $article_custom['feature_story'][0].  Now we can use this value to retrieve the post data for the post “Jo Krombholz’s Story”.  Fortunately, the get_post() function is handy for retrieving a post object without affecting the loop in the current template:

$story =& get_post($page_custom['feature_story'][0]);

If we do a print_r($story) we get the following output:

stdClass Object
(
    [ID] => 23
    [post_author] => 1
    [post_date] => 2008-03-25 14:48:37
    [post_date_gmt] => 2008-03-25 22:48:37
    [post_content] => I never entertained the idea...
    [post_title] => Jo Krombholz's Story
    [post_category] => 0
    [post_excerpt] => I never entertained the idea...
    [post_status] => publish
    [comment_status] => open
    [ping_status] => open
    [post_password] =>
    [post_name] => jo-krombholz
    [to_ping] =>
    [pinged] =>
    [post_modified] => 2008-04-29 12:08:40
    [post_modified_gmt] => 2008-04-29 20:08:40
    [post_content_filtered] =>
    [post_parent] => 0
    [guid] => http://gohave1.com/archives/augue-ipsum-orci-dolor-interdum-enim/
    [menu_order] => 0
    [post_type] => post
    [post_mime_type] =>
    [comment_count] => 1
    [ancestors] => Array
    (
    )

)

You can use this object directly however, if you want to use template functions like the_content() to format the post content, you can pass the post object $story to the setup_postdata() function.  Careful though, this will affect the loop.

At this point, it probably seems cumbersome to manage content elements using WordPress’ core functionality. And I admit, it certainly is at first.  However, once you fully understand this technique and realize its flexibility, you will be a happy developer.

Of course, there is still lots of room for improvement here. Using custom fields to maintain relationships between content elements and pages is not intuitive at all. In fact, looking up IDs and copying them into custom fields is a management nightmare. An interface that would enable visual mapping of these relationships is really needed. I may just have to write such a plugin in the future.  If you’re thinking of writing such a plugin yourself, let me know.  I’ve been hashing out the design in my head for a while now.