3

I am trying to get a list of SellableItem objects belonging to the same category using ServiceProxy.

Currently, my function is returning a string of GUIDs. Is there a way to get it to return a list of SellableItem objects instead of a string?

Below is my function and results

Function

var container = Sitecore.Commerce.Engine.Connect.EngineConnectUtility.GetShopsContainer();
var category = Sitecore.Commerce.ServiceProxy.Proxy
                        .GetValue<Category>(container.Categories
                        .ByKey("my-category-key")
                        .Expand("Components"));

return category.ChildrenSellableItemList

Results

"57e31714-635f-4232-964c-f405ff5424a2|4389608f-a7fa-78ab-c731-18f10e005b0f|984124ea-2714-f146-ea05-74692457c826
2

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.

0

I ended up using the following query instead.

var container = Sitecore.Commerce.Engine.Connect.EngineConnectUtility.GetShopsContainer();

var category = Sitecore.Commerce.ServiceProxy.Proxy
                        .GetValue<Category>(container.Categories
                        .ByKey("my-category-key")
                        .Expand("Components"));

var sellableItems = container.SellableItems.Where(x => category.ChildrenSellableItemList.Contains(x.SitecoreId))
                                        .Skip(skip)
                                        .Take(take)
                                        .ToList();

return sellableItems;
  • Keep in mind, that your solution will be die to performance issue. You will always get all SellableItems from Sitecore Commerce environment. So it will be a problem, if you will have a huge count of SellableItems – Andrei Paliakou Jun 18 at 7:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.