docs
  1. SCAYLE Resource Center
  2. Developer Guides
  3. Shops
  4. Shop Categories

Shop Categories

General

Shop Categories help you to cluster your Assortment in multiple Categories and render your Categories as a Tree.
There is a m:n relation between Products & Shop Categories. So each Category can have multiple Products and each Product can be assigned to multiple Categories.

Category name & Path

Each category comes with a name and a path.
The category name is a localised string:

[
    'de_DE' => 'Kleider',
    'en_EN' => 'Dresses',
]

The category path gets automatically calculated by SCAYLE and represents a unique category identifier which can be used for lookups.
It contains the full path (including all parent category names) and looks like this:

/women/clothing/dresses

Status & Visibility of Shop Categories

Each Shop Category comes with two attribute values defining their Status & Visibility in the Storefront:

  • isVisible - this attribute defines if the Storefront should render the Category to the Customer
  • isActive - this attribute defines whether the Shop Category is active or inactive
  • isExcludedFromSearch - This Attribute defines whether the Shop Category should be excluded from the search.

The Shop Category Status can be managed by the Shop Managers in the SCAYLE Panel.

If you're set a Shop Category to 'inactive', all child-categories automatically inherit from this action and also will be set to 'invisible' or 'deactivated'.
In case you activate a child-category all parent-categories will get activated within the same action.

If you're set a Shop Category to invisible there is no inheritance logic implemented.

Product Mapping via Product Sets

SCAYLE offers multiple ways to map the products to the corresponding shop categories:

  • Explicitly include Products by their ID's
  • Explicitly exclude Products by their ID's
  • Include or exclude products using Shared Attributes on product or product master level
  • Include only 'new' products
  • Reference to the products of another shop category

Be aware of the following limitation:

If you use include or exclude Products by their ID's you're not able to add other criteria but only can reference product sets of the same type.

Supported Filter for a Category

You can define the supported filter which can be used on a category page to filter products & do faceting.

Add additional data to your Shop Category

SCAYLE lets you define additional data on your Shop Category via Shop Category Properties.
This is useful for multiple reasons, just to highlight some:

  • Add internal ID's or additional data to your category you need use in your Storefront
  • Add SEO information to a category
  • Add Category Descriptions / additional Content to your category
  • Determine if the Shop Category is a sale-only category

Shop Category Properties allow you to store additional data to pre-defined keys.
Both - the key and the value must be a string.

Add a Shop Property Key

The definition of the Shop Category Property Key is done on SCAYLE instance level.
You can use the defined key on every shop within your instance.

let shopCategoryPropertyKey = {
  "key": "sale",
  "isInheritable": true
};

let response = await adminApi.apis.ShopCategories.createShopCategoryPropertyKey({requestBody: shopCategoryPropertyKey});
let createdShopCategoryPropertyKey = response.body;

Shop Category Properties can be inherited from a parent category to its child categories by setting the isInheritable flag to true.

Update a Shop Category Property Key

Once you created a shop category property key, you can come back and update it later. However, you can only modify a key if it is currently not used within a shop category.

let shopCategoryPropertyKey = {
    key: "new",
    isInheritable: true
};

let response = await client.apis.ShopCategories.updateShopCategoryPropertyKey(
    {shopCategoryPropertyKey: "sale"},
    {requestBody: shopCategoryPropertyKey}
);

let updatedShopCategoryPropertyKey = response.body;

Retrieve a Shop Category Property Key

You can request a shop category property keys by simply providing its name.

let response = await client.apis.ShopCategories.getShopCategoryPropertyKey({shopCategoryPropertyKey: "sale"});
let shopCategoryPropertyKey = response.body;

Retrieve all Shop Category Property Keys for a Shop Category

If you would like to retrieve more than one shop category property key, you can fetch them all at once.

let response = await client.apis.ShopCategories.getShopCategoryPropertyKeys();
let shopCategoryPropertyKeys = response.body.entities;

Add a Shop Property

After you created a Shop Category Property Key you can set the property value for a specific Shop Category.
In the example below we set the value 'NoSale' for the property key 'sale' for the Shop Category with the ID 1 within the Shop with the key 'ms' and the country code 'DE' (Germany):

let shopCategoryProperty = {
    "key": "sale",
    "value": "NoSale"
};

let response = await adminApi.apis.ShopCategories.updateOrCreateShopCategoryProperty({shopKey: "ms", countryCode: "DE", shopCategoryId: 1}, {requestBody: shopCategoryProperty});
let updatedShopCategoryProperty = response.body;

Update or add a Shop Property

