14. december 2010 by Thomas Stern
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.