Hi !

I'm starting using Drupal Commerce and i'm facing a problem.
I'm developing an assurance website.

My problem: the user can add only one item in his cart. Cause he can't subscribe to several offers simultaneously.
So how can i do that in drupal commerce ?

For the moment i've created a new module called "custom_cart" which implements this hook :

function custom_cart_commerce_order_presave($order) {
  $order_wrapper = entity_metadata_wrapper('commerce_order', $order);

  // Build an array of product line item IDs.
  $line_item_ids = array();

  foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {
    if ($line_item_wrapper->type->value() == 'product') {
      $line_item_ids[] = $line_item_wrapper->line_item_id->value();
	  $last_line_item_id = $line_item_wrapper->line_item_id->value();
    }
  }

  // Delete each line item one by one from the order. This is done this way
  // instead of unsetting each as we find it to ensure that changing delta
  // values don't prevent an item from being removed from the order.
  foreach ($line_item_ids as $line_item_id) {
    if($line_item_id != $last_line_item_id) {
	  $order = commerce_cart_order_product_line_item_delete($order, $line_item_id, TRUE);
    }
  }
}

This code is inspired from the function "commerce_cart_order_empty($order)". I've only added a condition to save the last line.

But ! When i use that code, I have this issue :
Notice : Undefined property: stdClass::$is_new dans CommerceOrderEntityController->save() (ligne 122 dans C:\xampp\htdocs\assur4pattes\sites\all\modules\commerce\modules\order\includes\commerce_order.controller.inc).
Notice : Undefined property: stdClass::$is_new dans CommerceOrderEntityController->save() (ligne 142 dans C:\xampp\htdocs\assur4pattes\sites\all\modules\commerce\modules\order\includes\commerce_order.controller.inc).
Notice : Undefined property: stdClass::$is_new dans CommerceOrderEntityController->save() (ligne 148 dans C:\xampp\htdocs\assur4pattes\sites\all\modules\commerce\modules\order\includes\commerce_order.controller.inc).

Am I completely taking it in the wrong way ?

thanks !

Comments

rszrama’s picture

Can you build this using Rules? There are events pertaining to adding items to the cart, and it should be possible for you to delete any existing line items in the order before adding the new one... that could be a bit of overkill, and it would certainly help if there was a Rules action for emptying the cart. I'll have to add that...

Flowster’s picture

Thanks a lot !
I'll try to add this rule on my own ;).

I'm starting using Drupal since last week for an internship in a French web agency, it's still a bit confusing for me right now, but I'm not gonna giving up !

Flowster’s picture

And there it is !

I've created a module named custom_cart in the commerce modules.
And created the following files :

commerce_custom_cart.rules.inc

/**
 * @file
 * Rules integration for custom_cart.
 *
 * @addtogroup rules
 * @{
 */
 
/**
 * Implements hook_rules_action_info().
 */
function commerce_custom_cart_rules_action_info() {
  $actions = array();
  
  $actions['commerce_custom_cart_empty_cart'] = array(
    'label' => t('Empty the cart'),
	'parameter' => array(
      'order' => array(
        'type' => 'commerce_order',
        'label' => t('Order to empty'),
      ),
	),
    'group' => t('Commerce Cart'),
    'callbacks' => array(
      'execute' => 'commerce_custom_cart_rules_empty_cart',
    ),
  );

  return $actions;
}

/**
 * Rules action: Empty the cart
 */
function commerce_custom_cart_rules_empty_cart($order) {
  commerce_cart_order_empty($order);
}

commerce_custom_cart.rules_defaults.inc

/**
 * @file
 * Default rule configurations for Cart.
 */

/**
 * Implements hook_default_rules_configuration().
 */
function commerce_custom_cart_default_rules_configuration() {
  $rules = array();

  // Add a reaction rule to empty a shopping cart order
  // before a product is added to.
  $rule = rules_reaction_rule();

  $rule->label = t('Empty the cart order before product add');
  $rule->active = FALSE;

  $rule
    ->event('commerce_cart_product_prepare')
    ->action('commerce_custom_cart_empty_cart', array(
      'order:select' => 'order',
    ));

  $rules['commerce_custom_cart_empty_cart'] = $rule;

  return $rules;
}

I've then activate the rule in the configuration page and it works ! (okay I may have had some issues but it finally works ;-) ).

