Today I'm introducing you to a new contrib module, I've created for allowing "add to cart" (or wishlist) buttons as links instead of forms. This helps to circumvent some unfortunate Drupal core limitations, when you want to build overview pages or blocks.
Have you ever built blocks or pages containing product listings, like category pages, bestseller blocks or related product blocks? Have you included the add to cart form to them, in order to be able to add items directly from the overview page? Currently, building product listing blocks is often accompanied with some headache due to existing Drupal Core limitations, when you want to include the form. That's why I have written the Drupal Commerce Add To Cart Link module. Here's an overview about the limitations I'm talking about:
Disappearing forms
The biggest problem is that Drupal forms won't work when they disappear on the submitting request, like in the following scenario:
You want to build a "related products" block, showing up to 4 related products. Let's say, there are 20 possible candidates for a given product, and you want to choose the 4 products randomly. You may want to set some Cache conditions though, but even then the 4 shown products are subject to change.
So, what happens, if you use a form to show the add to cart button, and the selected product is no longer displayed in that request you pressed the "add to cart" button? Nothing, just nothing happens. No message, no warning, no error, but also no product in your cart. That's just, how Form API works in Drupal, and there's little to nothing that Commerce could do to prevent that.
Offering a dedicated "add to cart" link instead of using the Form API prevents that problem.
Ajaxified Views are hijacking your forms
Ever tried to build a View with Ajax enabled (eg. for infinite pagination) and list products including the add to cart form? You'll fail. The forms will only work on initial page load. After the first Views Ajax link was clicked, you'll run into 404 errors because Views has "stolen" the forms from Commerce. This is actually very mean, as you might oversee this malfunction, if you first build and test your views, later add the Ajax paging and just quickly try it, first adding something to the cart, next do some paging - you won't see that immediately probably.
Normally, you only have two choices now: you can now either disable Ajax on that view or remove the add to cart form completely from the teaser view mode and only link to the detail page.
Again, eliminating the form here and replacing it by a link will not break anything and allows peaceful co-existence of an Ajax enabled View and the cart button.
See these links for more information on this topic:
- https://www.drupal.org/project/commerce/issues/2916671
- https://www.drupal.org/node/2185239#comment-8431647
Only show the default variation in the catalog view
Not an issue, but something you'll have to do with form alteration normally: if you have products with multiple variations, you'll probably do not want to have the variations selector on overview pages and other product listings. It's easy though to hide the selector, but you need to implement a hook_form_alter(), which may be a problem for non-developers. By using Commerce Add To Cart Link module, you can achieve that with sole entity view configuration.
Introducing Commerce Add To Cart Link
The problems described above were the main motivation behind implementing Commerce Add To Cart Link module. The module is adding a "add_to_cart_link" pseudo field (via hook_entity_extra_field_info()) to both Product and Product Variation entities. Why both? Because semantically, it belongs to the variation, and being there offers the most flexibility, but is also more complicated to setup correctly and brings some needless overhead for very simple use cases. It's more flexible, because on products with multiple variations a link is printed for every single variation. With preprocessors or view alter implementation, one could always decide to show only one link. But again, this would require some extra coding. Further, it wouldn't show up on the injected variation fields in the product template, so you would have to set up a custom view mode for variations, that only contains that link field, and configure the variations reference field to render the variation in that view mode -> needless complexity.
So, if you only have 1:1 references to variations, or anyway want to show a single "add to cart" button for the default variation only, configuring the field on the Product entity view display, is much easier: just the drag the field into visibilitty on the desired product view display, and you're finished! Btw, the fields are automatically available for all your product and product variation bundles.
Gettin' Twiggy with it
To offer best comfort and flexibility, the link is rendered via Twig template, letting you easily customize the appearance of the link. You have full Twig power and can extend and enrich the markup as you want. The link text is also part of the template and therefore easily swappable. Use an icon? No problem, insert it in your Twig template and it's done. You can even provide different templates per variation type or ID.
That's it. Enjoy the module - may it help you as much as it does for us :)
PS: The links are secured with a CSRF token, so that users can't be tricked into clicking a link and adding products to cart they don't want. In order to have these tokens working accordingly, a session for anonymous user will be enforced, if there isn't one existing already.
You're welcome :)
But there's still space for improvements. Doing Ajax calls instead of doing the redirect would be a great enhancement e.g
Hi, no. these field ormatters are designed for product and product variation entities. You'll need a custom solution, but you probably can inspire yourself from the module's source code.
Anyhow, I'm wondering how your setup does actually look like. Are refererncing product variations directly from a node? or do you have implemented your own PurchasableEntity class?