If you want to update a shop property or add it - in case it doesn't exist - you can use the updateOrCreateProperty method:

let shopCategoryProperty = {
    "key": "sale",
    "value": "NoSale"
};

let response = await adminApi.apis.ShopCategories.updateOrCreateShopCategoryProperty({shopKey: "ms", countryCode: "DE", shopCategoryId: 1}, {requestBody: shopCategoryProperty});
let updatedShopCategoryProperty = response.body;

Delete Shop Category property value

In case you want to remove the value for a saved Shop Category Property for a specific key you can use the deleteProperty method:

$adminApi->shopCategories->deleteProperty('ms', 'DE', 1, 'sale');

Retrieve a Shop Category Properties for a Shop Category

If you want to request a shop category property, you need to provide its respective category ID and property key.

let response = await client.apis.ShopCategories.getShopCategoryProperty({shopKey: "ms", countryCode: "DE", shopCategoryId: 1, shopCategoryPropertyKey: "sale"});
let shopCategoryProperty = response.body;

Retrieve all Shop Category Properties for a Shop Category

You can retrieve all shop category properties for a specific shop category. In this case you don't pass any property key to the call:

$shopCategoryPropertyCollection = $adminApi->shopCategories->allProperties('ms', 'DE', 1);

Shared Category Tree

If you work with multiple Shop Countries (e.g. Shop.com with a dutch and a german shop country) you normally manage the category tree only once.
SCAYLE offers you multiple features to make the shop category handling in such a scenario very efficient.
The general shop category tree is shared across all shops, but you can change the following settings on shop country level:

  • Visibility of categories
  • Active-Status
  • Name (for translation reasons / localisation)
  • Shop Properties

If you still want to have a dedicated shop category tree for a shop country you have to set a new categoryTreeId when you create the shop country, as documented here.

Manage Shop Categories

Shop Categories represent a nested category tree. Each node holds it's own product set. So the parent category node does not contain the products of it's children by default. If you want to enable this behavior you need to make sure that a parent category node contains all product set configurations of it's children.

To manage Shop Categories you need to work with the Admin API.

Create a shop category

The most minimal way to create a new shop category for the shop with the shop key 'ms' looks like this:

let shopCategory = {
    "name": {
        "en_EN": "New shop category"
    },
    "isActive": true,
    "isVisible": true,
    "isExcludedFromSearch": false
};

let response = await adminApi.apis.ShopCategories.createShopCategory({shopKey: "ms"}, {requestBody: shopCategory});
let createdShopCategory = response.body;
  • The category path is automatically calculated based on the category name and the category's parents. This operation is asynchronous and can be executed with a delay. This is the reason why the countries.path field might not be present in the response.
  • isActive, isVisible, and properties will be propagated to all existing shop countries if specified.

Shop category parameters

ParameterDetails
id

Integer READ-ONLY

The ID of the shop category.

parentId

Integer

The ID of the parent shop category.

leftSiblingId

Integer

The ID of the left sibling shop category. It defines the shop category position in the category tree.

name

String

The localized category name.

productSets

ShopCategoryProductSet

Product sets define which products to include in the shop category.

supportedFilterGroups

String

List of supported filter groups.

properties

ShopCategoryProperty

The properties assigned to the shop category. Can be specified on creation only.

isActive

Boolean

Declares whether the shop category is active or not. Can be specified on creation only.

isVisible

Boolean

Declares whether the shop category is visible in the shop or not. Can be specified on creation only.

customData

CustomData

Arbitrary fields assigned to shop categories

countries

ShopCategoryCountry READ-ONLY

List of country specific configurations.

ShopCategoryCountry

PropertyTypeDescription
countryCodeString READ-ONLYISO 3166 alpha 2 country code
shopCountryIdInteger READ-ONLYId of Shop Country
pathString READ-ONLYString representation of the URL path to the category
isActiveBooleanDeclares whether the shop category is active or not
isVisibleBooleanDeclares whether the shop category is visible in the shop or not
propertiesShopCategoryProperty[]The properties assigned to the shop category
customDataCustomDataArbitrary fields assigned to shop category countries

ShopCategoryProperty

PropertyTypeDescription
keyStringThe key of the shop category property
valueStringThe value of the shop category property

