Template dependent field validator with Sitecore Rules Engine

I got a requirement that I need to create more data templates with the same fields but the fields should have different validations based on the template. So I decided to create a base template and inherit the other templates from this. Now, how can I assign different validators for the fields based on the template?

Found out give a try for Sitecore Rules Engine. By default it provides rules and validators which I exactly need. Let’s setup my rules!

Navigation Level 1 rule:
level1

Navigation Level 3 rule:
level3

The rules actually are, that the Link field is required for Navigation Level 1 and Navigation Level 3 but not for Navigation Level 2.
To activate the Validator Rules for my template, I need use the /sitecore/system/Settings/Validation Rules/Item Rules/Rules/Validation Rules item validator on the standard values on the base template.
item-validation

So cool, I almost found what I need, the problem now that as it is an Item Rule it is calling the validation on item save.

item-save
I want to make it more user friendly. Run the validation and show the error message when the user changing that particular field value.
To make this I found out why I can’t use the same “Rule Runner” validation on the field validation? To do this I needed to copy the /sitecore/system/Settings/Validation Rules/Item Rules/Rules/Validation Rules item validator into the Field Rules root folder like on the following screenshot.
field-rule

Let’s setup the Base Navigation Item template’s Link field to use the /sitecore/system/Settings/Validation Rules/Field Rules/Rules/Validation Rules validation.

field-validation

Voilà! Sitecore calls the same method and executing the rules what I set up in the Validation Rules!

item-field-validation

I thought that it is enough to reuse the /sitecore/system/Settings/Validation Rules/Item Rules/Rules/Validation Rule item from Sitecore, but it isn’t. The problem with that, it is only checking the saved item values and does not know anything about the field itself. So I had to implement some additional things.

At first I needed to implement my custom field validator which is validating on field level.

using System;
using System.Runtime.Serialization;
using Sitecore.Common;
using Sitecore.Data.Items;
using Sitecore.Data.Validators;
using Sitecore.Rules;
using Sitecore.SecurityModel;
using MyProject.Foundation.Validation.Consts;
using MyProject.Foundation.Validation.Rules.Contexts;
namespace MyProject.Foundation.Validation.Validators.Fields.ValidationRules
{
[Serializable]
public class ValidationRulesValidator : StandardValidator
{
public ValidationRulesValidator(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
public ValidationRulesValidator()
{
}
public override string Name
{
get { return "Validation Rules"; }
}
protected override ValidatorResult Evaluate()
{
var item = GetItem();
var field = GetField();
if (item == null || field == null)
{
return ValidatorResult.Valid;
}
Item rulesItem;
using (new SecurityDisabler())
{
rulesItem = field.Database.GetItem(SitecoreIds.FieldValidationRules.ToID());
}
if (rulesItem == null)
{
return ValidatorResult.Valid;
}
var validatorsRuleContext = new FieldValidatorsRuleContext
{
Item = item,
FieldValue = GetControlValidationValue(),
FieldId = field.ID,
Validator = this,
Result = ValidatorResult.Valid,
Text = string.Empty
};
var ruleContext = validatorsRuleContext;
var rules = RuleFactory.GetRules<FieldValidatorsRuleContext>(rulesItem, "Rule");
if (rules == null)
{
return ValidatorResult.Valid;
}
rules.RunFirstMatching(ruleContext);
Text = GetText(ruleContext.Text);
return ruleContext.Result == ValidatorResult.Valid ? ValidatorResult.Valid : GetFailedResult(ruleContext.Result);
}
protected override ValidatorResult GetMaxValidatorResult()
{
return GetFailedResult(ValidatorResult.Error);
}
}
}

As you can see this implementation needs a FieldValidatorsRuleContext which is a really dump class and containing the following properties.

using Sitecore.Data;
using Sitecore.Rules.Validators;
namespace MyProject.Foundation.Validation.Rules.Contexts
{
public class FieldValidatorsRuleContext : ValidatorsRuleContext
{
public ID FieldId { get; set; }
public string FieldValue { get; set; }
}
}

I needed to use this implementation in my new Field Validator.

field-validator

Then use this Field Validator on the field of the template then it is running the rules under /sitecore/system/Settings/Rules/MyProject/Foundation/Validation/Field Validation Rules/Rules item.

Unfortunately I also needed to implement my own rule which is checking the length of the value. The implementation is the following.

using Sitecore.Data;
using Sitecore.Rules.Conditions;
using MyProject.Foundation.Validation.Rules.Contexts;
namespace MyProject.Foundation.Validation.Rules.Conditions
{
public class FieldMaxLengthCondition<T> : IntegerComparisonCondition<T> where T : FieldValidatorsRuleContext
{
public string FieldId { get; set; }
protected override bool Execute(T ruleContext)
{
if (ID.Parse(FieldId) != ruleContext.FieldId)
{
return false;
}
var length = string.IsNullOrWhiteSpace(ruleContext.FieldValue) ? 0 : ruleContext.FieldValue.Length;
return BaseCompare(length);
}
public virtual bool BaseCompare(int value)
{
return Compare(value);
}
}
}

The most important part and the main difference is that it is checking the Field ID. And only runs the validation if the Field ID is matching. In Sitecore it looks like the following.

field-length

2 thoughts on “Template dependent field validator with Sitecore Rules Engine

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s