You can get a list of Entity IDs, instead of GUIDs, and then retrieve Catalog items by Entity Id.
ICreateRelationshipPipeline
and IDeleteRelationshipPipeline
use UpdateCatalogHierarchyBlock
for control catalog entity relationships. All these Catalog item relations Sitecore Commerce stores in CatalogItemBase
namespace Sitecore.Commerce.Plugin.Catalog
{
/// <summary>Represents the base class for all catalog items</summary>
/// <seealso cref="T:Sitecore.Commerce.Core.CommerceEntity" />
public class CatalogItemBase : CommerceEntity
{
/// <summary>Gets or sets the sitecore identifier.</summary>
/// <value>The sitecore identifier.</value>
public string SitecoreId { get; set; }
/// <summary>
/// Gets or sets the Sitecore IDs of the catalogs directly related to this entity (i.e. CatalogToCategory or CatalogToSellableItem relationship).
/// </summary>
public string CatalogToEntityList { get; set; }
/// <summary>Gets or sets the parent catalogs.</summary>
/// <value>The parent catalogs.</value>
public string ParentCatalogList { get; set; }
/// <summary>Gets or sets the parent categories.</summary>
/// <value>The parent categories.</value>
public string ParentCategoryList { get; set; }
/// <summary>Gets or sets the child categories.</summary>
/// <value>The child categories.</value>
public string ChildrenCategoryList { get; set; }
/// <summary>Gets or sets the child sellable items.</summary>
/// <value>The child sellable items.</value>
public string ChildrenSellableItemList { get; set; }
/// <summary>Gets or sets the item variations.</summary>
/// <value>The item variations.</value>
public string ItemVariations { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is bundle.
/// </summary>
/// <value>
/// <c>true</c> if this instance is bundle; otherwise, <c>false</c>.
/// </value>
public bool IsBundle { get; set; }
}
}
In your case, you can create a new ExtendedCatalogItemComponent
:
public class ExtendedCatalogItemComponent : Component
{
public string ParentCatalogEntitiesList { get; set; }
public string ParentCategoryEntitiesList { get; set; }
public string ChildrenCategoryEntitiesList { get; set; }
public string ChildrenSellableItemEntitiesList { get; set; }
}
Create UpdateCatalogCustomHierarchyBlock
, which will update catalog item relationships, when new relation will be created or deleted.
public class UpdateCatalogCustomHierarchyBlock : PipelineBlock<RelationshipArgument, RelationshipArgument, CommercePipelineExecutionContext>
{
private const string CatalogToCategory = "CatalogToCategory";
private const string CategoryToCategory = "CategoryToCategory";
private const string CatalogToSellableItem = "CatalogToSellableItem";
private const string CategoryToSellableItem = "CategoryToSellableItem";
private readonly IFindEntityPipeline _findEntityPipeline;
private readonly IPersistEntityPipeline _persistEntityPipeline;
/// <summary>Initializes a new instance of the <see cref="UpdateCatalogCustomHierarchyBlock"/> class.</summary>
/// <param name="findEntityPipeline">The find entity pipeline.</param>
/// <param name="persistEntityPipeline">The persist entity pipeline.</param>
public UpdateCatalogCustomHierarchyBlock(
IFindEntityPipeline findEntityPipeline,
IPersistEntityPipeline persistEntityPipeline)
{
this._findEntityPipeline = findEntityPipeline;
this._persistEntityPipeline = persistEntityPipeline;
}
/// <summary>Runs the specified argument.</summary>
/// <param name="arg">The argument.</param>
/// <param name="context">The context.</param>
/// <returns></returns>
public override async Task<RelationshipArgument> Run(
RelationshipArgument arg,
CommercePipelineExecutionContext context)
{
Condition.Requires(arg).IsNotNull(this.Name + ": The argument can not be null");
Condition.Requires(arg.TargetName).IsNotNullOrEmpty(this.Name + ": The target name can not be null or empty");
Condition.Requires(arg.SourceName).IsNotNullOrEmpty(this.Name + ": The source name can not be null or empty");
Condition.Requires(arg.RelationshipType).IsNotNullOrEmpty(this.Name + ": The relationship type can not be null or empty");
if (!((IEnumerable<string>)new string[4]
{
CatalogToCategory,
CatalogToSellableItem,
CategoryToCategory,
CategoryToSellableItem
})
.Contains(arg.RelationshipType, StringComparer.OrdinalIgnoreCase))
{
return arg;
}
var source = await this._findEntityPipeline.Run(new FindEntityArgument(typeof(CatalogItemBase), arg.SourceName, false), context) as CatalogItemBase;
var entityIds = new List<string>();
if (arg.TargetName.Contains("|"))
{
var strArray = arg.TargetName.Split('|');
entityIds.AddRange(strArray);
}
else
{
entityIds.Add(arg.TargetName);
}
var sourceChanged = new ValueWrapper<bool>(false);
if (!source.HasComponent<ExtendedCatalogItemComponent>())
{
source.SetComponent(new ExtendedCatalogItemComponent());
}
var sourceComponent = source.GetComponent<ExtendedCatalogItemComponent>();
foreach (var entityId in entityIds)
{
var catalogItemBase = await this._findEntityPipeline.Run(new FindEntityArgument(typeof(CatalogItemBase), entityId, false), context) as CatalogItemBase;
if (catalogItemBase != null)
{
if (!catalogItemBase.HasComponent<ExtendedCatalogItemComponent>())
{
catalogItemBase.SetComponent(new ExtendedCatalogItemComponent());
}
var catalogItemBaseComponent = catalogItemBase.GetComponent<ExtendedCatalogItemComponent>();
var changed = new ValueWrapper<bool>(false);
if (arg.RelationshipType.Equals(CatalogToCategory, StringComparison.OrdinalIgnoreCase))
{
sourceComponent.ChildrenCategoryEntitiesList = this.UpdateHierarchy(arg, catalogItemBase.Id, sourceComponent.ChildrenCategoryEntitiesList, sourceChanged);
catalogItemBaseComponent.ParentCatalogEntitiesList = this.UpdateHierarchy(arg, source.Id, catalogItemBaseComponent.ParentCatalogEntitiesList, changed);
}
else if (arg.RelationshipType.Equals(CategoryToCategory, StringComparison.OrdinalIgnoreCase))
{
sourceComponent.ChildrenCategoryEntitiesList = this.UpdateHierarchy(arg, catalogItemBase.Id, sourceComponent.ChildrenCategoryEntitiesList, sourceChanged);
catalogItemBaseComponent.ParentCategoryEntitiesList = this.UpdateHierarchy(arg, source.Id, catalogItemBaseComponent.ParentCategoryEntitiesList, changed);
catalogItemBaseComponent.ParentCatalogEntitiesList = this.UpdateHierarchy(arg, this.ExtractCatalogId(source.Id), catalogItemBaseComponent.ParentCatalogEntitiesList, changed);
}
else if (arg.RelationshipType.Equals(CatalogToSellableItem, StringComparison.OrdinalIgnoreCase))
{
sourceComponent.ChildrenSellableItemEntitiesList = this.UpdateHierarchy(arg, catalogItemBase.Id, sourceComponent.ChildrenSellableItemEntitiesList, sourceChanged);
catalogItemBaseComponent.ParentCatalogEntitiesList = this.UpdateHierarchy(arg, source.Id, catalogItemBaseComponent.ParentCatalogEntitiesList, changed);
}
else if (arg.RelationshipType.Equals(CategoryToSellableItem, StringComparison.OrdinalIgnoreCase))
{
sourceComponent.ChildrenSellableItemEntitiesList = this.UpdateHierarchy(arg, catalogItemBase.Id, sourceComponent.ChildrenSellableItemEntitiesList, sourceChanged);
catalogItemBaseComponent.ParentCategoryEntitiesList = this.UpdateHierarchy(arg, source.Id, catalogItemBaseComponent.ParentCategoryEntitiesList, changed);
catalogItemBaseComponent.ParentCatalogEntitiesList = this.UpdateHierarchy(arg, this.ExtractCatalogId(source.Id), catalogItemBaseComponent.ParentCatalogEntitiesList, changed);
}
if (changed.Value)
{
await this._persistEntityPipeline.Run(new PersistEntityArgument(catalogItemBase), context);
}
}
}
if (sourceChanged.Value)
{
await this._persistEntityPipeline.Run(new PersistEntityArgument(source), context);
}
return arg;
}
/// <summary>Extracts the catalog identifier.</summary>
/// <param name="id">The identifier.</param>
/// <returns></returns>
private string ExtractCatalogId(string id)
{
var strArray = id.Split(new[] { "-" }, StringSplitOptions.RemoveEmptyEntries);
return strArray.Length < 3 ? string.Empty : CommerceEntity.IdPrefix<Catalog.Catalog>() + strArray[2];
}
/// <summary>Updates the hierarchy.</summary>
/// <param name="arg">The argument.</param>
/// <param name="targetId">The target identifier.</param>
/// <param name="rawChildren">The raw children.</param>
/// <param name="changed">The changed.</param>
/// <returns></returns>
private string UpdateHierarchy(
RelationshipArgument arg,
string targetId,
string rawChildren,
ValueWrapper<bool> changed)
{
if (rawChildren == null)
rawChildren = string.Empty;
var childIdList = rawChildren.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries).ToList();
if (arg.Mode.GetValueOrDefault() == RelationshipMode.Create & arg.Mode.HasValue && !childIdList.Contains(targetId))
{
if (!changed.Value)
{
changed.Value = true;
}
childIdList.RemoveAll(c => c.Equals(targetId, StringComparison.OrdinalIgnoreCase));
childIdList.Add(targetId);
}
else
{
if (arg.Mode.GetValueOrDefault() == RelationshipMode.Delete & arg.Mode.HasValue && childIdList.Contains(targetId))
{
if (!changed.Value)
{
changed.Value = true;
}
childIdList.RemoveAll(c => c.Equals(targetId, StringComparison.OrdinalIgnoreCase));
}
}
return string.Join("|", childIdList);
}
}
Register UpdateCatalogCustomHierarchyBlock
in ConfigureSitecore.cs
public void ConfigureServices(IServiceCollection services)
{
var assembly = Assembly.GetExecutingAssembly();
services.RegisterAllPipelineBlocks(assembly);
services.Sitecore().Pipelines(
p => p
.ConfigurePipeline<ICreateRelationshipPipeline>(
configure =>
{
configure.Add<UpdateCatalogCustomHierarchyBlock>().After<UpdateCatalogHierarchyBlock>();
})
.ConfigurePipeline<IDeleteRelationshipPipeline>(
configure =>
{
configure.Add<UpdateCatalogCustomHierarchyBlock>().After<UpdateCatalogHierarchyBlock>();
}));
services.RegisterAllCommands(assembly);
}
NOTE:
My solution works only for catalog/catalog items which was created from scratch after you added and registered my source code in Sitecore Commerce Engine. When you import Habitat catalog it will not create catalog item relationships programmatically, because they already exist in Habitat.zip
catalog. So you need to clean-up Catalog/Category/SellableItem entities and then recreate your catalog(s)
After all these changes you will able to retrieve a list of SellableItems from the ExtendedCatalogItemComponent
, which will be added when you will create a new one catalog relationship.