As part of our efforts to share the latest technologies and capabilities in the world of digital marketing and web development, we’ll be posting a series of articles on Sitecore, one of the most impressive experience platforms available today. Though this post will primarily be relevant to developers, we hope the content remains interesting to all of our readers and provides some insight into the more technical aspects of the digital development world.
Sitecore items support relationships, be that parent, child or linked, to other items. Glass.Mapper easily works with these types of relationships. It not only supports them when retrieving items, but also while saving. Glass.Mapper even checks to make sure all related items exist before saving the item, essentially enforcing referential integrity.
Let’s say you have a person's data template that has a related education field. The related education field allows choosing one or more education records. The education data template has the school, year of graduation and degree. On a clean/empty content tree, you could use Glass.Mapper to first create and save the education records. Then, you can create and save the person related to the newly created education records. An example of this may be as follows:
One thing you may have noticed in this example is that the education record cannot really be reused between other people. The data is specific to the person. You could also say the education data is owned by the person. The education record really does not exist outside of the context of the person.
Another thing you may have noticed is that I didn’t specify the <parent> when saving the items. If an education record exists only within the context of the person, we could save the education record as a child item of the person. If the person node in the Sitecore content tree is deleted, the child education nodes will also be deleted.
If the education records are owned by the person (parent) and are considered part of the parent, could we treat them as one unit? Could Glass.Mapper handle saving everything in one call? Here is an example of how we could perform a save using Glass.Mapper:
Out of the box, Glass.Mapper does not support this approach. Let’s see how we can achieve this.
Glass.Mapper has flexible pipelines that allow you to inject and replace logic. We can use a custom pipeline task to achieve what we need.
First, we need to tell Glass.Mapper what relationship fields we should be treating as part of the parent. We can do this through a custom property attribute. This attribute can be applied to all required properties on the Glass objects. When a child item is considered part of the parent, it is sometimes referred to as a Composite Relationship. This attribute can be named SitecoreCompositeRelationshipAttribute.
Next, we need to figure out where to put our custom pipeline logic. The ObjectSavingFactory handles saving the objects in Glass.Mapper. Through trial and error, I discovered that I needed to save the child items before the parent is saved. Glass.Mapper ensures that all related items exist in the database before saving the parent item. Our custom task needs to run before the other ObjectSavingFactory tasks. To plug into the ObjectSavingFactory pipeline, we modify the CreateResolver method in the GlassMapperScCustom.cs class. This task will be inserted before the other tasks. We will name our task the CompositeRelationshipSavingTask.
Now, we will create the CompositeRelationshipSavingTask. All tasks that participate in the ObjectSavingFactory implement the IObjectSavingTask interface. The Execute method must be implemented. Within this method, we will find all properties that have the SitecoreCompositeRelationshipAttribute set. We will then retrieve the value from the attributed properties. The value should be an IEnumerable. We will enumerate each item, creating or saving each child item using Glass.Mapper. To promote consistency, all child items will be placed under a subfolder below the parent item. The subfolder will be named the same name as the Field name with which the property is associated. We will essentially have a subfolder for each Composite Relationship, where the subfolder matches the field name. Now that all items of a Composite Relationship field are in the same sub-folder, we can perform extra operations on the items in the subfolder, such as deleting the children that are no longer being referenced by the parent.
The GetCompositeRelationshipFields method needs to be implemented. It uses reflection to obtain the attributes on the configured properties. The results are cached in a static variable.
Give it a try… All attributed relationship fields will be saved along with the parent.
Glass.Mapper is very flexible. With this flexibility, we were able to add custom logic to the saved pipeline. This logic automatically saves any related item where the relationship field is marked with the SitecoreCompositeRelationshipAttribute.
Want to read more about Sitecore and Glass.Mapper? Check out Sitecore: Glass.Mapper Required Field Validator.