diff --git a/src/services/Carts.php b/src/services/Carts.php index cde1ccd525..e8de0cea92 100644 --- a/src/services/Carts.php +++ b/src/services/Carts.php @@ -128,12 +128,33 @@ public function init() * Get the current cart for this session. * * @param bool $forceSave Force the cart. + * @param bool $readOnly Whether to retrieve the cart in read-only mode. * @throws ElementNotFoundException * @throws Exception * @throws Throwable */ - public function getCart(bool $forceSave = false): Order + public function getCart(bool $forceSave = false, bool $readOnly = false): ?Order { + if ($readOnly) { + if (isset($this->_cart)) { + return $this->_cart; + } + + if (!$this->getHasSessionCartNumber()) { + return null; + } + + $request = Craft::$app->getRequest(); + $number = $request->getCookies()->getValue($this->cartCookie['name'], false); + if (!$number) { + return null; + } + + $this->_cartNumber = $number; + $this->_cart = $this->_getCart(false, false); + return $this->_cart; + } + $this->loadCookie(); // TODO: need to see if this should be added to other runtime methods too $this->_getCartCount++; //useful when debugging @@ -212,7 +233,7 @@ public function getCart(bool $forceSave = false): Order /** * Get the current cart for this session. */ - private function _getCart(): ?Order + private function _getCart(bool $forgetInvalidCart = true, bool $checkAnonymousCartSession = true): ?Order { $number = $this->getSessionCartNumber(); /** @var Order|null $cart */ @@ -227,7 +248,9 @@ private function _getCart(): ?Order // If the cart is already completed or trashed, forget the cart and start again. if ($cart && ($cart->isCompleted || $cart->trashed)) { - $this->forgetCart(); + if ($forgetInvalidCart) { + $this->forgetCart(); + } return null; } @@ -237,7 +260,10 @@ private function _getCart(): ?Order // Did an anonymous user provide an email that belonged to a credentialed user? // See CartController::actionUpdate() - $anonymousCartWithCredentialedCustomer = $cart && Craft::$app->getSession()->get('commerce:anonymousCartWithCredentialedCustomer:' . $cart->number, false); + $anonymousCartWithCredentialedCustomer = false; + if ($checkAnonymousCartSession && $cart) { + $anonymousCartWithCredentialedCustomer = Craft::$app->getSession()->get('commerce:anonymousCartWithCredentialedCustomer:' . $cart->number, false); + } if ($cart && $cartCustomer && $cartCustomer->getIsCredentialed() && ( @@ -248,7 +274,9 @@ private function _getCart(): ?Order ($currentUser && $currentUser->id != $cartCustomer->id) ) ) { - $this->forgetCart(); + if ($forgetInvalidCart) { + $this->forgetCart(); + } return null; } diff --git a/tests/unit/services/CartsTest.php b/tests/unit/services/CartsTest.php index fb94bc51ad..25bda59f64 100644 --- a/tests/unit/services/CartsTest.php +++ b/tests/unit/services/CartsTest.php @@ -16,6 +16,7 @@ use craftcommercetests\fixtures\CustomerAddressFixture; use craftcommercetests\fixtures\CustomerFixture; use UnitTester; +use yii\web\Cookie; /** * CartsTest. @@ -140,4 +141,29 @@ public function testGetCartSwitchCustomer(): void Craft::$app->getUser()->setIdentity($originalIdentity); Craft::$app->getElements()->deleteElement($cart, true); } + + public function testGetCartReadOnlyModeDoesNotStartCartSession(): void + { + $cartNumber = Plugin::getInstance()->getCarts()->generateCartNumber(); + + $order = new Order(); + $order->number = $cartNumber; + Craft::$app->getElements()->saveElement($order, false); + + $carts = $this->make(Carts::class, [ + 'setSessionCartNumber' => function() { + self::fail('Read-only cart retrieval should not update the cart session.'); + }, + ]); + Plugin::getInstance()->set('carts', $carts); + Craft::$app->getRequest()->getCookies()->add(new Cookie([ + 'name' => $carts->cartCookie['name'], + 'value' => $cartNumber, + ])); + + $cart = Plugin::getInstance()->getCarts()->getCart(readOnly: true); + + self::assertNotNull($cart); + self::assertSame($cartNumber, $cart->number); + } }