In part one of this series we added a new entity to the commerce engine and gave business users the ability to maintain this entity. The example we used was a “Brand” for instance Nike, Reebok or New Balance.

In this post we will look at how we can bring the instances of the Brand entity into Sitecore as items so we can use them as content. You can then use those items to drive Brand landing pages and other client requirements.

The following is covered in this post :-

  1. Creating two templates to support the data provider
  2. Creating a data provider to expose the Brand entity as content
  3. Adding the new data provider to the configuration

Future posts will build on this foundation including how to use the new content in an SXA component.

The end result will look like the image at the start of the post.

Step 1 – Create the templates

  • In Sitecore create two new templates
  • Take note of the ids

Create the template for the new Brands Folder, this will be the container for the new items…brands_template_folder
Create the template for the Brands themselves, this template should match the fields from the commerce entity (only has one field named Description at the moment)…brands_template

Step 2 – Create the data provider

The data provider follows the same pattern as the data provider that exposes the catalog and products as Sitecore items.

Create the project and add references

  • Create a new “Class Library” project named “Sitecore.Services.Examples.DataProvider”
  • Add reference to Newtonsoft.Json
  • Add reference to Sitecore.Commerce.Engine.Connect
  • Add reference Sitecore.Kernel

Add the configuration

  • Add new folder named “Config”
  • Add new .config file named “zzzzSitecore.Services.Examples.DataProvider.config”
  • Copy in the following markup into the file
  • Change the template ids to match the ids of your templates created earlier
<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
<sitecore role:require="Standalone or ContentDelivery or ContentManagement">
<dataProviders>
<brandProvider role:require="Standalone or ContentManagement" type="Sitecore.Services.Examples.DataProvider.ReadOnlyBrandDataProvider, Sitecore.Services.Examples.DataProvider">
<param desc="targetDatabaseName">master</param>
<param desc="brandTemplateId">{746AAD00-457F-4644-9F28-6C8758FB97C1}</param>
<param desc="brandFolderTemplateId">{85D5B99D-CA7C-45D9-B5C8-23F97FEF5A20}</param>
</brandProvider>
<brandProvider role:require="ContentDelivery" type="Sitecore.Services.Examples.DataProvider.ReadOnlyBrandDataProvider, Sitecore.Services.Examples.DataProvider">
<param desc="targetDatabaseName">master</param>
<param desc="brandTemplateId">{746AAD00-457F-4644-9F28-6C8758FB97C1}</param>
<param desc="brandFolderTemplateId">{85D5B99D-CA7C-45D9-B5C8-23F97FEF5A20}</param>
</brandProvider>
</dataProviders>
<databases>
<database role:require="Standalone or ContentManagement" id="master" singleInstance="true" type="Sitecore.Data.DefaultDatabase, Sitecore.Kernel">
<dataProviders>
<dataProvider patch:before="*[1]" ref="dataProviders/brandProvider" />
</dataProviders>
</database>
<database id="web" singleInstance="true" type="Sitecore.Data.DefaultDatabase, Sitecore.Kernel">
<dataProviders>
<dataProvider patch:before="*[1]" ref="dataProviders/brandProvider" />
</dataProviders>
</database>
</databases>
</sitecore>
</configuration>

