Right now 2.x has a flat product list, no variations (though we actually have implemented attributes).

While we do have plans for a hierarchical product model (and an internal design document from a few years ago which I need to transfer here), we've decided to implement the previous "product variations" architecture first, to have something functional before we start exploring the complexities of hierarchical products.
This also allows us to postpone the hierarchical product model (and/or have it in a different module) in case of time or technological constraints.

So, we end up with Product and ProductVariation entity types, where the ProductVariation is the one with the sku and the price. The Product is a simple content entity similar to Node, which has a variations reference field, and uses Inline Entity Form.

A new detail that needs to be explored is how to handle multistore here.
Our initial idea, which joshtau and vasike worked on (https://github.com/commerceguys/commerce/pull/192, the merged store PRs) was that each product (variation) belongs to a single store, selected at the same time as the bundle. This allows us to also filter the available currencies, stored on the store entity.
This works nicely for the separate-products-for-each-store use case, which is both the "Etsy" use case and the default use case (where most people will have only 1 store). However, there is also an objective need to support having the same product catalog for all stores (just like others like Magento allow). Telling people to duplicate the catalog per store even if they don't have separate products, simply to allow fine grained currency lists doesn't sound like a great tradeoff.

The solution I've had in mind is to drop the store reference from product variations, and to have a multivalue store reference on the product. A product type setting can control the field cardinality in a user friendly way ("Allow products to be available from multiple stores").
Now, the add to cart form needs to know for which store to get the cart. It would do so by getting the referenced stores, and if there's only one, take that one. If there's more than one, it would take the resolved active store (StoreContext).

This means removing the currencies reference from the store entity (making the currency status global again), and punting on the "select store first" PR. However, I believe that the benefit of being able to share products across stores make it worth it.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

rszrama’s picture

I like enabling currencies at a global level, but we can still offer a default currency per store. I'm not sure it'll be that big of a UX need, but we could always provide a store level currency list filter as well. I'd delay that for now.

Re: the CommerceProduct vs. CommerceProductVariant, I'm not sure why we need two separate entities here. I thought we'd settled on a single CommerceProduct entity type, and even though that was to support a hierarchical model where any variant could reference a parent and the product type would determine how little of a hierarchy could be displayed for purchase, I think the single entity model still works.

If it's just a question of usability re: what to do with the unused SKU / fields on a parent product, I'd prefer to solve that than use a mandatory second entity type. Can you elaborate more on what the concerns are?

bojanz’s picture

I like enabling currencies at a global level, but we can still offer a default currency per store. I'm not sure it'll be that big of a UX need, but we could always provide a store level currency list filter as well. I'd delay that for now.

A per-store default currency will stay.
There can't be a store level currency list filter because if multiple stores are allowed, those lists can conflict, which creates weird behavior.

Re: the CommerceProduct vs. CommerceProductVariant, I'm not sure why we need two separate entities here.

Because that's the current Commerce 1.x architecture (node/product, product/variation), and it's the one we understand.

. I thought we'd settled on a single CommerceProduct entity type, and even though that was to support a hierarchical model where any variant could reference a parent and the product type would determine how little of a hierarchy could be displayed for purchase, I think the single entity model still works.

If it's just a question of usability re: what to do with the unused SKU / fields on a parent product, I'd prefer to solve that than use a mandatory second entity type. Can you elaborate more on what the concerns are?

The hierarchical product work is large in scope. It involves writing a module for hierarchies (D8 tree), a new product widget (IEF won't work because the relationship is inverted), and exploring the performance implications of field inheritance. There are also the smaller details of "does this product have its own page" and how that relates to multistore (is the store selected at the top of the hierarchy or at every level that has a display? And what of conflicts?)

It's still something I want to do, in july or august, I just want to have a working model first.

rszrama’s picture

I understand that you're looking to re-implement the (1:n) ProductDisplay : Product model we have right now, not looking to revisit that decision. What I'm asking about is specifically the need for two different entity types to accomplish that.

In Commerce 1.x, we have (1:n) Node : Product. Your proposal is (1:n) Product : ProductVariant. What can't it just be (1:n) Product : Product? In this scenario, a product with no parent takes the place of Node instead of a new entity type that serves no additional purpose beyond just using Node.

I'm not suggesting you implement a hierarchy now, but I am suggesting it will be much simpler to implement one down the road if you don't have an additional entity type to contend with. If there are usability issues to resolve to make that work (i.e. we don't need a SKU for the "grouping" product), then I'd prefer to resolve those.

bbujisic’s picture

Status: Active » Needs review
FileSize
46.05 KB

I started separating product variation from product entity (and product variation types from product types).

For time being (until we figure out how to programmatically set up Inline Entity form), product variation forms have their own routes (admin/commerce/products/variations/add/product_variation).

SKU's were moved from products into variations.
Product variations do not have titles (I guessed we could use SKU's instead).
Products now have one-to-many entity reference field to product variations.

I still did not figure out how to assign a price field to product variation. I experimented with yml files in product/config/install (see patch), but without any success (well, I managed to kill the price field in product entity, but couldn't enable it in product variation).

I hope it was a good start.
Thanks!

rszrama’s picture

If I may, I'd recommend putting product routes under non-administrative paths. In other words, if a product is meant to take the place of node, and a marketplace site is meant to allow general users to create products, we can simply make it product/add and product/%commerce_product*. Variations are dependent on products, so those could reasonably be under product/%commerce_product/add-variation or even a secondary tab of the edit tab, such as product/%commerce_product/edit/variations/add. These tabs would all be access controlled, but I don't think they need to be administrative; we've boxed ourselves in in the past by making the default URI for entity types administrative.

smccabe’s picture

By hierarchical product do you mean like shirt -> blue -> large, where everything is SHIRT, some SHIRTS are BLUE, then a BLUE SHIRT can be large, so that product attributes are based on just the relevant variations? When you select variations then you'd load the relevant ones as you went along, versus loading everything at once?

We've had some problems scaling variations in Commerce 1.x, so I'm just curious as to how the new setup will work.

bojanz’s picture

By hierarchical product do you mean like shirt -> blue -> large, where everything is SHIRT, some SHIRTS are BLUE, then a BLUE SHIRT can be large, so that product attributes are based on just the relevant variations? When you select variations then you'd load the relevant ones as you went along, versus loading everything at once?

Yes, that is the plan. We're committing the initial/old architecture this week, and working on the hierarchical products from August 1st.

bojanz’s picture

I've took #4, worked on it some more, committed the result. There's more work needed, so I'll keep the issue open and the commits coming.

bojanz’s picture

Status: Needs review » Fixed

Many followups happened since then, with the last one landing today #2547555: Allow products to belong to multiple stores.
That's enough to mark this issue as fixed.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.