When epi released Commerce 11, it came with a new way of working with Relations. The old way is obsolete. I will go through how we migrated to the new APIs and some gotchas with it.
In the Commerce 11 Breaking changes page, you can read the following point:
In IRelationRepository, the GetChildren and GetParents methods supercede GetRelationsBySource and GetRelationsByTarget. On the relation objects, the properties Parent and Child serve the same purpose.
So the big difference is that we need to migrate all our existing GetRelationsBySource
/ GetRelationsByTarget
to GetParents
/ GetChildren
. However, it is not as easy as it may sound. The old API have been known among developers to be confusing, because depending on what type of relation it is, the source/target differs. I’ll talk about how that played out pre Commerce 11 now, if you already are an expert with this, you can skip this segment.
Relations explained
Note: I sometimes use the term Node and perhaps sometimes Category, but for this post, they are interchangeable. In epi Commerce admin, they are named Category, but in the apis they are nodes.
In the API in epi commerce there are two kinds of relations, NodeRelation and EntryRelation. NodeRelations are relations where one side of the relation is a NodeContent (most commonly Category). EntryRelations are relations where both sides are EntryContent (most commonly Variant, Product, Bundle or Package). There are a couple of classes that inherit EntryRelation, that specifies them more, such as ProductVariation, PackageEntry and BundleEntry. In Commerce 11.2, we have as well a NodeEntryRelation in the APIs.
So if I take a variant as an example. This variant belongs to a product, which is a EntryRelation (more specifically ProductVariation) since both variant and products are EntryContent. The variant was also created in a category, NodeRelation between the variant and the category, as well as have been linked to 4 additional categories (NodeRelation again).
And if we do the same thing for a Category, that will only be NodeRelations. Nodes have relations to other nodes (a category can be in several categories) and nodes have relations to entries (an entry exists in or is linked to a node). There is a dangerous quirk here, one of the “node relations” is not actually a NodeRelation. The parent of the node (where the node was created), is not a relation, but simply stored in a “Parent” field. But all other node relations to the node will be NodeRelations. This might sound confusing, but later on I’ll try to show you the ramifications with this, how this is played out.
New Relations API explained
Episerver has presented dobby with a gift! Dobby is free [of having to remember and understand the different Target/Source depending on if it’s a node or entry, or coming up with some “product is always the target” chant]!
The new APIs are much more semantic, using GetParents and GetChildren. And on the Relation objects, you can access the Parents and Children properties.
Comparing old API vs. new API vs. IContentLoader
Why do I compare with IContentLoader, weren’t we only talking about Relations? Yes, but seeing as the IContentLoader has a GetChildren method, I thought I could include it as well, as that one is not the same as any of the new/old relation apis.
The variable x in these is always a ContentReference for the APIs, so the first column in the tables we assumed that you used the contentloader to fetch the content
(IContentLoader.Get<IContent>(x))
and hence get the type of the content.
Also note that I never specify in detail what Relation type I want to get, I always send in <Relation>. You can specify it closer, such as
IRelationRepository.GetParents<ProductVariation>((VariationContent) x)
and I will only get the parent product and not the parent nodes (see table).
N = Nodes
P = Products
V = Variation
LN = Linked Nodes
Commerce 11 Relations
GetParents(x) |
GetChildren(x) |
|
x is ProductContent |
N + P ExplanationYou get all the nodes this product is in, as well as the products if another product has this product as variant. |
V(/P) ExplanationYou get all ProductVariant relations. The trick here is that a product can have a product as variation as well, hence the (/P). But that product is not the “parent” product. |
x is VariationContent |
N + P ExplanationSame as the GetParents for ProductContent, I will get all nodes and all product this variant belongs to. |
- |
x is NodeContent |
LN ExplanationGets the additional nodes for x. So x has a parent node, the one it was created in, but you won’t get that parent here. Instead, you’ll get nodes this node has been linked to (All properties -> Belongs to in the admin UI) |
V + P + LN ExplanationGets all the variants and products that is linked to the category. You will also get nodes that have specifically linked this node as a parent node. See it as you will get the child node in the node to node relation. But you will not get nodes that are created in the parent node, only linked ones. If you take as an example, “Mens Jackets” was created under “Mens”, but were linked to “Womens Dresses” as well. “Subcategory womens..” was created in “Womens Dresses”. If you do GetChildren on “Womens Dresses” you will get all the products, variants and the “Mens Jackets”, but NOT the “Subcategory womens”. |
Commerce < 11 Relations
GetRelationsBySource(x) |
GetRelationsByTarget(x) |
|
x is ProductContent |
N + V(/P) ExplanationYou get all nodes and all ProductVariant relations. The trick here is that a product can have a product as variation as well, hence the (/P). But that product is not the “parent” product. |
P ExplanationA product can belong to another product, if so, you will get the parent product here. |
x is VariationContent |
N ExplanationGets all the nodes the variant belongs to. Note that the variant can belong to different/additional nodes than the product. |
P ExplanationGets the product that the variant belongs to. |
x is NodeContent |
LN ExplanationGets the additional nodes for x. So x has a parent node, the one it was created in, but you won’t get that parent here. Instead, you’ll get nodes this node has been linked to (All properties -> Belongs to in the admin UI) |
V + P + LN ExplanationGets all the variants and products that is linked to the category. You will also get nodes that have specifically linked this node as a parent node. See it as you will get the child node in the node to node relation. But you will not get nodes that are created in the parent node, only linked ones. If you take as an example, “Mens Jackets” was created under “Mens”, but were linked to “Womens Dresses” as well. “Subcategory womens..” was created in “Womens Dresses”. If you do GetRelationsByTarget on “Womens Dresses” you will get all the products, variants and the “Mens Jackets”, but NOT the “Subcategory womens”. |
IContentLoader
GetChildren(x) |
||
x is ProductContent |
- |
|
x is VariationContent |
- |
|
x is NodeContent |
V + P + N ExplanationThis will get everything “below” the node. Compared to GetRelationsByTarget it will also get the sub categories that were created in this node. |
Migrating to the new API
You can use the table above to try to understand what has changed and where you need to change something, or you can use the following logic applying the old API on top of the new API.
GetRelationsBySource switches on the ContentType
- If the ContentType is CatalogEntry, it will GetParents<NodeRelation> as well as GetChildren<Relation> and return that to you.
- If the ContentType is CatalogNode, it will GetParents<Relation> and return that to you.
GetRelationsByTarget also switches on the ContentType
- If the ContentType is CatalogEntry, it will GetParents<Relation> and return that to you
- If the ContentType is CatalogNode, it will GetChildren<Relation> and return that to you
So in conclusion, if you had any GetRelationsBySource/GetRelationsByTarget that included EntryRelations, you will need to think which method you’d want to use depending on the input and desired output. If they only include NodeRelations, you can just translate GetRelationsBySource to GetParents and GetRelationsByTarget to GetChildren.
Primary category for Entries
A thing that was changed in the API as well was how you determine which category is the primary category for a CatalogEntry. Epi explained this pretty well at their Breaking Changes Commerce 11 document so I will just unceremoniously copy&paste it here.
In previous versions, a catalog entry's home category, used for the content's ParentLink and when rendering default hierarchical URLs, was determined by the relation with the lowest SortOrder. This version introduces a separate field for marking one relation as the primary relation, used as home category.
Hopefully this post didn’t confuse you more, it was just notes on a deep dive on Relations we did when we upgraded to Commerce 11.