<?php
namespace App\Services;
use App\Model\DataObject\Customer;
use App\Model\DataObject\Sample;
use Carbon\Carbon;
use CustomerManagementFrameworkBundle\Model\CustomerInterface;
use Pimcore\Bundle\EcommerceFrameworkBundle\CartManager\CartInterface;
use Pimcore\Bundle\EcommerceFrameworkBundle\CartManager\SessionCart;
use Pimcore\Bundle\EcommerceFrameworkBundle\EnvironmentInterface;
use Pimcore\Bundle\EcommerceFrameworkBundle\Factory;
use Pimcore\Bundle\EcommerceFrameworkBundle\Model\CheckoutableInterface;
use Pimcore\Model\DataObject\OnlineShopOrder;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Security\Core\Security;
class CartService {
public const CART_KEY = 'cart';
public const GUEST_ORDER_LIMIT = 3;
public const PRODUCT_ORDER_LIMIT = 3;
public const COOKIE_GUEST_LIMIT = 'cookie_guest_limit';
protected const COOKIE_GUEST_LIMIT_LIFETIME = 24; // hours
const USER_ORDER_LIMIT = 999;
protected ?EnvironmentInterface $environment = null;
private bool $cartInitialized = false;
public function __construct(protected Factory $factory, protected Security $security, protected StockService $stockService, protected SessionInterface $session)
{
}
public function getEnvironment(): ?EnvironmentInterface
{
if (!$this->environment instanceof EnvironmentInterface) {
$this->environment = $this->factory->getEnvironment();
}
return $this->environment;
}
/**
* @param string $cartName
*
* @return CartInterface
*/
public function getCart(string $cartName = self::CART_KEY ): CartInterface
{
$cartManager = $this->factory->getCartManager();
if (!$this->cartInitialized) {
$this->initializeCart($cartName);
}
return $cartManager->getOrCreateCartByName( $cartName );
}
/**
* @param CheckoutableInterface $product
* @param int $count
* @param null $comment
* @param string $cartName
* @return CartInterface
*/
public function addToCart( CheckoutableInterface $product, int $count = 1, $comment = null, string $cartName = self::CART_KEY ): CartInterface
{
$cart = $this->getCart( $cartName );
$cart->addItem( $product, $count, null, false, [], [], $comment );
$cart->save();
return $cart;
}
public function containsProduct( CheckoutableInterface $product, string $cartName = self::CART_KEY ): bool
{
$cart = $this->getCart( $cartName );
foreach($cart->getItems() as $item) {
if ($item->getProduct()->getId() === $product->getId()) {
return true;
}
}
return false;
}
public function limitItemCountToMax(string $cartName = self::CART_KEY): void
{
$cart = $this->getCart($cartName);
$modified = false;
foreach($cart->getItems() as $item) {
if ($item->getCount() > 3) {
$item->setCount(3);
$modified = true;
}
}
if ($modified) {
$cart->save();
}
}
/**
* @param CheckoutableInterface $product
* @param int $count
* @param string $cartName
* @return CartInterface
*/
public function updateCartItem( CheckoutableInterface $product, int $count = 1, string $cartName = self::CART_KEY ): CartInterface
{
$cart = $this->getCart( $cartName );
if( $count <= 0 ){
$cart->removeItem( $product->getId() );
} else {
$cart->updateItemCount( $product->getId(), $count );
}
$cart->save();
return $cart;
}
public function removeCartItem(string $itemKey, string $cartName = self::CART_KEY): CartInterface
{
$cart = $this->getCart( $cartName );
$cart->removeItem($itemKey);
$cart->save();
return $cart;
}
/**
* load only Cart Counter
*
* @param string $cartName
*
* @return int
*/
public function getCartCount(string $cartName = self::CART_KEY): int
{
return $this->getCart($cartName)->getItemAmount();
}
/** Remove all items that in the cart
* @param string $cartName
*/
public function emptyCart(string $cartName = self::CART_KEY): void
{
$cart = $this->getCart($cartName);
if(!empty($cart->getItems())) {
foreach($cart->getItems() as $cartItem) {
$cart->removeItem($cartItem->getItemKey());
}
$cart->save();
}
}
public function deleteCart(string $cartName = self::CART_KEY): void
{
$this->getCart($cartName)->delete();
}
/**
* @param bool $enabled
*/
public function setUseGuestCart(bool $enabled): void
{
$env = $this->getEnvironment();
if ($env && $env->getUseGuestCart() !== $enabled) {
$env->setUseGuestCart($enabled);
$env->save();
}
}
public function canAddToCart($cartName = self::CART_KEY): bool
{
$cartCount = $this->getCartCount($cartName);
$env = $this->getEnvironment();
if ($env && $env->getUseGuestCart()) {
// if the guest has reached the daily limit. no ordering for him!
if($this->guestHasReachedDailySampleLimit()) {
return false;
}
// guest could have already ordered something, still the limit applies
$cartCount += $this->getGuestOrderedAmount();
return $cartCount < self::GUEST_ORDER_LIMIT;
}
return true;
}
/**
* @param $sessionCart
* @param $dbCart
* @throws \Exception
*/
public function convertSessionCartToDbCart($sessionCart, $dbCart)
{
foreach ($sessionCart->getItems() as $item) {
$dbCart->addItem($item->getProduct(), $item->getCount());
}
$dbCart->save();
$sessionCart->delete();
}
/**
* @param string $country
* @param string $cartName
*
* @return bool
* @throws \Doctrine\DBAL\Driver\Exception
* @throws \Doctrine\DBAL\Exception
*/
public function checkAvailability(string $country = LocationService::DEFAULT_LOCATION, string $cartName = self::CART_KEY): bool
{
$cart = $this->getCart($cartName);
if(!$cart || empty($cart->getItems())) {
return false;
}
foreach($cart->getItems() as $cartItem) {
$product = $cartItem->getProduct();
if($product instanceof Sample && $this->stockService->getStockForSampleBasedOnCountry($product, $country) <= 0) {
return false;
}
}
return true;
}
/**
* @param OnlineShopOrder $order
*/
public function saveSampleAmountToSession(OnlineShopOrder $order): void
{
$value = (int)$this->getMaxOrderAmount($order);
$cookie = $this->session->get(self::COOKIE_GUEST_LIMIT);
$expiresTime = Carbon::now()->addHours(self::COOKIE_GUEST_LIMIT_LIFETIME)->getTimestamp();
if($cookie instanceof Cookie) {
$expiresTime = $cookie->getExpiresTime();
$value += (int)$cookie->getValue();
}
$cookie = new Cookie(self::COOKIE_GUEST_LIMIT, $value, $expiresTime);
$this->session->set(self::COOKIE_GUEST_LIMIT, $cookie);
}
/**
* @param OnlineShopOrder $order
*
* @return float|int|null
*/
protected function getMaxOrderAmount(OnlineShopOrder $order) {
$amount = 0;
$items = $order->getItems();
if(!empty($items)) {
foreach($items as $item) {
$amount += $item->getAmount();
}
}
return $amount;
}
/**
* @return bool
*/
public function guestHasReachedDailySampleLimit(): bool
{
$this->checkIfCookieHasExpired();
$cookie = $this->session->get(self::COOKIE_GUEST_LIMIT);
return $cookie instanceof Cookie && (int)$cookie->getValue() >= self::GUEST_ORDER_LIMIT;
}
/**
* @return int
*/
protected function getGuestOrderedAmount(): int
{
$cookie = $this->session->get(self::COOKIE_GUEST_LIMIT);
return $cookie instanceof Cookie ? (int)$cookie->getValue() : 0;
}
protected function checkIfCookieHasExpired(): void
{
$cookie = $this->session->get(self::COOKIE_GUEST_LIMIT);
if($cookie instanceof Cookie && !$cookie->getMaxAge()) {
$this->session->set(self::COOKIE_GUEST_LIMIT, null);
}
}
/**
* @param $cartName
*/
protected function initializeCart($cartName): void
{
try {
$customer = $this->security->getUser();
$environment = $this->getEnvironment();
if($environment) {
if (!$customer instanceof Customer) {
$this->setUseGuestCart(true);
$environment->setCurrentUserId(null);
$environment->save();
} else {
$cartManager = $this->factory->getCartManager();
if ($environment->getCurrentUserId() !== $customer->getId()) {
$environment->setCurrentUserId($customer->getId());
$environment->save();
}
if ($environment->getUseGuestCart()) {
$sessionCart = $cartManager->getCartByName($cartName);
$this->setUseGuestCart(false);
$cartManager->reset();
if ($sessionCart instanceof SessionCart) {
$dbCart = $cartManager->getOrCreateCartByName($cartName);
$dbCart->setCheckoutData($cartName, $sessionCart->getCheckoutData($cartName));
$this->convertSessionCartToDbCart($sessionCart, $dbCart);
$sessionCart->delete();
}
}
}
}
} catch (\Throwable $exception) {
$this->setUseGuestCart(!$customer instanceof Customer);
}
$this->cartInitialized = true;
}
public function cartAddMaxAmount(CheckoutableInterface $product, CustomerInterface $user = null, string $cartName = self::CART_KEY): int
{
$cart = $this->getCart($cartName);
$item = $cart->getItem($product->getId());
$maxAmount = self::PRODUCT_ORDER_LIMIT;
if ($item) {
$maxAmount -= $item->getCount();
}
if (!$user) {
$itemCount = $cart->getItemAmount();
if (self::GUEST_ORDER_LIMIT - $itemCount < $maxAmount) {
$maxAmount = self::GUEST_ORDER_LIMIT - $itemCount;
}
}
if ($user) {
$maxAmount = self::USER_ORDER_LIMIT;
}
return $maxAmount;
}
}