Glass Mapper handles mapping of almost all Sitecore field types to your models, but I discovered one Sitecore field type that Glass Mapper can handle that isn’t well documented: the Rules
field type. If you haven’t worked with the Sitecore Rules
field type before, Jeff Darchuk has an excellent blog post about the Sitecore rules API that I recommend reading.
In this post I’ll show you how you can use Glass Mapper to map your Rules
fields to your models in code.
Add a Rules Field to Your Template
Add a field of type Rules
to one of your templates as I’ve done above. The Source
field takes two parameters: rulespath
and hideactions
.
For the rulespath
parameter, you specify the path or item ID of one of the Rules Context Folders
found at /sitecore/system/Settings/Rules
. If you do not specify a rulespath
, your Rules
field will not have any conditions or actions to select.
The hideactions
parameter is optional, and if you specify hideactions=true
, then only conditions will be displayed in your Rules
field; actions will be hidden.
For example, if you only want the conditions (and not the actions) from the Device Detection Rules Context Folder
, you would specify rulespath=/sitecore/system/Settings/Rules/Device Detection&hideactions=true
in the Source
field.
You can easily create your own Rules Context Folder
by duplicating one of the folders found at /sitecore/system/Settings/Rules
. The Default
item nested under the Tags
item under each Rules Context Folder
has a Tags
field that contains a list of all rules tags associated with that Rules Context Folder
. You can customize what rules will show up in your Rules
field by adding/removing tags from this Tags
field.
Add a Rules Property to your Model Class
The next step is to add a new property to your model class for your Rules
field, but there are a few things to note before adding the property. Glass Mapper maps Rules
fields to the generic RuleList<T>
class; there is no non-generic RuleListRuleList<T>
class takes a RuleContext
as its type parameter. In the Sitecore.Kernel
assembly, there are six different classes that extend RuleContext
:
ConditionalRenderingsRuleContext
ContentEditorWarningsRuleContext
DeviceRuleContext
InsertOptionsRuleContext
RulesElementVisibilityRuleContext
ValidatorsRuleContext
The Sitecore.Analytics
and Sitecore.ContentSearch
assemblies also have classes that extend RuleContext
, which you can explore yourself with JetBrains dotPeek. To execute your rules from your model, you must make sure that your RuleList<T>
property is specified with the correct RuleContext
type parameter, otherwise some or all of the conditions for your Rules
field will not load. For example, if you specify your property as RuleList<RuleContext>
and some of the rules in your field require DeviceRuleContext
, the condition for those rules won’t load and those rules will not yield correct results. Jeff Darchuk covers this in his blog post as well.
For this post I will be demonstrating a device rule, so I’ve added the TileRules
property as type RuleList<DeviceRuleContext>
to my model class as seen below:
using Glass.Mapper.Sc.Configuration.Attributes; using Glass.Mapper.Sc.Fields; using Sitecore.Rules; using Sitecore.Rules.Devices; namespace SitecoreDemo { [SitecoreType(TemplateName = "Tile", AutoMap = true)] public interface ITile { string Heading { get; set; } string Body { get; set; } Link ButtonUrl { get; set; } RuleList<DeviceRuleContext> TileRules { get; set; } } }
I’m using interfaces for my models, but this will work just as well with concrete classes.
Add a Rule to Your Item in Sitecore
Here I’ve added one rule to my TileRules
field to check if the query string is demo=true
. I didn’t add any actions, because I’ll take my own action in my controller based on the results of the field.
Use the Rules
Here is an extension method I wrote for the RuleList<T>
type to evaluate all of the rules in a rule list and return true
if any of them evaluate to true
; otherwise return false
.
using System; using Sitecore.Rules; namespace SitecoreDemo { public static class RuleListExtensions { public static bool EvaluateRules<T>(this RuleList<T> ruleList, T ruleContext) where T : RuleContext { if (ruleList == null) throw new ArgumentNullException(nameof(ruleList)); if (ruleContext == null) throw new ArgumentNullException(nameof(ruleContext)); if (ruleContext.IsAborted || ruleList.Count == 0) return false; foreach (var rule in ruleList.Rules) { if (rule.Condition == null) continue; var result = rule.Evaluate(ruleContext); if (ruleContext.IsAborted) return false; if (result) return true; } return false; } } }
In my controller, I first create the DeviceRuleContext
from Sitecore.Context.Device
and the controller’s HttpContext
property. I then pass the DeviceRuleContext
into the extension method above to evaluate the rules in my tile’s TileRules
field. I set the header of the tile component to The rules are True!
if the rules evaluate to true
, and The rules are False!
if the rules evaluate to false
.
using System.Web.Mvc; using Glass.Mapper.Sc.Web.Mvc; using Sitecore.Rules.Devices; namespace SitecoreDemo.Controllers { public class TileController : GlassController<ITile> { public ActionResult Tile() { var ruleContext = new DeviceRuleContext(Sitecore.Context.Device, HttpContext); var ruleResult = DataSource.TileRules.EvaluateRules(ruleContext); var model = new TileViewModel { Heading = $"The rules are {ruleResult}!", Body = DataSource.Body, ButtonText = DataSource.ButtonUrl.Text, ButtonUrl = DataSource.ButtonUrl.Url }; return View(model); } } }
Now when I navigate to a page with my Tile components, the header is The rules are False!
when there is no query string.
When I navigate to a page with demo=true
as the query string, the Tile item that I added the rule to has The rules are True!
as its header.
Final Thoughts
Sitecore’s rule engine lets you do a lot of cool stuff in your projects and Glass Mapper removes a lot of the ceremony involved in working with rules. Let me know your thoughts or how you use rules in the comments.
It worked because u have one rule, isn’t it better to change your extension method to return true if all the rules have been evaluated correctly?
That will just depend on your business requirements. Returning
true
fromEvaluateRules
only when all rules evaluate totrue
is certainly a valid approach as well.