Using the Glass Sitecore Mapper with Multiple Types of Children

Tuesday, May 07, 2013 @ 03:04

By: Jon Upchurch, Senior Developer

There’s no question that using the Glass Sitecore Mapper has increased the speed and flexibility of my Sitecore Development. I can create my complex Data Template structures, quickly and easily model them in .NET code, and be intuitively using them to rapidly create presentation in the form of Sublayouts.  The code is far easier to read and maintain and there’s a lot less chance for obscure errors.
Glass also give you ready access to the children of an item by using the [SitecoreChildren] attribute. Consider the following example:

[SitecoreChildren(IsLazy = false)]
public virtual IEnumerable<Widget> Widgets { get; set; }

If my WidgetKit Content Item contains 5 child Widgets, I can get access to them rapidly simply by calling myWidgetKit.Widgets. If you’ve worked with Glass in the past, this isn’t new to you but it is a very handy feature.

Let’s consider some more complicated scenarios now. For example, I’ve got a number of situations where I needed to get all items in a folder. Sure, this can be handled using a Sitecore Query, and Glass supports this as well. It’s not quite as easy to use as the [SitecoreChildren] tag is, but it’s still possible. Alternatively, I’ve modeled the Folder template in Glass and created methods that get all children as a certain type of item. For example:

[SitecoreChildren(IsLazy = false)]
public IEnumerable<EventType> EventTypeChildren { get; set; }

[SitecoreChildren(IsLazy = false)]
public IEnumerable<Location> LocationChildren { get; set; }

[SitecoreChildren(IsLazy = false)]
public IEnumerable<Topic> TopicChildren { get; set; }

[SitecoreChildren(IsLazy = false)]

public IEnumerable<Category> CategoryChildren { get; set; }

This worked out well at the time because I would only have one type of children at a time in a folder, and this gave me quick and easy access to them in a controlled way. Also, it fails gracefully since Glass will map fields that it can find and still return an item even if it’s not of the right type. With this in mind you’ll want to do some checking on the consuming side to be sure that the item has the right template name or ID, but we have provisions for this in our GlassBase class.

The biggest challenge comes into play when there are multiple Data Templates that are allowed to be inserted into a parent at the same time. Neither of these solutions will cover this case well. Having run into such a case, and wanting to keep things as simple and reusable as possible, I added the following methods to our GlassBase class:

//This was part of the initial GlassBase implementation
[SitecoreInfo(SitecoreInfoType.TemplateId)]
public virtual Guid TemplateId { get; private set; }

//This was part of the initial GlassBase implementation
[SitecoreInfo(SitecoreInfoType.TemplateName)]
[SitecoreFieldAttribute(FieldName = "Title")]
public virtual string TemplateName { get; private set; }
       
/// <summary>
/// Since all of our Glass classes inherit from this base class, we can guarantee
/// that they will be correctly mapped as this base type
/// </summary>
[SitecoreChildren(IsLazy = false)]
public virtual IEnumerable<GlassBase> BaseChildren { get; set; }

public IEnumerable<T> GetChildren<T>(string templateName) where T : GlassBase
{
var items = new List<T>();
       ISitecoreContext context = new SitecoreContext();
       if (BaseChildren != null)
           items.AddRange(
            from item in BaseChildren
              where item.TemplateName.ToLower() == templateName.ToLower()
              select context.GetItem<T>(item.Id));
return items;
}
public IEnumerable<T> GetChildren<T>(Guid templateId) where T : GlassBase
{
var items = new List<T>();
       ISitecoreContext context = new SitecoreContext();
       if (BaseChildren != null)
        items.AddRange(
               from item in BaseChildren
                     where item.TemplateId == templateId
               select context.GetItem<T>(item.Id));
return items;
}

This will allow you to selectively get only the children of a given type by passing in a type and a template name or ID as an argument. Better, this is available automatically without needing to use the potentially problematic [SitecoreChildren] attribute, and you don’t need to filter out the unwanted types.
In addition to this, I’ve added template names and ID’s to our Constants class so the usage would look something like this:

    public class Test : GlassUserControl<Event>
    {
        public void Page_Load(Object sender, EventArgs e)
        {
            foreach (
                var item in Model.GetChildren<EventRegistration>(
            Constants.Events.EventTemplateNames.EventRegistration.Id))
            {
               if(item.Any()) {...}
            }
            foreach (
                var item in Model.GetChildren<EventLocation>(
            Constants.Events.EventTemplateNames.EventLocation.Id))
            {
                if(item.Any()) {...}
            }
        }
    }
This can also be easily extended using recursion to propagate through all descendants of a specific type, or any number of other practical uses.