Is it all good this time ?

Flowster’s picture

Status: Active » Fixed
rszrama’s picture

Excellent. I've modified the code and added it straight to the Cart module. It won't conflict with your custom module, but you can convert over to the new action if you want.

Flowster’s picture

Ok, thanks !
My first (but not last) contribution to Drupal.

One last question.
I have corrupted my database with orders that points to line item objects that doesn't exist anymore.
What's the simplest way to clean the database without touching the products ?

rszrama’s picture

Just start truncating tables. ^_^

You'll need to truncate the order and order revisions tables and then the tables of order fields and their revisions. Last, make sure you clear your cache when you're done. There isn't really a simple way, though...

Status: Fixed » Closed (fixed)

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

Weaver’s picture

Is there a way to clear the cart and add item to cart in php (in a custom module) using API's?

Is there any commerce API documentation? (and yes, I have googled extensively thankyou)

rszrama’s picture

We're working on setting up an API subsite of DC.org using the same API module powering api.d.o. In the meantime, check out the Specification handbook on DC.org, as it contains articles on various aspects of the APIs.

thomjjames’s picture

Hi,

This seems like an old issue now but wanted to add something I think would be super useful, a rules action to either:

1. Block an item from being added to cart (not sure that's possible?) OR
2. Remove all items of a certain type or SKU from the order

Senario I have now is a site which sells subscriptions & other products, like Flowster only one subscription can be added per cart, but other products have no limit so I can't wipe the whole order.
Wonder if this might be better fit for a contrib module?

Cheers
Tom

rszrama’s picture

You can achieve that purely through Rules, even if it is a little redundant to remove a line item you've just added in the case of point 1. For point 2, you'd simply have to loop over the order's line items and pass them one by one to a rules component that checks the line item type / referenced product and deletes it if necessary.

nithinkolekar’s picture

I am also looking for same feature but couldn't find in rules section.Is it still available with cart module(v 7.x-1.8) or removed?

Marko B’s picture

Add a rule:

Event: before add product to cart,
Action: remove all products from order.

Existing products are removed and only the latest selected product is added to the cart.

bsandor’s picture

I used following code for achieving it:

/**
 * Implementing hook_commerce_cart_product_prepare()
 *
 **/
function MY_MODULNAME_commerce_cart_product_prepare($order, $product, $line_item) {
	commerce_cart_order_empty($order);
}
roball’s picture

Title: Allow only one item per cart. » How to remove all products from an order (clear the cart) before adding a new one
Version: 7.x-1.0-beta2 » 7.x-1.11

Thank you bsandor for your suggestion. Using a solution based on that code is the best for me, since it allows the greatest flexibility. I am now using the following code that clears the existing cart before adding a product to it if contains a line item of a certain type (in this case "registration"):

/**
 * Implements hook_commerce_cart_product_prepare().
 *
 * Extends commerce_cart.module
 */
function MYMODULE_commerce_cart_product_prepare($order, $product, $quantity) {
  $line_items_count = count($order->commerce_line_items['und']);
  // The number of line items in the cart before adding the new product.
  if (!$line_items_count) {
    // The cart is empty - nothing to do.
    return;
  }

  // The cart already contains one or more line items.
  $clear_cart = FALSE;
  for ($i = 0; $i < $line_items_count; $i++) {
    // Loop through all line items in the cart.
    $line_item_id = $order->commerce_line_items['und'][$i]['line_item_id'];
    $line_item = commerce_line_item_load($line_item_id);
    if ($line_item->type == 'registration') {
      // The cart contains a line item of type "registration".
      $clear_cart = TRUE;
      $i = $line_items_count;
      $message = 'Some explanation to the customer here. ';
      $message .= 'The existing cart have been cleared before adding the new product.';
    }
  }

  if ($clear_cart) {
    commerce_cart_order_empty($order);
    // Deletes every product line item from the shopping cart order.
    // Equals to the rules action "Remove all products from an order".
    drupal_set_message(
      $message,
      'warning',
      FALSE
    );
  }
}

Note that if you want that code to also work with registrations added to the cart via the Registration Commerce module, the patch from #2452345: hook_commerce_cart_product_prepare and hook_commerce_cart_product_add never invoked must be applied to to registration_commerce.module.