For this post I will look into a simple technique for building easy to extend or reuse features in Sitecore solutions. This simple architectural concept will, for the rest of this blog post, be refereed to as an Interface template. This idea comes from my work in Pentia, where we build website that should be easy to maintain and reuse or extend implemented features. Some of you, that where at Dreamcore 2010, might have been so lucky, that they saw my brilliant colleague Thomas Eldblom in cooperation with Dan Algrove from Hedgehog development, in a session on how to build maintainable Sitecore solutions. You can read more great thoughts from Thomas at his blog Molten Core. Some of the key points from this session and my daily work with Thomas is the base for the blog post, and I will not take any credit for being the inventor for the interface template concept. I’ve just used it a lot recently to make it easier for new templates/pagetypes to reuse implemented code features, and thought I would make for a good post.
We all know that code degrades as time goes by, and that the more “quick fixes – Hey it works don’t change it” where used during implementation the faster the code degrades, and oh yes even the best code will degrade over time, but maybe not on a quite as steep slope.
We have all tried to write simple parts of a website, but even the simple parts may lead to complicated code. Let’s take an simple example as a Menu or Breadcrumb path. Most websites have some sorts of navigation and likewise have breadcrumbs. So to start with the Customer have a simple website where the breadcrumb starts at the current item and works it’s way up the content tree to the root of the site i.e. at the front page. It would be an fairly easy task to implement code that can build this sort of breadcrumb path. This could be done either in an simple xslt or maybe by implementing some sort of breadcrumb provider in c# class.We will traverse every ancestor until the following condition is meet. This could for example be done using a Sitecore query a look something like this.
Note only pseudo code will be used in this post.
ancestor-or-self::item[@tid = ‘frontpage’]
This will work great and is a simple and acceptable solution. So what is the problem ? After an year your customer returns with an wish that they want functionality restart the navigation “breadcrumb” for some node in content tree, this could be anywhere. So the root item ie the first link in the breadcrumbs path is no longer the front page but could potentially be any node.
The question now is, how do you determine where the root of the breadcrumbs path is? You can no longer settle for a simple test against the “frontpage”-template or could we ? A simple way would either be to make an new front page at the given point in the content tree, but it seems stupid that the customer will have to use a front page for this task and might be confusing as well. So okay no problem we will just make an new template, or choose a page type and add the template to the query condition and it might look something like this.
ancestor-or-self;;item[@tid = ‘frontpage’ or @tid = ‘subfrontpage’]
Great it works and was pretty simple too, but now we started on the slippery slope.The key point to notice here is that for this to work we had to make changes to the code.
What will happen the next time the customer returns with a wish to reset the navigation for the all ready existing document template or any template for that matter. And even more the document template should have a check box that enables the reset features. No problem we simply add the check box to the document template and extend the select query.
ancestor-or-self::item[@tid = ‘frontpage’ or @tid = ‘subfrontpage’ or (@tid = ‘document’ and resetmenuField = ‘1’ )]
Wow it worked but now the query is becoming quite complex and keep in mind this is an simple example. And soon the customer will be back with a note that the shoppingItemTemplate needs to reset the breadcrumbs as well and with the same condition as the document template. As the years go bye the query is becoming more and more unreadable.
But how can we avoid this slippery slope ?
We could build a new template and let it inherit from the “frontpage”-template, new template will then have the same fields as the front page. This might lead to a more confused customer because a simple document template will have the same field as front page without using them.
So now we will have a look at an interface template. This could be a simple template with or without fields. The sole purpose of the this interface template, is to define “some functionality”
for another templates that inherits from it. Lets revisit our example with the breadcrumbs.
First we define this template without any fields lets call it _isMenuRoot. And let the frontpage template inherit from this. Now we can write the first query as this.
ancestor-or-self::item[ is derived from template _isMenuRoot]
The query will return the front page as the menu root just as we wanted. Perfect now the beauty of the simple template comes into play. Now the customer wants a simple sub front page that can reset the breadcrumbs. This should be a fairly easy task. Build your normal sub front page a let it inherit from the _isMenuRoot template now look at the query
ancestor-or-self::item[ is derived from template _isMenuRoot]
Wow it’s the same and we didn’t have to change anything in the code, great!
Now let’s extend the template so it has a simple checkbox field that allows back end users to enable/disable the reset functionality. This is done simply by extending the _isMenuRoot Template with an checkbox field. Of course this demands for a simple change to the code, so we change the query accordingly so it reads.
item[ is derived from template _menuRoot and reset menu = 1]
Of course this will force back end users to actively enable/disable this feature for existing items. But this can be done fairly easy on templates that inherit from the interface template. For example, you might want the front page to always have this feature enabled, and as default disabled for a document page or alike. You can achieve this but setting the checkbox value in standard values for all the templates the inherits from our _isMenuRoot interface template. Thank you sitecore for standard values. So without changing any code, future templates can nowgain access to this Menu root functionality by inheriting from our _isMenuRoot template.
The above helped me on project i recently worked on where I had an if statements that had to test for 8 different templates. Everytime someone came to develop new templates/page types they had to go in and actively add another clause to the if statement, if they wanted to acces the code features provided after in if statement. This were change by the use of an interface template so the if statement checked for an interface template instead of 8 different templates .And of course all the template previously in the if statement, now simply had to inherit from the newly created interface template. New templates/pages, that want to reuse the same functionality, simply had to inherit from the interface template,without the need for changing any code at all.