When creating a shop category, it is important to keep the following details in mind:

  • leftSiblingId is only used to determine a new category position. The category will be inserted after the given left sibling category.
  • The category path is automatically calculated based on the category name and the category's parents. This operation is asynchronous and can be executed with a delay. This is the reason why the countries.path field might not be present in the response.
  • isActive, isVisible, and properties will be propagated to all existing shop countries if specified. It is important to note that new shop countries will not automatically receive these values. You have to create a new configuration each time a shop country is created.
  • The deactivation of a parent category by setting the isActive flag to false will also deactivate its child categories. Activating a child category will activate all respective parent categories.
  • It is recommended to create shop countries beforehand to avoid manual creation of shop-specific configs.
  • There is one reserved property with a special meaning: sale. It allows you to include or exclude discounted products in the assortment by setting the value respectively to Sale or NoSale.
  • A shop category is created along with shop country-specific settings. The category gets a localized name for every existing country. If name has a translation that matches the country's language, then this value is used as a category name. Otherwise, the default language translation is used as a country specific name.

If there exists a custom data configuration for the shopCategory entity and the custom data isn't provided within the create request, the defaultValue specified in the custom data configuration for each property would be considered.

Create a shop category with a shop property

It's possible to directly add some shop properties to a shop category create call:

let shopCategory = {
    "name": {
        "de_DE": "New Shop Category"
    },
    "isActive": true,
    "isVisible": true,
    "properties": [
        {
            "key": "sale",
            "value": "NoSale"
        }
    ]
};

let response = await adminApi.apis.ShopCategories.createShopCategory({shopKey: "ms"}, {requestBody: shopCategory});
let createdShopCategory = response.body;

Create a nested shop category

The attribute 'parentId' defines the parent category id.
You can create a category 'Apparel' and a child-category 'Dresses' with the following code:

let apparelShopCategory = {
    "name": {
        "en_EN": "Apparel"
    },
    "isActive": true,
    "isVisible": true
};

let response = await adminApi.apis.ShopCategories.createShopCategory({shopKey: "<shop-key>"}, {requestBody: apparelShopCategory});
let createdApparelCat = response.body;

let dressesShopCategory = {
    "name": {
        "de_DE": "New Shop Category"
    },
    "isActive": true,
    "isVisible": true,
    "parentId": createdApparelCat.id
};

let response = await adminApi.apis.ShopCategories.createShopCategory({shopKey: "<shop-key>"}, {requestBody: dressesShopCategory});
let createdDressesCat = response.body;

Create a nested shop category with a category sorting

In case you have multiple shop categories on the same hierarchy level you typically want to define the sorting of the category nodes.
This can be done by using the 'leftSiblingId' property. The new Category 'Dresses' will be inserted after the given left sibling category (in the example below category with ID 100).

let shopCategory = {
    "name": {
        "en_EN": "Dresses"
    },
    "isActive": true,
    "isVisible": true,
    "leftSiblingId": 100
};

let response = await adminApi.apis.ShopCategories.createShopCategory({shopKey: "<shop-key>"}, {requestBody: shopCategory});
let createdDressesCat = response.body;

Create a new shop category with a product set definition

let shopCategory = {
    "parentId": 1,
    "leftSiblingId": 2,
    "name": {
        "en_EN": "Red Products Category"
    },
    "productSets": [
        {
            "isNew": true,
            "attributes": [
                {
                    "name": "color",
                    "include": ["red"]
                }
            ]
        },
        {
            "referencedProductSetId": 1,
            "includeProductIds": [1, 2]
        },
        {
            "excludeProductIds": [4]
        }
    ],
    "isActive": true,
    "isVisible": true,
    "properties": [
        {
            "key": "sale",
            "value": "NoSale"
        }
    ]
};

let response = await adminApi.apis.ShopCategories.createShopCategory({shopKey: "ms"}, {requestBody: shopCategory});
let createdShopCategory = response.body;

Update a Shop Category

To update a shop category SCAYLE offers a update method:

let shopCategory = {
     "name": {
        "en_EN": "Sneaker"
    }
};

let response = await adminApi.apis.ShopCategories.updateShopCategory({shopKey: "ms", shopCategoryId: 1}, {requestBody: shopCategory});
let updatedShopCategory = response.body;

The category path is automatically recalculated when a category name or its parent are changed. This operation is asynchronous and can be executed with a delay. This is the reason why the countries.path field might be not present in the response for a short period of time.

Referencing Product Sets

If a shop category was initially created with a product set that references another product set via the 'referencedProductSetId' attribute and this reference is not sent again in the update request, the reference is removed.

Please note that `productSets.includeProductIds` and `productSets.excludeProductIds` are both limited to a maximum of 10,000 Product IDs.

Get a shop category

You can get a shop category and select the additional information & data which should be returned for the selected shop category.

The following options are available for the 'with' paramter:

  • countries
  • countries.properties
  • countries.customData
  • customData
