Update all items that share the same catalog entity when the item is saved
Tags: sitecore, commerce
Published: 2020-11-24 08:19:31

In Sitecore Commerce, a category/product entity associated with the different parents is treated as the different items in the Content Editor. Each item's fields are kept in the entity's `ExternalSettings` component per entity relationship. So one item  is edited, the others will not be updated. You can change this behavior by customizing the `CatalogDataProvider` class.  

To retrieve all items that share an entity, use the `CatalogRepository` class. The implementation is the following.

```csharp
public List<Item> GetEntityShareCatalogItems(Item item)
{
    // Deterministic ID is an item's ID in the Content Editor.
    var deterministicId = item.ID.Guid.ToString();

    // Sitecore ID is an one-to-one GUID with the entity.
    // This is used for mapping entity with items internally, so not visible in the Content Editor.
    var catalogRepository = new CatalogRepository(item.Language.Name);
    var sitecoreId = catalogRepository.GetSitecoreIdForDeterministicId(deterministicId);
    if (string.IsNullOrWhiteSpace(sitecoreId))
    {
        return new List<Item> { item };
    }

    var entityId = catalogRepository.GetEntityIdFromMappings(sitecoreId);
    if (string.IsNullOrWhiteSpace(entityId))
    {
        return new List<Item> { item };
    }

    // Get all items that share the entity ID.
    var deterministicIds = catalogRepository.GetDeterministicIdsForEntityId(entityId, includeVariations: true);
    if (deterministicIds == null || !deterministicIds.Any())
    {
        return new List<Item> { item };
    }

    return deterministicIds
        .Select(id => ID.Parse(id, ID.Null))
        .Select(item.Database.GetItem)
        .Where(item => item?.Name == item.Name)
        .Where(item => item.DescendsFrom(item.TemplateID))
        .Where(item => item.Language == item.Language)
        .ToList();
}
```

And apply field changes to all the items by customizing the `CatalogDataProvider.SaveItem` method.

```csharp
public class MyCatalogDataProvider : CatalogDataProvider
{
    public override bool SaveItem(ItemDefinition itemDefinition, ItemChanges changes, CallContext context)
    {
        if (!CanProcessItem(itemDefinition, false))
        {
            return false;
        }

        using (new LanguageSwitcher(changes.Item.Language))
        {
            // Call the method here.
            var catalogItems = GetEntityShareCatalogItems(changes.Item);
            var success = true;

            // Apply the same changes to all the items.
            foreach (var item in catalogItems)
            {
                var definition = new ItemDefinition(item.ID, itemDefinition.Name, itemDefinition.TemplateID, itemDefinition.BranchId);
                var clonedChanges = changes.Clone(item);

                try
                {
                    success &= base.SaveItem(definition, clonedChanges, context);
                }
                catch (Exception ex)
                {
                    success = false;
                }
            }

            return success;
        }
    }
}
```

Then replace the default data provider with the customized one.

```xml
<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <dataProviders>
      <catalogProvider type="Sitecore.Commerce.Engine.Connect.DataProvider.CatalogDataProvider, Sitecore.Commerce.Engine.Connect">
        <patch:attribute name="type">Namespace.To.MyCatalogDataProvider, Assembly</patch:attribute>
      </catalogProvider>
    </dataProviders>
  </sitecore>
</configuration>
```

Now when a catalog item is saved, all related items are updated together.