Hi,

i was faced to the problem, that I added some items to cart as anonymous user, whereas my personal cart (cart_id = uid) was empty. After login, the items have been shifted to my personal cart by uc_cart_login_update(). But in this scenario there is no cart-content rebuild caused by a faulty condition. So I had problems with uc_cart_get_contents() called afterwards during page call.

Cart rebuild and any consolidation actions for items only are relevant, if there was a shift of any cart item to my personal cart. Otherwise, there is no need to do anything.

I added a patch for this against 6.x-2.4. (Tested successfully with version 6.x-2.2)

Comments

TS79’s picture

why is nobody interested in fixing an obvious bug with a no-brainer patch?

longwave’s picture

Shouldn't this patch be checking db_affected_rows() instead of the result of db_query()?

TS79’s picture

yes, you are right. i did not recognise any failure with db_query(), since the action "content-rebuild", which has been performed always, does not effect anything negative.

but really should be db_affected_rows()

do you require a new patch or can you change this while committing?

longwave’s picture

I have no commit access to this project; I suspect the maintainers will want a fully working patch they can apply immediately.

TS79’s picture

StatusFileSize
new895 bytes

voila, the corrected patch; but any hope, that this gets committed within the next weeks does not exist ;)

tr’s picture

Version: 6.x-2.4 » 6.x-2.x-dev
Status: Needs review » Needs work

Kicking the bot to get it to test the patch.

tr’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, uc_cart_login_update_V2.patch, failed testing.

TS79’s picture

Status: Needs work » Needs review
StatusFileSize
new813 bytes

next try

longwave’s picture

I am still slightly confused as to what this actually fixes. Can you demonstrate the issue in core alone, or does it require custom code?

TS79’s picture

if you are confused, then please answer first the question for yourself, why this if-clause exists in source code after "merging two carts".

if you have this answer, you can read my issue description again and apply the given scenario (empty cart of logged user; filled cart of anonymous user).
after all there should be confusion anymore.

with the bugfix, the $items object holds the merged cart at end of function in the given scenario. without the bugfix, the $items object does not hold the merged cart.

longwave’s picture

But $items isn't returned so what difference does this make? The point of that code is to merge identical items, but if the logged in user has an empty cart, there is nothing to merge.

longwave’s picture

StatusFileSize
new1.7 KB

I am still confused by the original uc_cart_login_update() and why it does things the way it does. The attached patch/code below makes it much simpler and it still seems to work, please review.

function uc_cart_login_update($uid) {
  if (!isset($_SESSION['uc_cart_id'])) {
    return;
  }

  // If there are items in the anonymous cart, consolidate them.
  if ($items = uc_cart_get_contents($_SESSION['uc_cart_id'])) {
    // Remove the anonymous cart items.
    db_query("DELETE FROM {uc_cart_products} WHERE cart_id = '%s'", $_SESSION['uc_cart_id']);

    // Merge the anonymous items into the user cart.
    foreach ($items as $key => $item) {
      uc_cart_add_item($item->nid, $item->qty, $item->data, $uid, FALSE, FALSE, FALSE);
    }

    // Unset the anonymous cart ID, it's no longer needed
    unset($_SESSION['uc_cart_id']);
  }
}
TS79’s picture

the 'rebuild' parameter of uc_cart_get_contents() is the key. if you do not rebuild the cart with the "new merged items", any call of uc_cart_get_contents() afterwards(!!!) without the 'rebuild' parameter fails. the "old static cashed cart items" (not the merged ones) are returned there. these calls of uc_cart_get_contents() can be implemented in any other function during page load (e.g. rules/actions, which are triggered afterwards).

longwave’s picture

uc_cart_add_item() calls uc_cart_get_contents($cart_id, 'rebuild') so the suggested improvement in #13 should handle this. Can you try that patch and see if it helps for your case? As I still don't know how to demonstrate this as an actual bug, I need someone else to test things for me.

TS79’s picture