let response = await adminApi.apis.ShopCategories.getShopCategory({shopKey: "ms", shopCategoryId: 1, with: "countries"});
let shopCategory = response.body;

Get multiple shop categories

To fetch multiple shop categories SCAYLE offers you pagination and filtering.

let response = await adminApi.apis.ShopCategories.getShopCategories({shopKey: "shopKey"});
let shopCategoryCollection = response.body.entities;

Options:

ParameterTypeDetails
withString

Allows to load the following nested resources within this request:

  • countries
  • countries.properties
  • countries.customData
  • customData
limitIntegerMaximum number of items in the result. (default: 100, maximum: 1000)
filters[id]StringComma-separated list of shop category IDs that should be used for filtering.
filters[minId]IntegerMinimum ID of shop categories, which should be returned.
filters[maxId]IntegerMaximum ID of shop categories, which should be returned.
filters[parentId]IntegerFilter shop categories by parent ID.

Delete a shop category

You can delete a shop category. In this case all children also will get deleted.

adminApi.apis.ShopCategories.deleteShopCategory({shopKey: shopKey, shopCategoryId: shopCategoryId});

Deletion effect on Product Sets

Direct and indirect incoming references (if any) will be deleted without copying conditions over.

This method can be used to unlink a product set from the referencing product sets. It is possible to copy conditions of the reference to the product sets from which it will be unlinked.

Method Signature

client.apis.ShopCategories.unlinkReferencingProductSets({shopKey: shopKey, productSetId: productSetId}, {requestBody: productSetUnlinkInstruction});
ParameterDetails
productSetIds

Integer

IDs of the referencing product sets for which the reference to the stated product set should be unlinked.

copyConditions

Boolean

Declares whether the conditions of the stated product set should be copied over to the referencing product sets.

Details

Copying conditions

If you want to unlink a product set but preserve its conditions (e.g. excluded or included product ids), you need to set copyConditions = true. In this case, all conditions defined for the unlinked reference product set will be copied to the product set IDs provided in the payload.

Examples

let productSetUnlinkInstruction = {
    productSetIds: [10, 22],
    copyConditions: true
};

client.apis.ShopCategories.unlinkReferencingProductSets({shopKey: "ms", productSetId: 1}, {requestBody: productSetUnlinkInstruction});

Use Shop Categories in your Storefront

To fetch the Shop Categories in your Storefront you need to work with the Storefront API.

Get a category tree

If you are not working with a specific child category yet, root categories are used to access the whole category tree. Usually the category tree makes a fundamental part of the navigation in an online shop.

Furthermore, there are certain parameters (such as name, slug, path, and description) that are used by default when requesting a category.

Categories are related by parentId and childrenIds.

Root-level categories have a parentId = 0

Get the full category tree

We can use a shop category tree for the navigation in our shop.

// Get full category tree 
const categories = await client.categories.getRoots({
  with: { children: 1 },
});

for (const category of categories) {
  console.log(category.name)
}

By default each category comes with its childrenIds. But to iterate through our category tree and to build a link structure we need to ask for the fields of the children categories, e.g. name, slug or even their children. To do so, we pass children as our with parameter.

If you want to limit the number of levels to receive you can provide a numeric depth parameter.

Get a single category

Retrieve a shop category by ID or path with parent and child categories.

Categories are stored in a tree structure. Using this API, any individual category can be requested. Additionally, it is possible to include its parents (recursively) and children.

You can query a single category by specifying its ID or path. A path consists of the category's slug prepended with the slugs of its parent up to the root category.

Example: Get a category by ID

We know the category ID of 'Women' is 6. To simply get the category's own properties we just have to pass the ID as parameter.

const category = await client.categories.getById(6, {});
console.log(category.name)

Example: Get a category by ID with their parent

In this example we query the category "Shirts", id 8, and also all its parent categories which are "Women" and "Clothing".

const category = await client.categories.getById(6, {
  with: { parents: 'all' }, 
});

console.log(category.name)
console.log(category.parent?.name)

Example: Get category by ID with its children

In the previous example we saw that our category "Women" has a field childrenIds. Of course we could now look up each of these categories with a dedicated requests. We could also request them all by their IDs in a single second request, see this example. But instead we are going to safe these extra requests and include all children by specifying them as with parameter.

const category = await client.categories.getById(6, {
  with: { children: 1 }, 
});

console.log(category.name)
console.log(category.children?.length)

Example: Get a category by path

You can also query a category by its path instead of its ID.

const category = await client.categories.getByPath(
  ["women", "clothing", "shirts"],
);

console.log(category.name);

Get multiple categories

