Skip to content

[5.x]: forgetCart() state is silently overwritten by loadCookie() in getCart() (regression from 4.x) #4279

@samhibberd

Description

@samhibberd

What happened?

Description

In Commerce 5.x, calling forgetCart() followed by getCart() in the same request no longer results in a new cart being created. Instead, the original cart is restored from the request cookie, effectively making forgetCart() a no-op when getCart() is called afterwards in the same request.

This is a regression from 4.x behaviour where this sequence worked as expected.

Root Cause

The 5.x getCart() method added a call to $this->loadCookie() at the top of the method ([Carts.php#L261](https://github.com/craftcms/commerce/blob/5.x/src/services/Carts.php#L261)). This method was not present in 4.x.

loadCookie() unconditionally reads the cart number from the request cookies and calls setSessionCartNumber(), overwriting the _cartNumber = false state that forgetCart() sets:

protected function loadCookie(): void
{
    // ...
    if ($requestCookies->has($this->cartCookie['name'])) {
        $this->setSessionCartNumber($requestCookies->getValue($this->cartCookie['name']));
    }
}

Steps to Reproduce

Any code that calls forgetCart() then getCart() in the same request will reproduce this:

$cartsService = \craft\commerce\Plugin::getInstance()->getCarts();

// Store original cart number
$originalCartNumber = $cartsService->getSessionCartNumber();

// Forget the current cart
$cartsService->forgetCart();

// Attempt to get a new cart
$cart = $cartsService->getCart(forceSave: true);

// Expected: $cart->number !== $originalCartNumber (new cart)
// Actual:   $cart->number === $originalCartNumber (old cart restored by loadCookie)

Expected Behaviour

forgetCart() followed by getCart() should produce a new cart with a new cart number, as it did in Commerce 4.x.

Actual Behaviour

getCart() calls loadCookie() which reads the original cart number from the request cookie (still present for the duration of the current request) and restores it via setSessionCartNumber(), overwriting the forgotten state.

Suggested Fix

loadCookie() should respect the _cartNumber = false state set by forgetCart():

protected function loadCookie(): void
{
    // ...

    // Don't restore from cookie if the cart was explicitly forgotten
    if ($this->_cartNumber === false) {
        return;
    }

    if ($requestCookies->has($this->cartCookie['name'])) {
        $this->setSessionCartNumber($requestCookies->getValue($this->cartCookie['name']));
    }
}

Environment

  • Craft Commerce: 5.x
  • Worked in: Commerce 4.x (where getCart() did not call loadCookie())

Relevant Code

Craft CMS version

5.9.19

Craft Commerce version

5.6.1.1

PHP version

No response

Operating system and version

No response

Database type and version

No response

Image driver and version

No response

Installed plugins and versions

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions