Custom facets in the marketing overview
Describes facets and how to customize facet groups in Optimizely Customized Commerce.
Many ecommerce sites have a large number of campaigns and promotions running. Facets in Optimizely Customized Commerce let users apply filters to campaigns and discounts (promotions), for easier location of specific items in the Campaign view.Â
Key components mentioned here are available in the EPiServer.Commerce.Shell.Facets namespace.
FacetGroup
FacetGroup contains properties that identify a group of items by which to filter a campaign and settings to configure a group. Initialize a FacetGroup through a constructor.
public FacetGroup(string id, string name, IEnumerable<FacetItem> items, FacetGroupSettings settings)
{
Id = id; // the id of facet group
Name = name; // the name of facet group
Items = new List<FacetItem>(items); // the list facet item
Settings = settings; // settings to configuration a facet group
}
FacetGroup settings
FacetGroupSettings contains properties to configure a facet group. Initialize these properties through a constructor.
public FacetGroupSettings(
FacetSelectionType selectionType,
int itemsToShow,
bool collapsible,
bool hasIcons,
bool showMatchingItems,
IEnumerable<string> dependsOn)
{
SelectionType = selectionType; // Determine the selection type of facet group, single or multiple through FacetSelectionType enum
ItemsToShow = itemsToShow; // The number to determine how many facet items will be show as default. If there are more than facet items, show more option will be available
Collapsible = collapsible; // Determine the facet group is collapsible or not
HasIcons = hasIcons; // Determine the icon of facet items are displayed or not
ShowMatchingItems = showMatchingItems; // Determine the number of matching filtered items are shown or not
DependsOn = dependsOn; // List of facet group that current group depends on. That mean changes the list facet group could affect the number of matching filterd items in the current group
}
FacetItem
FacetItem contains properties that identify a facet item in the facet group, which is a value to a filtered campaign. Initialize these properties through a constructor.
public FacetItem(string id, string name, string iconClass = "")
{
Id = id; // the id of facet item that display in the url as a value
Name = name; // the name of facet item that shown in the facets widget
IconClass = iconClass; // the css class that determining the icon of facet item. Default it's empty
}
Custom facets Customized Commerce 12.10 and higher
To customize facet groups, create a class inheriting from FacetGroupModifier, then register it on one of your initialization modules.
Example: Displaying four markets by default in the market facet group, instead of three.
public class CustomFacetGroupModifier : FacetGroupModifier
{
public override IEnumerable<FacetGroup> ModifyFacetGroups(IEnumerable<FacetGroup> facetGroups)
{
var marketFacetGroup = facetGroups.FirstOrDefault(f => f.Id == CampaignFacetConstants.MarketGroupId);
if (marketFacetGroup != null)
{
marketFacetGroup.Settings.ItemsToShow = 4;
}
return facetGroups;
}
}
Example: Registering the facet.
public void ConfigureContainer(ServiceConfigurationContext context)
{
var services = context.Services;
services.AddSingleton<FacetGroupModifier, CustomFacetGroupModifier>();
}
Custom facets Customized Commerce 12.9 and lower
First, you need a custom facet based on a campaign facet. Here we create a custom facet that:
- Clears all built-in groups.
- Has a facet group that filters by a campaign's last modified date. This user can choose last modified day, week, or month.
public class CustomFacet : CampaignFacet
{
public CustomFacet(
FacetFactory facetFactory,
IMarketService marketService,
LocalizationService localizationService
) : base(facetFactory, marketService, localizationService)
{
// Clear all built-in facet groups
Groups.Clear();
Groups.Add(facetFactory.CreateFacetGroup(
"lastmodified",
"Last Modified",
new List()
{
facetFactory.CreateFacetItem("day", "Today"),
facetFactory.CreateFacetItem("week", "A Week Ago"),
facetFactory.CreateFacetItem("month", "A Month Ago")
},
new FacetGroupSettings(FacetSelectionType.Single, 0, true, true, false, Enumerable.Empty<string>())));
}
}
This example shows that the last modified facet group is a single selection with three options: day, week, or month.
new FacetGroupSettings(FacetSelectionType.Single, 0, true, true, true /*showMatchingItems*/, Enumerable.Empty<string>())
Note
To show a item numbers that indicate how many campaigns are filtered, in FacetGroupSettings, set showMatchingItemsto true.
To use a CustomFacet, follow these steps.
- Create a rest store named "customfacet" that returns a custom facet.
[RestStore("customfacet")]
public class CustomFacetStore : RestControllerBase
{
FacetFactory _facetFactory;
IMarketService _marketService;
LocalizationService _localizationService;
public CustomFacetStore(
FacetFactory facetFactory,
LocalizationService localizationService,
IMarketService marketService)
{
_facetFactory = facetFactory;
_marketService = marketService;
_localizationService = localizationService;
}
public RestResult Get(string id, string facetString, ContentReference parentLink)
{
return Rest(new CustomFacet(_facetFactory, _marketService, _localizationService));
}
}
Note
To show matching items, use the facet query handler to calculate matching numbers.
public RestResult Get(string id, string facetString, ContentReference parentLink)
{
var customFacet = new CustomFacet(_facetFactory, _marketService, _localizationService);
var contentLoader = ServiceLocator.Current.GetInstance();
var facetQueryHandler = new FacetQueryHandler();
facetQueryHandler.CalculateMatchingNumbers(
contentLoader.GetChildren(parentLink),
customFacet.Groups,
facetString,
new GetContentsByFacet[] { new GetCampaignsByLastModified() });
return Rest(customFacet);
}
- Register the custom facet store and override the existing store "epi.commerce.facet". Create a module initializer. If an initializer exists, edit it.
define(
[
"dojo/_base/declare",
"epi/_Module",
"epi/routes"
],
function (declare, _Module, routes)
{
return declare([_Module],
{
initialize: function ()
{
this.inherited(arguments);
var registry = this.resolveDependency("epi.storeregistry");
// remove existing facet store
if (registry.get("epi.commerce.facet"))
{
delete registry._stores["epi.commerce.facet"];
}
// register the custom facet
registry.create("epi.commerce.facet", routes.getRestPath(
{ moduleArea: "app", storeName: "customfacet" } ));
}
}
);
}
);
- If you do not register a module initializer, you need to update or create the project's module.config file, as shown in the example below.
<?xml version="1.0" encoding="utf-8"?>
<module loadFromBin="false">
<assemblies>
<add assembly="OptimizelySite5" />
</assemblies>
<dojo>
<paths>
<add name="app" path="Scripts" />
</paths>
</dojo>
<clientModule initializer="app.ModuleInitializer">
<moduleDependencies>
<add dependency="Commerce" type="RunAfter" />
</moduleDependencies>
</clientModule>
</module>
After following the steps above, the Campaign view has a custom facet on the left side, and the built-in groups have disappeared. We can interact in the UI, but still need to filter campaigns.
- Create a function to filter campaigns by last modified date, based on GetContentsByFacet.
public class GetCampaignsByLastModified : GetContentsByFacet
{
public override string Key { get { return "lastmodified"; } }
public override IEnumerable<IContent> GetItems(IEnumerable<IContent> items, IEnumerable<string> facets)
{
return items.Where(item => !(item is SalesCampaign) || AvailableFor((SalesCampaign)item, facets));
}
private bool AvailableFor(SalesCampaign item, IEnumerable<string> facets)
{
var changed = DateTime.UtcNow - item.Changed;
var filterString = facets.FirstOrDefault();
var isAvailable = false;
switch (filterString)
{
case "day":
isAvailable = changed.Days < 1;
break;
case "week":
isAvailable = changed.Days < 7;
break;
case "month":
isAvailable = changed.Days < 30;
break;
}
return isAvailable;
}
}
- Customize the query to return GetCampaignsByLastModified, by overriding GetSalesCampaignChildrenQuery.Â
[ServiceConfiguration(typeof(IContentQuery))]
public class CustomGetSalesCampaignChildrenQuery : GetSalesCampaignChildrenQuery
{
public CustomGetSalesCampaignChildrenQuery(
IContentQueryHelper queryHelper,
IContentRepository contentRepository,
LanguageSelectorFactory languageSelectorFactory,
CampaignInfoExtractor campaignInfoExtractor,
FacetQueryHandler facetQueryHandler)
: base(queryHelper, contentRepository, languageSelectorFactory, campaignInfoExtractor, facetQueryHandler){}
public override int Rank
{
// need set rank to higher
get { return 1000; }
}
protected override IEnumerable<GetContentsByFacet> FacetFunctions
{
get
{
return new GetContentsByFacet[]
{
new GetCampaignsByLastModified()
};
}
}
}
Here is an example of the customized Campaign view.
Updated 8 months ago