Behind the Curtain Part IV
Part IV in the Behind the Curtain series was delayed for longer than I anticipated. So without further ado, let’s dive right in, shall we? The topic of Part IV is relationships. Relationships can be a very powerful tool when designing the data structure of your site. For some, relationships are clouded in mystery, and not having seen them used in a practical sense, they either avoid relationships, or use them poorly. So we’ll look at a real world example that I used when redesigning our site, and this will perhaps help you identify with this feature of ExpressionEngine.
Let’s go ahead and look at the end result here: Features Overview
Jesse of 31three.com had already given us the design for this page, so this was our end goal. Jesse created this page layout with no thought as to what the ExpressionEngine solution would be, so it was up to me to best determine how to bring Jesse’s design to life.
At first glance, it looks like a Category Archive, does it not? “Weblog Publishing”, “Template Engine”, “Add-On Modules and Plugins”, etc. could all be categories within one weblog, which we could call “Features”. Each feature could be an entry, and then we would just use the Category Archive tag to output this for us. One problem. Click on a feature, and notice that it discloses details about the feature. That means custom field data, and for performance reasons, the Category Archive tag does not allow the fetching the custom field data—only titles and paths. So if we went this route, we’d have to use either PHP or the Query module to fetch the custom field data for each entry. Messy at best, and very performance expensive at worse, especially if we just plopped a query module tag nested within the {entry_titles} tag pair.
But relationships, ah! What if each feature “category” was really a weblog entry in one weblog, and the features were entries in another weblog, related to the “feature category”? Then we could use the “feature category” weblog in a weblog entries tag, and use a {reverse_related_entries} tag pair to output the features themselves. This solution works great in this instance for a variety of reasons, including:
- Related entry data is cached, so unlike trying to add custom field data to the Category Archives tag, there aren’t many queries and large table joins to fetch the data on display
- Each feature only belongs to one “feature category”, so we don’t have to worry about multiple relationship fields, nor try to guess what a maximum number of relationships each feature would need
- Nearly everything available to the weblog entries tag can be shown as reverse related data
- Gives us supreme flexibility for the parent “feature categories” weblog. I had not yet created the Category Custom Field feature in February, so this was a more important factor at the time, in case we wanted more than a simple icon and description for the “feature categories” down the road.
- Not having exactly decided the order that the features would be displayed in, nor the amount of information we’d have for each, it was possible that we might desire to order by a custom field—something also not available to the Category Archives tag.
- Simple data entry: a single drop-down to assign a feature to its “feature category” instead of the category multi-select box. When you are only selecting one item, drop-downs are simpler (for me anyway).
It was put together as follows in two weblogs:
"Features - Categories" (features_categories)
- features_category_blurb (textarea)
- features_category_icon (text input)
"Features - Entries" (features_entries)
- features_entry_blurb (textarea)
- features_parent_category (relationship)
The tag to output our data (simplified):
{exp:weblog:entries weblog="features_categories"}
{features_category_icon}
{title}
{features_category_blurb}
{reverse_related_entries}
{title}
{features_entry_blurb}
{/reverse_related_entries}
{/exp:weblog:entries}
So at it’s simplest, we are looping through the “Features - Categories” weblog, outputting the title and blurb, and then listing all features below each heading. Add some markup, some Javascript for the blurb disclosure, our order by and sort parameters, and a disable= parameter for unused features of the weblog entries tag, and our three-column, fancied up output is complete:
{exp:weblog:entries weblog="features_categories" disable="categories|member_data|pagination|trackbacks" orderby="date" sort="asc"}
{if count == 1 OR count == 5 OR count == 10}<div class="tallColumn">{/if}
<div class="icon"><img src="/images/design/{features_category_icon}" width="32" height="32" alt="Icon" /></div>
<h3>{title}</h3>
{features_category_blurb}
<ul class="features">
{reverse_related_entries orderby="date" sort="asc"}
<li id="item{entry_id}"><a href="#" onclick="showhide('blurb{entry_id}');return false;">{title}</a>
<div id="blurb{entry_id}" class="blurbFull" style="display:none;">{features_entry_blurb}</div>
</li>
{/reverse_related_entries}
</ul>
{if count == 4 OR count == 9 OR count == total_results}</div>{/if}
{/exp:weblog:entries}
And that’s all there is to it. With the sheer number of features, I admit that I had a concern going in that the page load might be high and slow to process, especially since this page would undoubtedly receive a lot of traffic. And even with a high file limit for our caches, I was concerned that the sheer size of the four sites being ran from this single installation might result in cache files getting overwritten on a regular basis. So I had a worst case scenario contingency ready that would have one template produce this page dynamically, but to serve the actual features page as static HTML, generated as needed from the private dynamic template’s output. This was my first foray into using relationships on a large, traffic-heavy site, and it was the first ExpressionEngine site ever that would run on the Multiple Site Manager, so I was cautious but hopeful about what to expect. Happily, the static template was not needed. Even without caching, the page processes in just a hair over a second. With an active template cache, it’s just over 1/10th of a second.
So I hope this peek Behind the Curtain at EllisLab has provided some insight for you on how relationships might be used, as well as how to come to the conclusion that they might be a good fit for your information storage and display. Always start knowing clearly where you want to be at the end of the solution, then evaluate the pros and cons of any possible roads that will take you there. Relationships can be fantastic, but let yourself find them as a solution to a problem. Don’t create a problem just to use the solution. ExpressionEngine typically never locks you into a single method of getting from point A to point B, so study the map closely, plan your trip appropriately, and allow your mind to detour to explore other routes before quickly deciding on a given solution.


