The separation of product display nodes and product entities in Drupal Commerce may confuse some people at first sight, but is an outstanding architectural decision, that distinguishes DC from other e-commerce solutions. In my eyes, this is the best, most flexible and natural way to build up a data model for an online shop. But this post is not about discussing pros and cons about this, nor it is explaining the idea behind it. There are already some great explanations out there, like the "Setting Up a Product Catalog" post in the DC documentation. There is also an excellent presentation by Richard Jones, explaining this concept and giving hints, what attributes should go where. But even if you've already built some shops with DC, there can always be some edge cases, where you have to think long and hard about where to put a specific field. One typical edge case is the categorization, and this post is about this very important decision, investigating it from some different point of views.
The semantic perspective
Ignoring all technical and data-modeling facts and influences, we can ask, where does a category belong to "naturally". At this point, you can find reasonable arguments for putting it under the display node as well as in the product entity. Categorize the product entity itself can be argued, that this is one of the product-specific properties like size or colour. For example, a BluRay Disc movie can never be an Audio CD. But otherwise like colour or size, the category is not variant specific, which is an argument against sticking it to the product entity.
Another argument pro display node is that the both the wording and granularity of categorization is a frontend-specific decision, e.g. if a music CD is categorized as "Tech-House" or just as "Electronic Music".
The data-modeling perspective
From this point of view, in most cases clearly the product display node wins. While it does not make much difference, as long as you have a 1:1 relationship between display nodes and product entities, the impact when having product variations (different colours and/or sizes, etc) is big. Normally the category is generic, so you would store category information redundantly, if you attach the field to the product entities.
The technical and structural perspective
Given the fact, that the nodes are the entities you display and navigate on the frontend, this leads us to another pro-argument for attaching the category to the node. When you want to search and navigate through categories, it's both easier and more obvious to categorize the nodes. The indirection over the product entities would make site-building more complicate and could also hit performance negatively, when it leads to more complex database queries.
In the interim result, the display node is clearly the winner in most cases - if there wasn't...
Commerce Discount - the kill-joy
Managing discounts in DC isn't really one of their strengths. Based on the Rules module, you get both the amazing power and flexibility of Rules and the its complexity, which is in most cases a too big obstacle for merchants to handle. This is the point, where Commerce Discount jumps in, as it provides a more user-friendly UI to manage discounts, while it is still on top of the built-in, Rules-based discount system. It is extendable, but also provides some default conditions for building discounts. One very important of them is discounting items based on taxonomy terms. Naturally, it only uses fields directly attached to the product entity. So, if you 1. want to attach categories to the node and 2. want to use Commerce Discounts, and 3. also want to define category-based discounts, you are going round in circles.
What are the possible solutions and workarounds, we have in this situation?
Moving category field from node display to product entity
Like discussed above, this is a possible way to categorize your shop products. But also, it could make you life harder in other parts (navigation, etc) and also lead to redundant information (when having product variations). Additionally, why should we change our wisely chosen architectual decision just for the sake of using a certain module? If managing discount is the only reason, going this way would be really just a workaround, no real solution.
Attaching category both to nodes AND product entities
I'm not quite sure, if I really should mention this because it's definitley the worst and most desperate way we could solve our problem. Why?
- because of the data redundancy
- As a consequence we would run into danger of getting messed-up data, if you don't maintain your content properly. The least thing, we would have to do, is to ensure programmatically, that in no case, the categories of a node display and its product variation(s) can differ, e.g. hiding the fields from the entities in the edit form and setting and saving the values on the product entities automatically, when saving the node.
- Did I already mention the redundant data??
Provide your own inline conditions for use with Commere Discount
This is imho the best way to solve the problem and the only real solution, that is free of hacks and workarounds. Although the other two solutions are far more easier to accomplish, I would strongly recommend this way. Basically, you have to do three things:
- Implement hook_inline_conditions_info() inlcuding configure callback to provide your own Inline conditions
- Implement hook_rules_condition_info() inlcuding build callback to provide Rules condition that are built by your Inline condition(s)
- Implement a custom function that queries the product entities by a given category (joining over the display nodes)
We did that in a recent project. And my next blog post will show you, how we have accomplished this...
Conclusion
We have seen, that in most cases it is recommended to attach the categorization fields to the node. But like always, in specific use-cases other solutions may be preferable. Therefore: take your time to think about the data structure at the beginning of every new project, and try to resist to (seemingly) easy workarounds, if a single problem interferes with your decision that still is your favourite. If you're facing the same problem with Commerce Discount, be prepared for my next blog post, where I will explain, how we solved it by providing our own inline conditions. Hopefully I've enough time for writing within the next days - at the lates in the Christmas holidays...