except the rebuild issue, there is another item consolidation in current implementation: quantities of the same order product are added via uc_cart_get_contents(). in your patch, item consolidation happens within uc_cart_add_item() and leads to call of 'hook_update_cart_item'. this call is not in current implementation at login-time and i think this is correct. the call of 'hook_update_cart_item' was already triggered before, when anonymous user added the item to cart.

longwave’s picture

In the original code doesn't hook_update_cart_item get called as well? Seems that uc_cart_add_item() is called for every item in both the anonymous and authenticated carts, which should trigger this hook when consolidating identical items?

TS79’s picture

no, hook_update_cart_item is not called, since cart for logged user is deleted before the merged items are added again to the cart of the logged user:

function uc_cart_add_item(..) {
...
  // If the item isn't in the cart yet, add it.
  if (is_null($item) || $item === FALSE) {
    db_query("INSERT INTO {uc_cart_products} (cart_id, nid, qty, changed, data) VALUES ('%s', %d, %d, %d, '%s')", $cid, $node->nid, $qty, time(), serialize($data));
    if ($msg) {
      drupal_set_message(t('<strong>@product-title</strong> added to <a href="!url">your shopping cart</a>.', array('@product-title' => $node->title, '!url' => url('cart'))));
    }
  }
  else {
    // Update the item instead.
    if ($msg) {
      drupal_set_message(t('Your item(s) have been updated.'));
    }
    $qty += $item->qty;
    module_invoke($data['module'], 'update_cart_item', $node->nid, $data, min($qty, 999999), $cid);
  }
longwave’s picture

But won't hook_update_cart_item() still be triggered in both cases only if both carts have an identical item (with the same nid and data), which is the point of consolidating the items?

longwave’s picture

An example, let's assume attributes etc are disabled so $data is empty. Anonymous cart has nids 1 and 2. Authenticated cart has nids 2 and 3.

The original code:
- merges the carts so the authenticated cart contains items with nids 1, 2, 2 and 3.
- deletes all items
- calls uc_cart_add_item for each item, adding nid 1, nid 2, nid 2 again (triggering hook_update_cart_item) and nid 3

The simplified code:
- calls uc_cart_add_item for each item in the anonymous cart, adding nid 1 (no trigger, as it's not in the authenticated cart) and nid 2 (triggering hook_update_cart_item, as it's already in the authenticated cart)

TS79’s picture

hm, no, there is a consolidation of items for the same nid in uc_cart_get_contents()

The original code:
- nid 1 and 2 are "shifted" to authenticated cart
- now there are in DB: nid 1, nid 2, nid 2 and nid 3 (all qty 1)
- all items of the authenticated cart are loaded and consolidated via uc_cart_get_contents():
.... nid 1 (qty 1), nid 2 (qty 2), nid 3 (qty 1)... this is the new merged cart
- deletes all items in DB
- calls uc_cart_add_item for each item, adding nid 1, nid 2 (with qty 2) and nid 3 to DB
... no call of hook_update_cart_item

longwave’s picture

uc_cart_get_contents() doesn't perform consolidation:

    $result = db_query("SELECT * FROM {uc_cart_products} WHERE cart_id = '%s' ORDER BY cart_item_id ASC", $cid);

    while ($item = db_fetch_object($result)) {
      if ($item = uc_cart_get_item($item)) {
        $items[$cid][] = $item;
      }
    }

There was code in there that *tried* to do this before, but it never worked because it compared the existing unserialized data items to the serialized version.

longwave’s picture

I think calling hook_update_cart_item() is actually the right thing to do here, it is not a proper hook (only invoked on the module that owns the product), and perhaps a product type module would want to know when an anonymous cart and authenticated cart with the same items were being merged.

uc_product_update_cart_item() will be the function called in 99% of cases and this is necessary to update the quantity field in the db anyway!

TS79’s picture

There was code in there that *tried* to do this before

... jup, I am on an old UC version (2.3). upgrading and testing anything is not possible for me at the moment. but if this old code is eliminated, your patch might work.

longwave’s picture

Status: Needs review » Fixed

Committed #13 to both branches.

Status: Fixed » Closed (fixed)

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