Drupal Commerce Blog

What's happening in the world of Drupal Commerce.

Commerce 2.x Stories: Products

Previously we talked about currencies and stores. This week we’ll focus on products.

Drupal 8 product list.

Before describing the improvements we’ve made, let’s remember the previous implementation.

Commerce 1.x

Commerce 1.x has a flexible but slightly unfriendly product architecture.

Products represent individual purchasable SKUs: a small red t-shirt, a file, a conference ticket. Products can have attributes, which are special fields such as a Size or Color field used to switch between grouped products on the Add to Cart form. Products are grouped together in product display nodes. A product display node is a node that has a product reference field, allowing a “DrupalCon T-Shirt” product display to reference the individual SKUs (combinations of color and size).

Products need to be created individually in their own UI, then referenced from a product display node. This means having to use two different UIs to do what is essentially one operation. Back in 2012 we developed Inline Entity Form, allowing products to be managed directly on the product display form, and used it as a basis of the then new Commerce Kickstart 2.x distribution. It has since become a standard for managing products in Commerce.

Kickstart also tried to relabel the relevant entity types, calling the commerce_product entity type “product variation“ and calling product display nodes “products”. This rename was only skin deep (limited to the UI), and created confusion for documentation writers and readers.

Looking back, what caused the most trouble?

  1. Inline Entity Form

    Not having / requiring Inline Entity Form from the start.

  2. Mixing product and non-product content

    This made it necessary for Kickstart to create separate views for product and non-product content. The same was done for content types.

  3. Different ways of creating attribute fields

    Attribute fields can be any field with an options list, such as taxonomy_term_reference fields (pointing to a vocabulary of terms) or list_string fields. This increases the documentation burden, and creates the question of “which approach to use“. Kickstart standardized on taxonomy_term_reference fields, but at that point it was already obvious that too much choice is not always a good thing.

  4. Data model paper cuts

    Product displays weren’t required (even though most people used them), so there was no canonical way of getting the product’s display. Modules had to reinvent this relationship using EntityFieldQuery. Deleting a product display node didn’t delete the referenced products, since there was no guarantee they weren’t going to be referenced elsewhere. Once again this had to be done in Inline Entity Form.

    Products had required titles, even though attributes were usually displayed instead. Inline Entity Form thus had to offer an option to auto-generate the title, which was sometimes buggy.

Commerce 2.x

In the new architecture, a Product entity references one or more ProductVariation entities. Thus the product entities replace D7 product display nodes (and match the D8 nodes visually) while the variation entities replace D7 product entities. Each product type has a matching product variation type. A product always references variations of the same type.

Drupal 8 product variations list.
See also the full product edit form.

Variations are only manageable from the parent product, using Inline Entity Form, which is now a Commerce dependency. Variation titles are also no longer stored. They are dynamically constructed from the attribute labels instead, so there’s no more need for auto generation on insert. Deleting a product deletes its variations. Adding a variation to a product automatically creates a backreference on the variation, accessed via $variation->getProduct().

Drupal 8 taxonomy list.

Attributes are now entity reference fields. Referencing entities such as terms allows the usage of fields to hold things such as the image representation of the attribute, the color value for a color swatch, etc.

But what happens if a variation type has no attributes? For example, a product is only selling a single file. In that case, the Inline Entity Form widget will render the variation form as a regular fieldset on the product. This piece is still in progress but will be ready for the alpha release.

Custom product architectures

When it comes to product architectures, there is no one true answer. Furthermore, different clients might have different needs. That’s why it’s important for Commerce 2.x to support any number of product architectures.

The ProductVariation entity class implements the PurchasableEntityInterface:

Code snippet of the PurchasableEntityInterface.

Any content entity type that implements this interface can be purchased. The order module doesn’t depend on the product module, the product module just provides the default (and most common) product architecture. A product bundle module will probably want to define its own product architecture, etc.

Line items have a purchased_entity reference field. The target_type of that reference field is different for each line item type.

Drupal 8 line item type form.

Here the line item type points to the product variation entity type, indicating that the "Product variation" line item type is used to purchase product variations.

Early in the Commerce 2.x cycle we explored the idea of hierarchical products, but after initial exploration found out that the idea required several months of extra effort (having to rewrite the Tree module, reinvent an IEF like widget, UX and performance considerations). We removed it from the roadmap with a heavy heart, but now that Commerce 2.x supports custom product architectures, we can easily explore the idea in contrib at a later date.

Conclusion

Commerce 2.x ships with much better UX out of the box, thanks to a revamped product architecture. It also features under the hood improvements that allow developers to implement custom product architectures for different use cases.

Bojan Zivanovic
Posted: Nov 20, 2015

Comments

googletorp Jakob Torp on November 20, 2015

Thanks for the update, things are looking really promising for Commerce in Drupal 8, I really like that we are able to make use of the things learned from Drupal 7.

S1L on November 21, 2015

Awesome stuff. Thanks for writing this.
VERY excited about Commerce 2.x in D8!

Lukas von Blarer on November 26, 2015

Thank you for the update Bojan. I am about to start building a D8 site which will eventually use Commerce 2.x. Can I structure my product content types in a way that I am able to re-use them later as commerce products? If I understand you correctly, product display nodes don't exist anymore, right? Do you have tips on how to approach this?

giorgio79 on December 16, 2015

What if a product has no variation? :) This relationship still feels a bit forced. Why not just go with simple product entities, and have the option to set other product entities as variations of one another?

bojanz Bojan Zivanovic on January 25, 2016

One entity needs to hold the price and sku. If you go with simple products + products that contain variations, then different entities hold the price/sku.
You essentially get two different data models. You can do that in 2.x of course (implement a simple_product entity type), but 1 product + 1 variation covers the use case nicely.

giorgio79 on January 26, 2016

IMHO, in a proper SKU system a shirt with a different color should get a different SKU :) Namely, a variation should be a product on its own and that's it. You can reference together related products with a reference field.
Also, I was under the impression that the product entity + node display was due to D7 limits on entities. These limits should be gone in D8.

Andrej Galuf on January 15, 2016

Here's a problem I can forsee with forced one-to-one relationship between a product and his variation: combination products. Let's say for instance that we have a product X and a product Y which we sell separately. However, we also want to sell them in a combination offer with same colors bundled together - for instance a baby stroller with a matching bag. We would then apply default discounts to this combination to make it cheaper in front-end, while keeping track of both stocks to know what the combination stock is at any time.

To do something like that in the new setup would require setting up a new product for the combination, which causes all sorts of headaches with stock, price updates and erp syncronization.

That's why I tend to prefer one physical product to one shop product (or even a layer further, one product = 2 packages to control shipping as well), which can then be mixed anc matched as needed.

Then again, it's Drupal - as long as the default is iverridable, that's ok

bojanz Bojan Zivanovic on January 25, 2016

Understood, thanks for the feedback.
The solution here is a bundle entity type (just like commerce_bundle does in 1.x) that allows you to select which variations are included.
That way you're not duplicating your products, simply defining the additionally offered bundles on a different level.
Bundles are a big enough concept to warrant their own contrib, and actually have tax implications in many countries.

heyyo on April 28, 2016

With Drupal Commerce 1.x I was able to preselect default product variations, when creating a new product display.
I needed this approach, to sell print of painting on different medium and size.
But now, with Drupal Commerce 2.x that I'm just testing I don't see this option anymore, the Variations field is locked, we can't specify default variations.
Is it still possible ?