It's possible to retrieve multiple shop categories by their IDs. Useful when you have a single category page and want to tease and link to it's children.

Example: Get Categories with their Children

This time we are on one of our root categories, "Women". We already know their childrenIds from our previous data request in this example.

Now we want to know more about our children categories. So we pass their ids as a parameter. Also we set the depth to get the properties of their children as well.

// Get properties of multiple categories plus their children
const childrenIds = [7, 9, 10];
const categories = await client.categories.getByIds(childrenIds, {
  with: {
    absoluteDepth: 2,
  },
});

console.log(categories[0].name);
console.log(categories[0].children[0].name);
// => Clothing
// => Shirts

absoluteDepth and depth start from the category itself, so 1 will retrieve only the categories with specified IDs, 2 will retrieve their first children etc.

Get Categories with their parents

Sometimes we also want to get information about the parents of our categories.

In this example we request information about two shirt categories.

They are similar and even belong to parent categories with the same name. But they are different. One is a child of Men, the other one a child of Women.

To get the parent we pass it in the with parameter.

// Get properties of multiple categories plus their parents
const childrenIds = [3, 8];
const categories = await client.categories.getByIds(childrenIds, {
  with: {
    parents: "all",
  },
});

console.log(categories[0].parent.parent.name);
console.log(categories[1].parent.parent.name);
// => Men
// => Women
Response
[
    {
        "id": 3,
        "path": "/men/clothing/shirts",
        "name": "Shirts",
        "slug": "shirts",
        "description": "",
        "parentId": 2,
        "rootlineIds": [1, 2, 3],
        "childrenIds": [],
        "properties": [],
        "isHidden": false,
        "depth": 3,
        "supportedFilter": [],
        "parent": {
            "id": 2,
            "path": "/men/clothing",
            "name": "Clothing",
            "slug": "clothing",
            "description": "",
            "parentId": 1,
            "rootlineIds": [1, 2],
            "childrenIds": [3],
            "properties": [],
            "isHidden": false,
            "depth": 2,
            "supportedFilter": [],
            "parent": {
                "id": 1,
                "path": "/men",
                "name": "Men",
                "slug": "men",
                "description": "",
                "parentId": 0,
                "rootlineIds": [1],
                "childrenIds": [2, 4, 5],
                "properties": [],
                "isHidden": false,
                "depth": 1,
                "supportedFilter": [],
                "shopLevelCustomData": {},
                "countryLevelCustomData": {}
            },
            "shopLevelCustomData": {},
            "countryLevelCustomData": {}
        },
        "shopLevelCustomData": {},
        "countryLevelCustomData": {}
    },
    {
        "id": 8,
        "path": "/women/clothing/shirts",
        "name": "Shirts",
        "slug": "shirts",
        "description": "",
        "parentId": 7,
        "rootlineIds": [6, 7, 8],
        "childrenIds": [],
        "properties": [],
        "isHidden": false,
        "depth": 3,
        "supportedFilter": [],
        "parent": {
            "id": 7,
            "path": "/women/clothing",
            "name": "Clothing",
            "slug": "clothing",
            "description": "",
            "parentId": 6,
            "rootlineIds": [6, 7],
            "childrenIds": [8],
            "properties": [],
            "isHidden": false,
            "depth": 2,
            "supportedFilter": [],
            "parent": {
                "id": 6,
                "path": "/women",
                "name": "Women",
                "slug": "women",
                "description": "",
                "parentId": 0,
                "rootlineIds": [6],
                "childrenIds": [7, 9, 10],
                "properties": [],
                "isHidden": false,
                "depth": 1,
                "supportedFilter": [],
                "shopLevelCustomData": {},
                "countryLevelCustomData": {}
            },
            "shopLevelCustomData": {},
            "countryLevelCustomData": {}
        },
        "shopLevelCustomData": {},
        "countryLevelCustomData": {}
    }
]

This method does not allow inclusion of hidden child categories. If you need to retrieve those, have a look at the Get a Single Shop Category.

Get products count for all categories

You can retrieve the products count for all categories using our Filters Endpoint. Categories which don't have any product assigned to them will not be returned.

You are also able to pass filters to the endpoint to be able to check which categories have products in combination with the filters.

Example: Get categories with products matching a maximum price

In this example, all categories with products having a maximum price of 10 euros are returned.

const categories = await client.filters.getValues('categoryIds', {
  where: {
    maxPrice: 1000,
  },
});

for (const category of categories) {
  console.log(category)
}
Response
[
  {
    "id": 1,
    "productCount": 10
  },
  {
    "id": 2,
    "productCount": 20
  },
]

Further education - SCAYLE Academy