Creating a Product Bundle

Let’s assume the shop offers winter sports equipment, namely snowboards, bindings, boots, and so on. The snowboards in this example do not have bindings assembled by default so the customer needs to attach some bindings first before they can get out to the slope. Therefore it makes sense to offer the customers some product bundles containing a plain snowboard and a pair of bindings.

Image Product Bundle

In this tutorial we are going to explain how you can model such product bundles with the data models provided by commercetools Composable Commerce.

Prerequisites

Before starting this tutorial you should be familiar with using the Composable Commerce API and have a Project set up already. If you are new to commercetools, our getting started guide will assist you in learning.

We assume that your project already contains the products to be bundled.

Create ProductType for bundles

In this tutorial we are going to explain how you can model product bundles like the snowboard/binding-bundle with the data models provided by Composable Commerce.

The approach to follow is to create a particular ProductType that is made for bundles containing two or more different products.

We could extend the product bundle even further by adding a pair boots to the board and binding. To keep it simple we'll stick to a bundle of two products for now, one snowboard and one binding. The diagram below shows the custom attributes board and binding that we need to define for the SnowboardBundle ProductType.

Image ProductType Bundle

As you can see in the diagram, the values of the attributes are particular Products. Therefore we need to define the type of the board and binding attributes as such. The API provides the AttributeReferenceType for those attributes that refer to other objects in the Composable Commerce APIs. The ReferenceType fits best to the use case when we further specify to what sort of object the attributes should refer to. Reference Types support several objects, but as shown in the diagram above we want the attributes refer to a Product. To achieve this we'll specify the AttributeType for board and binding by the following JSON snippet:

"type":{
"name":"reference",
"referenceTypeId":"product"
}

Now let's create the SnowboardBundle ProductType at the API endpoint /<project-id>/product-types. Since Create requests are sent via HTTP POST carrying JSON representations we are submitting following payload with the request:

{
"name": "SnowboardBundle",
"description": "this is the ProductType that contains the actual bundle",
"attributes": [
{
"type": {
"name": "reference",
"referenceTypeId": "product"
},
"name": "board",
"label": { "en": "board" },
"isRequired": true,
"isSearchable": true
},
{
"type": {
"name": "reference",
"referenceTypeId": "product"
},
"name": "binding",
"label": { "en": "binding" },
"isRequired": true,
"isSearchable": true
}
]
}

If the ProductType creation went well you'll get a response similar to this:

{
"id":"<snowboard-bundle-product-type-id>",
"version":1,
"name":"SnowboardBundle",
"description":"this is the ProductType that contains the actual bundle",
"classifier":"Complex",
"attributes":[
{
"name":"board",
"label":{"en":"board"},
"isRequired":true,
"type":{
"name":"reference",
"referenceTypeId":"product"
},
"attributeConstraint":"None",
"isSearchable":true,
"inputHint":"SingleLine",
"displayGroup":"Other"
},
{
"name":"binding",
"label":{"en":"binding"},
"isRequired":true,
"type":{
"name":"reference",
"referenceTypeId":"product"
},
"attributeConstraint":"None",
"isSearchable":true,
"inputHint":"SingleLine",
"displayGroup":"Other"
}
],
"createdAt":"<timestamp>",
"lastModifiedAt":"<timestamp>”
}

We just have extended the existing bundle ProductType with two attributes of the AttributeReferenceType. We have further specified that the attributes board and binding contain references to Products. Both attributes have been defined as required and searchable.

Create Product bundle

As we now have created the ProductType for the product bundle we are now able to create concrete product bundles. In this section are going to create the bundle “Feather_Cartel” made of following products already stored in the project:

BoardBinding
"Feather 56""Cartel R"
a snowboard a binding
173 € in all countries for all customer groups115 € in all countries for all customer groups

To create the bundle it is enough to specify the references to those two existing products; there is no need to repeat specifications of “Feather 56” and “Cartel R”. Thus the values of attributes board and binding contain the identifiers of those products only. You can retrieve the product identifiers from either the Merchant Center or the ImpEx.

The above mentioned product bundle is created by a HTTP POST request on endpoint: {projectKey}/products with following JSON payload:

{
"name": {
"en": "Bundle Feather Cartel"
},
"productType": {
"typeId": "product-type",
"id": "<snowboard-bundle-product-type-id>"
},
"slug": {
"en": "feather_cartel"
},
"description": {
"en": "bundle of board Feather 56 and binding Cartel R"
},
"masterVariant": {
"sku": "<sku_bundle_feather_cartel>",
"attributes": [
{
"name": "board",
"value": {
"typeId": "product",
"id": "<snowboard-Feather-56-product-id>"
}
},
{
"name": "binding",
"value": {
"typeId": "product",
"id": "<binding-Cartel-R-product-id>"
}
}
]
}
}

The JSON object you’ll get with your response contains the references to the bundled products only. In the next section we are going to show you how you can get the information about the bundled products too.

Using Reference Expansion

The default representation for products of this type if you do a simple request on the product endpoint. Since the representation contains the id of the referenced products only you will have to make additional requests to the product endpoint to get the full information about the single products. Of course you can do it this way, but why not use the Reference Expansion feature here that provides a way to get the product presentation for snowboard and binding in just one go? For this you add the expand parameter to the GET request, like this:

{projectKey}/product-projections?expand=masterVariant.attributes%5B*%5D.value&staged=true

Expanding the attribute on the master variant only is just a special case for this tutorial in which the bundle product has just one variant. All other variants of the product will not be expanded by using Reference Expansion on the master variant only.
For products that have more variants the expand request should be extended like below to get the attribute expanded in all variants as well:

{projectKey}/product-projections?expand=masterVariant.attributes%5B*%5D.value&expand=variants%5B*%5D.attributes%5B*%5D.value&staged=true

Search for the bundles that contain a particular Product

Let's say you want to search for the bundles that contain the binding Cartel R. For this you would use the Product Search functionality.

Remark: At the search endpoint you have the opportunity to search for either the current or the staged ProductProjections. If you want to search for the unpublished products you need to set the staged parameter to true, but if you want to search for published products you don't need to set the staged parameter at all since it's default value is false.

For the search request we'll set the filter expression to the binding attribute and the product id of the binding "Cartel R" as value like this:

{projectKey}/product-projections/search?filter.query=variants.attributes.binding.id:"<binding-Cartel-R-product-id>"