Quantcast
Viewing all articles
Browse latest Browse all 43

Sitecore Pipeline Enabled LinkProvider

The LinkProvider is probably one of the most customized features in Sitecore and there are a bunch of great blog posts about how to create your own LinkProvider including these excellent posts by Martin Davies "A Switching Link Provider" and Valerie Concepcion "Site-Specific Providers".

However these posts as well as the majority of posts about working with the LinkProvider and other areas in Sitecore focus on how to handle multi-site scenarios which in itself can be a bit of a challenge with Sitecore.

But the challenge involved in handling multi-site setups is in some way only the tip of the iceberg and points to an underlying ever present issue in Sitecore that I like to call the monolithic nature of the beast.

Sitecore is in almost all aspects a true enterprise level CMS but unfortunately there are a few monolithic features remaining in the architecture that doesn’t cater well to enterprise range installations.

When it comes to multi-site setups in most cases great people from the community have come up with elegant ways of solving the challenges.

Another challenge that is less easily solved is how to find elegant ways of maintaining a clean well structured solution architecture that abides the most fundamental teachings of good object oriented programming such as the open/closed principle and the single responsibility principle when dealing with some of the remaining monoliths in Sitecore.

The LinkProvider is on of these odd areas that have a tendency to get fouled up in switch statement code blocks and a mishmash of mixed responsibility. Sure Sitecore offers the ability to register multiple LinkProviders but then when you opt to use the LinkManager to access the LinkProviders you have access to the registered default LinkProvider as well as a collection of all the LinkProviders that are registered in the LinkManager’s providers section in the web.config. However which of the LinkProviders to use in the specific case is left for you to decide. This either means that you will have to access a specific LinkProvider by name in the LinkProvider collection or write some kind of outer wrapper that again will end up with a bunch of switch case statements to determine which of the LinkProviders to use. Well it would be easy if you could stuff all that logic in somehting like the LinkManager but then again you can’t because the LinkManager is static.

So you end up doing what everyone else does. You write a single custom LinkProvider and register it as the default provider and then you stuff all the logic with the awful switch statements into the LinkProvider and end up breaking just about every conceivable object orientated principle in the book.

for a second or two you may even pause to wonder “who on earth came up with the idea of the LinkManager and the LinkProviders collection when it is so evident that there is no clear way to really utilize having multiple LinkProviders?”

So how can we solve the challenge of having multiple bits of logic for resolving item urls and at the same time uphold the basic OO principles?

One way of solving it is to combine one of the less shining features in Sitecore, “The LinkProvider”, with one of the truly brilliant features, “custom pipelines”.

By creating a custom LinkProvider that runs an internal pipeline we can create individual pipeline processor steps that are only capable of resolving a specific type of custom item links (single responsibility principle) to ensure clarity. At the same time we can add additional processor steps if we need to add new functionality without having to rewrite existing code (open/closed principle).

Creating a pipeline enabled LinkProvider is actually surprisingly easy.

For a condensed example of setting up a custom pipeline, you can also read this excellent post "Creating a custom pipeline in Sitecore" by Anders Laub Christoffersen.

First we need to setup a new custom pipeline in the web.config (yes please do use include files and don’t edit the web.config file directly).

<sitecore>
  <pipelines>
    <resolveItemUrl>
    </resolveItemUrl>
  </pipelines>
</sitecore>

Now we need to create a LinkProvider that can run the pipeline.

using System;
using Sitecore.Data.Items;
using Sitecore.Links;
using Sitecore.Pipelines;

namespace TheGrumpyCoder
{
  public class LinkProvider : Sitecore.Links.LinkProvider
  {
    public override String GetItemUrl(Item item, UrlOptions options)
    {
      ResolveItemUrlPipelineArgs args = new ResolveItemUrlPipelineArgs(item, options);
      CorePipeline.Run("resolveItemUrl", args);

      if (args.IsResolved && !String.IsNullOrEmpty(args.Url))
        return args.Url;

      return base.GetItemUrl(item, options);
    }
  }
}

The code in the LinkProvider uses a specialized pipeline args class to send data to the individual processor steps in the pipeline. The args contains the item and the url options passed to the LinkProvider as they may come in handy - especially the item is pretty much required since we are trying to resolve the url for it. You could also pass the LinkProvider itself to have access to some of the default LinkProvider properties as well. On top of the data the args also contains the string property “Url” that is initially null and the boolean value “IsResolved” that is initially false.

using System;
using Sitecore.Data.Items;
using Sitecore.Pipelines;

namespace TheGrumpyCoder
{
  public class ResolveItemUrlPipelineArgs : PipelineArgs
  {
    public ResolveItemUrlPipelineArgs(Item item, UrlOptions urlOptions)
    {
      Item = item;
      UrlOptions = urlOptions;
    }

    public Item Item { get; private set; }
    public UrlOptions UrlOptions { get; private set; }
    public String Url { get; set; }
    public Boolean IsResolved { get; set; }
  }
}

Now we need to create a url resolving processor step for our pipeline. The processor can contain all sorts of code, mumbo jumbo or whatever else is needed to resolve the url for the item at hand. The most important thing to remember is that you don’t need any switch statements or mixed responsibilities - that part you can leave out. If the item or other conditions doesn’t meet the requirements by the processor step then simply return and hopefully one of the next steps in the pipeline will be able to resolve the url.

using System;
using Sitecore.Data.Items;
using Sitecore.Pipelines;

namespace TheGrumpyCoder
{
  public class SpecificItemUrlResolver
  {
    public void Process(ResolveItemUrlPipelineArgs args)
    {
      if (args.IsResolved)
        return;
        
      // Some kind of custom url resolving using the args.Item
      args.Url = "The resolved item url";
      args.IsResolved = true;
    }
  }
}

The last step is to register the processor step in the resolveItemUrl pipeline.

<sitecore>
  <pipelines>
    <resolveItemUrl>
     <processor type="TheGrumpyCoder.SpecificItemUrlResolver, TheGrumpyCoder" />
    </resolveItemUrl>
  </pipelines>
</sitecore>

With the pipeline enabled LinkProvider in place all that is needed to add additional logic in the future is simply to create a new processor step and register it in the pipeline - you can even control the order in which the steps are executed by arranging them in the pipeline to ensure that the least taxing operations or most common scenarios are handled first.

And the best part is that you won’t ever have to wonder about who came up with the idea of having a collection of LinkProviders in the LinkManager ever again.


Viewing all articles
Browse latest Browse all 43

Trending Articles