Add the data provider code

  • Add a new class named “ReadOnlyBrandDataProvider”
  • Add the following code
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Sitecore.Collections;
using Sitecore.Commerce.Engine.Connect.DataProvider;
using Sitecore.Commerce.Engine.Connect.SitecoreDataProvider.Extensions;
using Sitecore.Configuration;
using Sitecore.Data;
using Sitecore.Data.DataProviders;
using Sitecore.Data.IDTables;
using Sitecore.Data.Items;
using Sitecore.Data.Managers;
using Sitecore.Diagnostics;
using Sitecore.Globalization;
namespace Sitecore.Services.Examples.DataProvider
{
public class ReadOnlyBrandDataProvider : Data.DataProviders.DataProvider
{
public string BrandTemplateId
{
get;
set;
}
public string BrandFolderTemplateId
{
get;
set;
}
public string Prefix => "CommerceBrands";
public Database ContentDb => Factory.GetDatabase(this.TargetDatabaseName);
public string TargetDatabaseName
{
get;
set;
}
public ReadOnlyBrandDataProvider(string targetDatabaseName, string brandTemplateId, string brandFolderTemplateId)
{
TargetDatabaseName = targetDatabaseName;
BrandTemplateId = brandTemplateId;
BrandFolderTemplateId = brandFolderTemplateId;
}
public override ItemDefinition GetItemDefinition(ID itemId, CallContext context)
{
ItemDefinition itemDefinition = null;
var key = string.Empty;
if (!GetEntityId(itemId, ref key))
return null;
var itemName = ItemUtil.ProposeValidItemName(key);
itemDefinition = new ItemDefinition(itemId, itemName, ID.Parse(BrandTemplateId), ID.Null);
return itemDefinition;
}
private bool GetEntityId(ID itemId, ref string key)
{
var idEntry = IDTable.GetKeys(Prefix, itemId);
if (idEntry.Any())
key = idEntry[0].Key;
return key.Contains("Entity-Brand-");
}
private bool TryGetParentId(ID itemId, ref ID parentId)
{
var idEntry = IDTable.GetKeys(Prefix, itemId);
if (idEntry.Any())
parentId = ID.Parse(idEntry[0].ParentID);
return parentId.IsNull;
}
public override FieldList GetItemFields(ItemDefinition item, VersionUri version, CallContext context)
{
FieldList fieldLists = null;
try
{
if (CanProcessItem(item))
{
var template = TemplateManager.GetTemplate(item.TemplateID, ContentDb);
if (template != null)
{
var key = string.Empty;
if (GetEntityId(item.ID, ref key))
{
var entity = GetEntity(key, version.Version.Number);
if (entity != null)
{
fieldLists = new FieldList();
var displayName = FieldIDs.DisplayName;
var displayNameValue = entity["DisplayName"]?.Value<string>();
fieldLists.Add(displayName, displayNameValue);
foreach (var templateField in template.GetFields().Where(ItemUtil.IsDataField))
{
var value = entity[templateField.Name]?.Value<string>();
fieldLists.Add(templateField.ID, value);
}
fieldLists.Add(FieldIDs.Created, entity.GetEntityValue("DateCreated"));
fieldLists.Add(FieldIDs.Updated, entity.GetEntityValue("DateUpdated"));
fieldLists.Add(FieldIDs.Security, "ar|Everyone|pe|+item:read|pd|+item:read|");
var createdBy = entity.GetEntityValue("CreatedBy");
var updatedBy = entity.GetEntityValue("UpdatedBy");
if (!string.IsNullOrEmpty(createdBy))
{
fieldLists.Add(FieldIDs.CreatedBy, createdBy);
fieldLists.Add(FieldIDs.Owner, createdBy);
if (string.IsNullOrEmpty(updatedBy))
{
updatedBy = createdBy;
}
}
if (!string.IsNullOrEmpty(updatedBy))
{
fieldLists.Add(FieldIDs.UpdatedBy, updatedBy);
}
}
}
else
{
Log.Error($"Could not find the combined entity ID for Item ID {item.ID} with template ID {item.TemplateID}", this);
}
}
}
}
catch (Exception exception)
{
var message =
$"There was an error in GetItemFields. ItemDefinition ID: {item.ID} Template ID: {item.TemplateID}.\r\nError StackTrace: {exception.StackTrace}";
Log.Error(message, this);
fieldLists = null;
}
return fieldLists;
}
private bool CanProcessItem(ItemDefinition item)
{
return item.TemplateID.ToString().Equals(BrandTemplateId, StringComparison.OrdinalIgnoreCase);
}
public override ID GetParentID(ItemDefinition itemDefinition, CallContext context)
{
if (!CanProcessItem(itemDefinition))
return null;
var key = ID.Null;
context.Abort();
TryGetParentId(itemDefinition.ID, ref key);
return key.IsNull ? null : key;
}
public bool CanProcessBrandParent(ItemDefinition parentItem)
{
return parentItem.TemplateID.ToString().Equals(BrandFolderTemplateId, StringComparison.OrdinalIgnoreCase);
}
public override IDList GetChildIDs(ItemDefinition parentItem, CallContext context)
{
if (!CanProcessBrandParent(parentItem))
return base.GetChildIDs(parentItem, context);
var childIdStrings = new List<string>();
var guid = parentItem.ID.Guid;
foreach (var listItem in GetListItems(guid.ToString(), "Brands",
"Sitecore.Services.Examples.Entities.Entities.Brand, Sitecore.Services.Examples.Entities", 1000))
{
childIdStrings.Add(listItem);
}
if (!childIdStrings.Any())
return base.GetChildIDs(parentItem, context);
var childIDs = new IDList();
foreach (var childIdString in childIdStrings)
{
childIDs.Add(ID.Parse(childIdString));
}
return childIDs;
}
public List<string> GetListItems(string parentId, string listName, string typeName, int take)
{
var ids = new List<string>();
var catalogRepository = new CatalogRepository();
var results = catalogRepository.InvokeHttpClientGet(
$"GetList(id='{listName}',type='{typeName}',skip=0,take={take})?$expand=Items($expand=Components($expand=ChildComponents($expand=ChildComponents)))");
if (string.IsNullOrEmpty(results))
return ids;
var jObjects = JsonConvert.DeserializeObject<JObject>(results, CatalogRepository.JsonSettings);
var value = (JArray)jObjects.GetValue("Items", StringComparison.OrdinalIgnoreCase);
foreach (var jTokens in value)
{
var item1 = jTokens["SitecoreId"];
var sitecoreId = item1?.Value<string>();
ids.Add(sitecoreId);
var id = jTokens["Id"]?.Value<string>();
var idEntry = IDTable.GetID(Prefix, id);
if (idEntry != null)
{
IDTable.RemoveKey(Prefix, id);
}
IDTable.Add(Prefix, id, ID.Parse(sitecoreId), ID.Parse(parentId));
}
return ids;
}
public JToken GetEntity(string mixId, int? version = null)
{
var executeString = string.Empty;
if (mixId.StartsWith("Entity-Brand-", StringComparison.OrdinalIgnoreCase))
{
var cleanedEntityId = mixId.Replace("Entity-Brand-", string.Empty);
executeString = $"Brands('{cleanedEntityId}')?$expand=*";
}
var versionStrings = new Dictionary<string, string>();
if (version.HasValue)
{
versionStrings.Add("EntityVersion", version.ToString());
}
var catalogRepository = new CatalogRepository("en");
var jsonResult = catalogRepository.InvokeHttpClientGet(executeString, false, true, null, versionStrings);
if (string.IsNullOrEmpty(jsonResult))
{
return null;
}
var entity = JsonConvert.DeserializeObject<JToken>(jsonResult, CatalogRepository.JsonSettings);
return entity;
}
public override LanguageCollection GetLanguages(CallContext context)
{
return null;
}
public override VersionUriList GetItemVersions(ItemDefinition item, CallContext context)
{
if (!CanProcessItem(item))
return null;
var versions = new VersionUriList {{Language.Current, Data.Version.First}};
return versions;
}
}
}

Your project should look like this…

brands_project

Step 3 – Deploy and test

Deploy…

  • Build the solution
  • Deploy the resultant “Sitecore.Services.Examples.DataProvider.dll” to your Sitecore bin directory
  • Deploy the config file “zzzzSitecore.Services.Examples.DataProvider.config” to the App_config folder for example “C:\inetpub\wwwroot\xp902.sc\App_Config\Include\Y.Commerce.Engine”
  • Go to your sites “showconfig.asp” and verify that your new data provider has been incorporated into your site. For example, “https://sxa.storefront.com/sitecore/admin/showconfig.aspx&#8221;

Test…

  • Open sitecore content editor and add a new item using the “Commerce Brand Folder” template. You should be able to do this anywhere but for example add it to “/sitecore/content/Brands” (see the banner image for an example)
  • When you expand the brands folder, the data provider should retrieve and cache the brands from the commerce engine and show them under the brands folder