Sitecore SXA:创建基于规则的片段呈现

阅读时间: 关于 7-10 minutes
用于: Sitecore 开发人员、高级开发人员和潜在客户
关键要点: 如何使用 Sitecore 规则引擎创建自定义呈现以动态显示片段内容。

在最近的一个 Commerce 项目中,需要在产品详细信息页面上显示产品特定的内容。这可能是品牌提供的图片、额外的细节,或者某种广告或特殊广告。由于产品数据是动态显示的,我需要想出一种动态方式来处理这个问题。

我首先研究了个性化。虽然这可能有效,但最佳做法是仅使用它来显示基于客户行为的内容。当将访问者规则和内容规则混合在一起时,规则也会很快变得混乱。然而,规则引擎是一个强大的工具,所以我决定通过新的自定义渲染来利用它来满足要求。

创建基于规则的代码段

我决定使用片段作为起点。我基于代码段渲染为内容作者提供了他们可以添加的内容的灵活性。它允许他们使用多个渲染添加任何内容,从简单的图像渲染到详细的产品信息。

我首先克隆了 Snippet 渲染,并在数据源项上仅添加了一个额外的规则字段。这个想法是使用规则引擎根据显示的产品的一组条件选择一个片段数据项。前提相当简单,我只需要根据规则引擎的结果更改数据源项。

GetXmlBasedLayoutDefinition 管道

When a snippet is rendered it’s xml layout definition gets injected into the page items layout definition. To do this the data source item is resolved and its layout definition is read and injected. This occurs in the Sitecore.XA.Feature.Composites.Pipelines.GetXmlBasedLayoutDefinition.InjectCompositeComponents. Here I overrode the ResolveCompositeDatasource method to resolve the data based on the rules field rather than the rendering’s data source field.

var datasourceItem = Context.Database.GetItem(datasourceId);
if(datasourceItem.TemplateID == Templates.RulesBasedSnippetSnippet.ID)
{
    var rulesBasedDatasource = rulesBasedSnippetRepository.GetRulesBasedSnippetDataSource(datasourceItem, contextItem);
    return rulesBasedDatasource ?? datasourceItem;
}
else
{
    return datasourceItem;
}

更改很简单,如果数据源项是基于规则的片段,则从规则引擎获取该项,否则返回原始数据源项。我将在下一节中介绍规则引擎代码。

There is another change to the pipeline that needs to be considered. The resulting layout definition xml is stored in the dictionary cache based on the current context item id. This needed to be adjusted since I needed to return different results for the same page. So I overrode the CreateCompositesXmlCacheKey method to return a different cache key with the url appended only for rules based snippets.

var pagePath = args.PageContext.RequestContext.HttpContext.Request.Path;
if (currentItem.Name == "*" && renderingIds.Any(r => r == Renderings.RulesBasedSnippet.ID))
{
    return $"SXA::{Constants.CompositesXmlPropertiesKey}::{siteItem.ID}::{Context.Database.Name}::{Context.Device.ID}::{Context.Language.Name}::{currentItem.ID}::{pagePath}";
}
else
{
    return $"SXA::{Constants.CompositesXmlPropertiesKey}::{siteItem.ID}::{Context.Database.Name}::{Context.Device.ID}::{Context.Language.Name}::{currentItem.ID}";
}

解析数据源项

最后,我需要使用规则引擎解析数据源项。让我们从下面的代码片段开始:

var commerceContextItem = siteContext.CurrentCatalogItem ?? contextItem;
var rules = RuleFactory.ParseRules(contextItem.Database, XElement.Parse(rulesBasedSnippetSnippetItem[Templates.RulesBasedSnippetSnippet.Fields.SnippetRules]));
var ruleContext = new RuleContext()
{
    Item = commerceContextItem
};

if (rules.Rules.Any())
{
    foreach (var rule in rules.Rules)
    {
        if (rule.Condition != null)
        {
            var stack = new RuleStack();
            rule.Condition.Evaluate(ruleContext, stack);

            if (ruleContext.IsAborted)
            {
                continue;
            }
            if ((stack.Count != 0) && ((bool)stack.Pop()))
            {
                rule.Execute(ruleContext);
                var action = rule.Actions.FirstOrDefault();
                var snippetId = action is SelectSnippet ? ((SelectSnippet)action)?.SnippetId : string.Empty;
                return !string.IsNullOrEmpty(snippetId) ? contextItem.Database.GetItem(ID.Parse(snippetId)) : null;
            }
        }
        else
        {
            rule.Execute(ruleContext);
        }
    }
}

return null;

首先,我使用 RuleFactory 解析器解析了规则字段 xml。然后,由于我需要针对产品项目执行规则引擎,因此我从商业站点上下文中检索了当前目录项目。然后我将其用作规则引擎的上下文项。最后,我循环并评估每个规则,规则引擎返回最终结果。

SelectSnippet is a custom RuleAction that returns the the selected snippet id from the resolved rule. The SelectSnippetRuleContext is set using a custom macro for setting the snippet. I won’t go into details on creating the macro in this post. However you can find tons of resources for creating custom macros online.

public class SelectSnippet : RuleAction where T : RuleContext
{
    public string SnippetId { get; set; }

    public override void Apply(T ruleContext)
    {
        SelectSnippetRuleContext dataSourceRuleContext = ruleContext as SelectSnippetRuleContext;
        ID result;
        if (dataSourceRuleContext == null || !ID.TryParse(SnippetId, out result))
        {
            return;
        }
        dataSourceRuleContext.SnippetId = result;
    }
}

包起来

到目前为止,该解决方案对我们的客户来说效果很好。目前尚不清楚随着规则条件数量的增加,此解决方案是否会引起任何性能问题。到目前为止,它并没有给我们带来十几个规则的任何问题。一个积极因素是它可以与个性化结合使用。因此,它允许根据客户行为选择不同的规则集。

我希望您发现这些信息很有用。如果您有任何问题,请随时发表评论。