A challenge of any Sitecore solution is finding the ancestors of an item in a cost efficient (i.e. using as little processing as possible) manner. For example we might want to find out if the current page is a the child of a news landing page, the simplest way to do this is to see if the news landing page is an ancestor of the current page.

We have several options when it comes to finding the ancestors of the current item:

  • XPath query - using the token "ancestor::*"
  • Using recursion and iterating up the Parent property of each item
  • Using the Item.Paths.LongID property in Sitecore
  • Using a Sitecore Search query

Out of the box Glass.Mapper.Sc will allow you to achieve the XPath, Parent and Sitecore Search solutions, however these are not necessarily the most efficient solutions. Instead we will see how we can use the Items.Paths.LongId property.

We start by creating a custom Data Mapper that will convert the LongID value which looks like this:

"/{11111111-1111-1111-1111-111111111111}/{0DE95AE4-41AB-4D01-9EB0-67441B7C2450}/{110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9}/{D6FB3BEF-6597-428F-8404-E46EF9265F83}"

Into an enumerable of Guids.

First we need to create the basic outline for the Data Mapper:

    public class AncestorDataMapper : Glass.Mapper.AbstractDataMapper
    {
        public override void MapToCms(AbstractDataMappingContext mappingContext)
        {
            throw new NotImplementedException();
        }

        public override object MapToProperty(AbstractDataMappingContext mappingContext)
        {
            throw new NotImplementedException();
        }

        public override bool CanHandle(AbstractPropertyConfiguration configuration, Context context)
        {
            throw new NotImplementedException();
        }
    }

We then need to indicate that this mapper is readonly, this can be done in the constructor:

        public AncestorDataMapper()
        {
            this.ReadOnly = true;
        }

The CanHandle method allows Glass.Mapper.Sc to determine if the Data Mapper can handle the type requested by the model. I am going to be lazy and just check two factors:

  1. Does the property have the return type IEnumerable<Guid>
  2. Does it have the SitecoreInfoAttribute

For you solution you might want to create a custom attribute rather than use the SitecoreInfoAttribute but for this example it will be sufficient. The CanHandle method will now look like this:

        public override bool CanHandle(AbstractPropertyConfiguration configuration, Context context)
        {
            var scConfig = configuration as SitecoreInfoConfiguration;

            if (scConfig == null)
                return false;

            Type type = scConfig.PropertyInfo.PropertyType;

            return typeof (IEnumerable) == type;

        }

Next we we need to write the code that maps data from Sitecore to the property in the MapToProperty method. This code is equally as simple:

        public override object MapToProperty(AbstractDataMappingContext mappingContext)
        {
            var scContext = mappingContext as SitecoreDataMappingContext;
            if (scContext == null || scContext.Item == null)
            {
                return new Guid[0];
            }

            var guidStrs = scContext.Item.Paths.LongID.Split(new char[]{'/'}, StringSplitOptions.RemoveEmptyEntries);
            List<Guid> guids = new List<Guid>();
            foreach (var guidStr in guidStrs)
            {
                Guid guid;
                if (Guid.TryParse(guidStr, out guid))
                {
                    guids.Add(guid);
                }
            }

            return guids;
        }

With both methods complete the final stage is to register this Data Mapper with the Glass.Mapper.Sc container using the GlassMapperScCustom class that is part of the web project.

Find the CastleConfig method and update with the following:

        public static void CastleConfig(IWindsorContainer container){
			var config = new Config();

		    container.Register(
                Component.For().ImplementedBy().LifestyleCustom()
		        );
			container.Install(new SitecoreInstaller(config));
		}

We now have everything in place to start using it with our models. All we need to do now is define a property on our model with an IEnumerable<Guid> and the SitecoreInfo attribute:

    public class AncestorModel
    {
        [SitecoreInfo]
        public virtual IEnumerable<Guid> Ancestors { get; set; } 
    }

Running and debugging the application I can see that my model now contains a list of the ancestor Guids from my item which will allow me to do a lot of useful tasks within my business logic:



It is relatively simple to extend Glass.Mapper.Sc to pull more data from Sitecore and make your models even more intelligent. If you are interested in leaning more about how Glass.Mapper.Sc works and how you can extend it to help create your Sitecore solution then check out our Training Courses.

comments powered by Disqus