Drupal Commerce Add To Cart Form Tweaks / Registration Improvements
In this example I’m showing how to improve the Add to Cart button for Drupal Commerce products to show differently based on whether or not users have already purchased a product, or whether the product is already in their cart.
I’m currently working on a site where users are registering for “programs,” which are synonymous with classes, courses, etc. I’m using the following modules (among others) in this example:
- Drupal Commerce
- Registration
- Commerce Registration
Please understand that when a user registers for a course, they are actually purchasing a product that has a referenced Registration entity.
Here are the objectives I address with the code below:
Only allow the user to register once for each program (purchase a product once), and update the Add to Cart button to tell them they’ve already enrolled in the program (purchased the product).
If the user already added a program to their cart, update the Add To Cart button to tell them they’ve already added the program to their cart. As far as I can see, you could use this code for any type of product. You would simply replace “program” with the product type you wish to target. Please read the comments throughout the code to understand how the pieces work.
There are some considerations for people looking to do this with registrations, specifically. Note that the code below doesn’t handle the situation where a registration is canceled but the order isn’t updated to remove the associated program product line item. This is because we’re looking at the user’s product order history, not the user’s registration status for each program. To overcome this, instead of looking at the products a user has purchased, maybe we should look at registrations that are in the system for the user. I’ll post a comment to this post when I’ve written that functionality.
DISCLAIMER: This is not a plug-and-play module for a reason: there would be much to consider to make a contributed module that would work for every situation. This is simply an example of what you can do with a few functions for a custom implementation on a site.
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
/** * Implementation of hook_form_FORMID_alter(). */ function mymodule_form_commerce_cart_add_to_cart_form_alter(&$form, &$form_state) { $cart_product_ids = mymodule_get_products_in_cart(); $purchased_product_ids = mymodule_get_users_purchased_products(); $line_item = $form_state['line_item']; $product = commerce_product_load($line_item->commerce_product[LANGUAGE_NONE][0]['product_id']); // If this was a contrib module, we'd want to check if the associated registration // entity is set to "Allow multiple registrations" for a user instead of just // checking whether the product type is "program" as is the case for this specific site if ($product->type == 'program') { // Change the "Add to Cart" button text $form['submit']['#value'] = t('Enroll'); if (in_array($product->product_id, $cart_product_ids)) { // Product is already in cart! We only want to allow a quantity of 1, // so disable the submit button and change its text accordingly $form['submit']['#disabled'] = TRUE; $form['submit']['#value'] = t('Already in cart'); } if (in_array($product->product_id, $purchased_product_ids)) { // Product has already been purchased! // We only want users to register for a program once $form['submit']['#disabled'] = TRUE; $form['submit']['#value'] = t('Already enrolled'); } } } /** * Return the product_id values for all products in the cart * * @return * An array of product ids */ function mymodule_get_products_in_cart() { $cart_product_ids = &drupal_static(__FUNCTION__); if (!isset($cart_product_ids)) { global $user; $cart_product_ids = array(); $order = commerce_cart_order_load($user->uid); if ($order) { $order_wrapper = entity_metadata_wrapper('commerce_order', $order); foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) { $product_wrapper = $line_item_wrapper->commerce_product; $cart_product_ids[] = $product_wrapper->product_id->value(); } } $cart_product_ids = array_unique($cart_product_ids); } return $cart_product_ids; } /** * Return the product_id values for all products already purchased * * @return * An array of product ids */ function mymodule_get_users_purchased_products() { $purchased_product_ids = &drupal_static(__FUNCTION__); if (!isset($purchased_product_ids)) { global $user; $query = db_select('commerce_order', 'corder'); $query->join('commerce_line_item', 'li', 'corder.order_id = li.order_id'); $query->join('field_data_commerce_product', 'prod', 'li.line_item_id = prod.entity_id'); $query->condition('corder.uid', $user->uid, '=') ->condition('corder.status', 'completed', '=') ->fields('prod', array('commerce_product_product_id')); $result = $query->execute(); $purchased_product_ids = array_unique($result->fetchCol()); } return $purchased_product_ids; } |
Result
Here is what the output looks like:
8 Comments
Avinash Raut
Modify code to get product ids from commerce_line_item
you made some very good join it help me to get exact that i want
Thanks once again …
/******************** get Product id for your custom (business logic)
********************* add in commerce_checkout module (commerce_checkout_complete) fucntion..
*********************/
function commerce_checkout_get_users_purchased_products($order_id) {
/*start*/
global $user;
$purchased_product_ids = &drupal_static(__FUNCTION__);
if (!isset($purchased_product_ids)) {
$query = db_select(‘commerce_order’, ‘corder’);
$query->join(‘commerce_line_item’, ‘li’, ‘corder.order_id = li.order_id’);
$query->join(‘field_data_commerce_product’, ‘prod’, ‘li.line_item_id = prod.entity_id’);
$query->condition(‘corder.uid’, $user->uid, ‘=’)
->condition(‘li.order_id’,$order_id, ‘=’)
->condition(‘corder.status’, ‘pending’, ‘=’)
->fields(‘prod’, array(‘commerce_product_product_id’));
$result = $query->execute();
$purchased_product_ids = array_unique($result->fetchCol());
}
return $purchased_product_ids;
/*end*/
}
Andrew
This looks great. I am trying to do something very similar.
Where do I put this code to intigrate into my site?
Thanks for your help
Andrew
Andrew
In addition, does this apply to the active cart before checkout or is this user specific. Why I ask is that I want the user to be able to make separate bookings for different children. Each child has to have an individual order. So if this stops a user from purcvhasing two tickets to the same course it would not work for me.
Thanks again
Andrew
Chandan singh
what if, product display having more than one product in case of product variation
Juan Pablo
Hey!! thank you for this, you pointed me on the good way to write a similar module.
Thanks man!
Hans K
Thanks for this code. It works very well for one thing. I use it for the Commerce Add-to-cart button and had to change:
$form['submit']['#disabled'] = TRUE;
for:
$form['submit']['#attributes']['disabled'] = 'disabled';
Natan Miranda
Hey man, you saved my life!!! Tanks a lot! haha I was searching how check if user already purshased one product to limit only one product id by user. Perfect, tnks 😉
Juan Pablo
Hello,
recently I wrote a thanks giving comment for this approach that pointed me on the good way for my project.
But after working a while I got an error:
—
EntityMetadataWrapperException: Unknown data property commerce_product. in EntityStructureWrapper->getPropertyInfo() (line 335 of home/mysite/all/modules/contrib/entity/includes/entity.wrapper.inc
—
because your code on mymodule_get_products_in_cart() function doesn’t checks the line item type on:
$product_wrapper = $line_item_wrapper->commerce_product;
assumming that all line item types have a ‘commerce_product’ field.
But this is not the case on certain modules, for example commerce_shipping or custom_fees, that will set a line item type when you install them.
I resolved this issue adding an if inside the foreach statement checking it:
// There are ‘line_items’ as ‘shipping’ or ‘custom_fee’ without ‘commerce_product’ field
if(isset($line_item_wrapper->commerce_product)) {
$product_wrapper = $line_item_wrapper->commerce_product;
$cart_product_ids[] = $product_wrapper->product_id->value();
}
Hope this helps somebody.
And Thanks again!!