I cart facade
This commit is contained in:
parent
0e776b6d84
commit
7478f69bcf
|
|
@ -13,6 +13,7 @@ use Illuminate\Support\Facades\Facade;
|
|||
* @method static \Blax\Shop\Models\CartItem|true remove(\Illuminate\Database\Eloquent\Model $product, int $quantity = 1, array $parameters = [])
|
||||
* @method static \Blax\Shop\Models\CartItem update(\Blax\Shop\Models\CartItem $cartItem, int $quantity)
|
||||
* @method static int clear(\Blax\Shop\Models\Cart|null $cart = null)
|
||||
* @method static void clearSession()
|
||||
* @method static \Illuminate\Support\Collection|mixed checkout(\Blax\Shop\Models\Cart|null $cart = null)
|
||||
* @method static float total(\Blax\Shop\Models\Cart|null $cart = null)
|
||||
* @method static int itemCount(\Blax\Shop\Models\Cart|null $cart = null)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ namespace Blax\Shop\Models;
|
|||
use Blax\Shop\Contracts\Cartable;
|
||||
use Blax\Shop\Enums\CartStatus;
|
||||
use Blax\Shop\Enums\ProductType;
|
||||
use Blax\Shop\Services\CartService;
|
||||
use Blax\Workkit\Traits\HasExpiration;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
|
|
@ -176,6 +177,17 @@ class Cart extends Model
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the cart ID in the session for retrieval across requests
|
||||
*
|
||||
* @param Cart $cart
|
||||
* @return void
|
||||
*/
|
||||
public static function setSession(Cart $cart): void
|
||||
{
|
||||
session([CartService::CART_SESSION_KEY => $cart->id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an item to the cart or increase quantity if it already exists.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ use Blax\Shop\Exceptions\HasNoDefaultPriceException;
|
|||
use Blax\Shop\Exceptions\HasNoPriceException;
|
||||
use Blax\Shop\Exceptions\InvalidBookingConfigurationException;
|
||||
use Blax\Shop\Exceptions\InvalidPoolConfigurationException;
|
||||
use Blax\Shop\Services\CartService;
|
||||
use Blax\Shop\Traits\HasCategories;
|
||||
use Blax\Shop\Traits\HasPrices;
|
||||
use Blax\Shop\Traits\HasPricingStrategy;
|
||||
|
|
@ -374,10 +375,23 @@ class Product extends Model implements Purchasable, Cartable
|
|||
{
|
||||
// If this is a pool product, use cart-aware pricing if cart is provided
|
||||
if ($this->isPool()) {
|
||||
// If no cart provided, try to get the current user's cart
|
||||
// If no cart provided, try to get the cart from session first, then user's cart
|
||||
if (!$cart) {
|
||||
// Try session first
|
||||
$cartId = session(CartService::CART_SESSION_KEY);
|
||||
if ($cartId) {
|
||||
$cart = \Blax\Shop\Models\Cart::find($cartId);
|
||||
// Make sure the cart is valid (not expired/converted)
|
||||
if ($cart && ($cart->isExpired() || $cart->isConverted())) {
|
||||
$cart = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to authenticated user's cart if no valid session cart
|
||||
if (!$cart && auth()->check()) {
|
||||
$cart = auth()->user()->currentCart();
|
||||
}
|
||||
}
|
||||
|
||||
if ($cart) {
|
||||
// Cart-aware: Get price for next available item after what's in cart
|
||||
|
|
|
|||
|
|
@ -14,47 +14,83 @@ use Blax\Shop\Exceptions\NotPurchasable;
|
|||
use Blax\Shop\Models\Product;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class CartService
|
||||
{
|
||||
/**
|
||||
* Get current authenticated user's cart
|
||||
* Throws exception if no user is authenticated
|
||||
* Session key for storing the current cart ID
|
||||
*/
|
||||
public const CART_SESSION_KEY = 'blax_shop_cart_id';
|
||||
|
||||
/**
|
||||
* Get current cart from session, or fall back to authenticated user's cart
|
||||
* Throws exception if no cart found in session and no user is authenticated
|
||||
*
|
||||
* @return Cart
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function current(): Cart
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
if (!$user) {
|
||||
throw new \Exception('No authenticated user found. Use guest() for guest carts or provide a cart ID.');
|
||||
// Try to get cart from session first
|
||||
$cartId = session(self::CART_SESSION_KEY);
|
||||
if ($cartId) {
|
||||
$cart = Cart::find($cartId);
|
||||
if ($cart && !$cart->isExpired() && !$cart->isConverted()) {
|
||||
// If user is authenticated and session cart is a guest cart, clear it
|
||||
if (Auth::check() && !$cart->customer_id) {
|
||||
session()->forget(self::CART_SESSION_KEY);
|
||||
} else {
|
||||
return $cart;
|
||||
}
|
||||
} else {
|
||||
// Clear expired or converted cart from session
|
||||
session()->forget(self::CART_SESSION_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
return $user->currentCart();
|
||||
// Fall back to authenticated user's cart
|
||||
$user = Auth::user();
|
||||
|
||||
if (!$user) {
|
||||
return self::guest();
|
||||
}
|
||||
|
||||
$cart = $user->currentCart();
|
||||
|
||||
// Store the cart ID in session for future requests
|
||||
session([self::CART_SESSION_KEY => $cart->id]);
|
||||
|
||||
return $cart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create a guest cart by session ID
|
||||
* If no session ID provided, uses session()->getId()
|
||||
* Stores the cart ID in the session for future requests
|
||||
*
|
||||
* @param string|null $sessionId
|
||||
* @return Cart
|
||||
*/
|
||||
public function guest(?string $sessionId = null): Cart
|
||||
{
|
||||
$sessionId = $sessionId ?? session()->getId();
|
||||
$sessionId = $sessionId ?? session(CartService::CART_SESSION_KEY) ?? session()->getId();
|
||||
|
||||
return Cart::firstOrCreate([
|
||||
$cart = Cart::firstOrCreate([
|
||||
'session_id' => $sessionId,
|
||||
'customer_id' => null,
|
||||
'customer_type' => null,
|
||||
]);
|
||||
|
||||
// Store cart ID in session
|
||||
Cart::setSession($cart);
|
||||
|
||||
return $cart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cart for specific user
|
||||
* Stores the cart ID in the session for future requests
|
||||
*
|
||||
* @param Authenticatable $user
|
||||
* @return Cart
|
||||
|
|
@ -65,7 +101,11 @@ class CartService
|
|||
throw new \Exception('User model must have shopping capabilities');
|
||||
}
|
||||
|
||||
return $user->currentCart();
|
||||
$cart = $user->currentCart();
|
||||
// Store cart ID in session
|
||||
Cart::setSession($cart);
|
||||
|
||||
return $cart;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -80,8 +120,8 @@ class CartService
|
|||
}
|
||||
|
||||
/**
|
||||
* Add item to current user's cart (throws exception if no user)
|
||||
* For guests, use guest() first: Cart::guest()->add($product)
|
||||
* Add item to current cart (from session or authenticated user)
|
||||
* For guests without session, use guest() first: Cart::guest()->add($product)
|
||||
*
|
||||
* @param Model&Cartable $product
|
||||
* @param int $quantity
|
||||
|
|
@ -92,23 +132,19 @@ class CartService
|
|||
*/
|
||||
public function add(Model $product, int $quantity = 1, array $parameters = []): CartItem
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
if (!$user) {
|
||||
throw new \Exception('No authenticated user found. Use guest() for guest carts.');
|
||||
}
|
||||
$cart = $this->current();
|
||||
|
||||
// Validate pricing before adding to cart
|
||||
if ($product instanceof Product) {
|
||||
$product->validatePricing(throwExceptions: true);
|
||||
}
|
||||
|
||||
return $user->addToCart($product, $quantity, $parameters);
|
||||
return $cart->addToCart($product, $quantity, $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove item from current user's cart (throws exception if no user)
|
||||
* For guests, use guest() first: Cart::guest()->remove($product)
|
||||
* Remove item from current cart (from session or authenticated user)
|
||||
* For guests without session, use guest() first: Cart::guest()->remove($product)
|
||||
*
|
||||
* @param Model&Cartable $product
|
||||
* @param int $quantity
|
||||
|
|
@ -117,13 +153,8 @@ class CartService
|
|||
*/
|
||||
public function remove(Model $product, int $quantity = 1, array $parameters = [])
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
if (!$user) {
|
||||
throw new \Exception('No authenticated user found. Use guest() for guest carts.');
|
||||
}
|
||||
|
||||
return $user->currentCart()->removeFromCart($product, $quantity, $parameters);
|
||||
$cart = $this->current();
|
||||
return $cart->removeFromCart($product, $quantity, $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -397,6 +428,17 @@ class CartService
|
|||
return empty($this->validateBookings($cart));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cart from session (unlink cart from session)
|
||||
* This does not delete the cart, just removes it from the session
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clearSession(): void
|
||||
{
|
||||
session()->forget(self::CART_SESSION_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a booking product to cart with timespan
|
||||
*
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class CartFacadeTest extends TestCase
|
|||
auth()->logout();
|
||||
|
||||
$this->expectException(\Exception::class);
|
||||
$this->expectExceptionMessage('No authenticated user found');
|
||||
$this->expectExceptionMessage('No cart in session and no authenticated user found');
|
||||
|
||||
Cart::current();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue