<?php
namespace Customize\Service;
use Customize\Entity\EstimateCart;
use Customize\Entity\EstimateCartItem;
use Customize\Entity\Master\EstimateItemType;
use Customize\Repository\EstimateCartRepository;
use Customize\Repository\EstimateRepository;
use Customize\Repository\Master\EstimateItemTypeRepository;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\UnitOfWork;
use Eccube\Entity\Cart;
use Eccube\Entity\CartItem;
use Eccube\Entity\Customer;
use Eccube\Entity\ItemHolderInterface;
use Eccube\Entity\ProductClass;
use Eccube\Repository\ProductClassRepository;
use Eccube\Service\Cart\CartItemAllocator;
use Eccube\Service\Cart\CartItemComparator;
use Eccube\Util\StringUtil;
use Plugin\ProductOption42\Service\Cart\ProductClassAndOptionComparator;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
class EstimateCartService
{
/**
* @var EstimateCart[]
*/
protected $estimateCarts;
/**
* @var SessionInterface
*/
protected $session;
/**
* @var \Doctrine\ORM\EntityManagerInterface
*/
protected $entityManager;
/**
* @var ItemHolderInterface
*
* @deprecated
*/
protected $estimateCart;
/**
* @var ProductClassRepository
*/
protected $productClassRepository;
/**
* @var EstimateCartRepository
*/
protected $EstimateCartRepository;
/**
* @var ProductClassAndOptionComparator
*/
protected $productClassAndOptionComparator;
/**
* @var EstimateRepository
*/
protected $estimateRepository;
/**
* @var TokenStorageInterface
*/
protected $tokenStorage;
/**
* @var AuthorizationCheckerInterface
*/
protected $authorizationChecker;
/**
* @var EstimateItemTypeRepository
*/
protected $estimateItemTypeRepository;
/**
* EstimateCartService constructor.
*/
public function __construct(
SessionInterface $session,
EntityManagerInterface $entityManager,
ProductClassRepository $productClassRepository,
EstimateCartRepository $estimateCartRepository,
ProductClassAndOptionComparator $productClassAndOptionComparator,
CartItemAllocator $cartItemAllocator,
EstimateRepository $estimateRepository,
TokenStorageInterface $tokenStorage,
AuthorizationCheckerInterface $authorizationChecker,
EstimateItemTypeRepository $estimateItemTypeRepository
) {
$this->session = $session;
$this->entityManager = $entityManager;
$this->productClassRepository = $productClassRepository;
$this->estimateCartRepository = $estimateCartRepository;
$this->productClassAndOptionComparator = $productClassAndOptionComparator;
$this->cartItemAllocator = $cartItemAllocator;
$this->estimateRepository = $estimateRepository;
$this->tokenStorage = $tokenStorage;
$this->authorizationChecker = $authorizationChecker;
$this->estimateItemTypeRepository = $estimateItemTypeRepository;
}
/**
* 現在のカートの配列を取得する.
*
* 本サービスのインスタンスのメンバーが空の場合は、DBまたはセッションからカートを取得する
*
* @param bool $empty_delete true の場合、商品明細が空のカートが存在した場合は削除する
*
* @return EstimateCart[]
*/
public function getCarts($empty_delete = false)
{
if (null !== $this->estimateCarts) {
if ($empty_delete) {
$cartKeys = [];
foreach (array_keys($this->estimateCarts) as $index) {
$Cart = $this->estimateCarts[$index];
if ($Cart->getItems()->count() > 0) {
$cartKeys[] = $Cart->getCartKey();
} else {
$this->entityManager->remove($this->estimateCarts[$index]);
$this->entityManager->flush();
unset($this->estimateCarts[$index]);
}
}
$this->session->set('estimate_cart_keys', $cartKeys);
}
return $this->estimateCarts;
}
if ($this->getUser()) {
$this->estimateCarts = $this->getPersistedCarts();
} else {
$this->estimateCarts = $this->getSessionCarts();
}
return $this->estimateCarts;
}
/**
* 永続化されたカートを返す
*
* @return Cart[]
*/
public function getPersistedCarts()
{
return $this->estimateCartRepository->findBy(['Customer' => $this->getUser()]);
}
/**
* セッションにあるカートを返す
*
* @return Cart[]
*/
public function getSessionCarts()
{
$cartKeys = $this->session->get('estimate_cart_keys', []);
if (empty($cartKeys)) {
return [];
}
return $this->estimateCartRepository->findBy(['cart_key' => $cartKeys], ['id' => 'ASC']);
}
/**
* 会員が保持する永続化されたカートと、非会員時のカートをマージする.
*/
public function mergeFromPersistedCart()
{
$persistedCarts = $this->getPersistedCarts();
$sessionCarts = $this->getSessionCarts();
$CartItems = [];
// 永続化されたカートとセッションのカートが同一の場合はマージしない #4574
$cartKeys = $this->session->get('estimate_cart_keys', []);
if ((count($persistedCarts) > 0) && !in_array($persistedCarts[0]->getCartKey(), $cartKeys, true)) {
foreach ($persistedCarts as $Cart) {
$CartItems = $this->mergeCartItems($Cart->getCartItems(), $CartItems);
}
}
// セッションにある非会員カートとDBから取得した会員カートをマージする.
foreach ($sessionCarts as $Cart) {
$CartItems = $this->mergeCartItems($Cart->getCartItems(), $CartItems);
}
$this->restoreCarts($CartItems);
}
/**
* @return Cart|null
*/
public function getCart()
{
$Carts = $this->getCarts();
if (empty($Carts)) {
return null;
}
$cartKeys = $this->session->get('estimate_cart_keys', []);
$Cart = null;
if (count($cartKeys) > 0) {
foreach ($Carts as $cart) {
if ($cart->getCartKey() === current($cartKeys)) {
$Cart = $cart;
break;
}
}
} else {
$Cart = $Carts[0];
}
return $Cart;
}
/**
* @param EstimateCartItem[] $cartItems
*
* @return EstimateCartItem[]
*/
protected function mergeAllCartItems($cartItems = [])
{
/** @var EstimateCartItem[] $allCartItems */
$allCartItems = [];
foreach ($this->getCarts() as $Cart) {
$allCartItems = $this->mergeCartItems($Cart->getCartItems(), $allCartItems);
}
return $this->mergeCartItems($cartItems, $allCartItems);
}
/**
* @param $cartItems
* @param $allCartItems
*
* @return array
*/
protected function mergeCartItems($cartItems, $allCartItems)
{
foreach ($cartItems as $item) {
// $itemExists = false;
// foreach ($allCartItems as $itemInArray) {
// // 同じ明細があればマージする
// if ($this->productClassAndOptionComparator->compare2($item, $itemInArray)) {
// $itemInArray->setQuantity($itemInArray->getQuantity() + $item->getQuantity());
// $itemExists = true;
// break;
// }
// }
// if (!$itemExists) {
$allCartItems[] = $item;
// }
}
return $allCartItems;
}
protected function restoreCarts($cartItems)
{
foreach ($this->getCarts() as $Cart) {
foreach ($Cart->getCartItems() as $i) {
$this->entityManager->remove($i);
$this->entityManager->flush();
}
$this->entityManager->remove($Cart);
$this->entityManager->flush();
}
$this->estimateCarts = [];
/** @var EstimateCart[] $Carts */
$Carts = [];
foreach ( $cartItems as $item ) {
$allocatedId = $this->allocate($item);
if ( !empty($allocatedId) ) {
break;
}
}
foreach ($cartItems as $item) {
$cartKey = $this->createCartKey($allocatedId, $this->getUser());
if (isset($Carts[$cartKey])) {
$Cart = $Carts[$cartKey];
$Cart->addCartItem($item);
$item->setCart($Cart);
} else {
/** @var EstimateCart $Cart */
$Cart = $this->estimateCartRepository->findOneBy(['cart_key' => $cartKey]);
if ($Cart) {
foreach ($Cart->getCartItems() as $i) {
$this->entityManager->remove($i);
$this->entityManager->flush();
}
$this->entityManager->remove($Cart);
$this->entityManager->flush();
}
$Cart = new EstimateCart();
$Cart->setCartKey($cartKey);
$Cart->addCartItem($item);
$item->setCart($Cart);
$Carts[$cartKey] = $Cart;
}
}
$this->estimateCarts = array_values($Carts);
}
/**
* カートに商品を追加します.
*
* @param $ProductClass ProductClass 商品規格
* @param $quantity int 数量
*
* @return bool 商品を追加できた場合はtrue
*/
// public function addProduct($ProductClass, $quantity = 1)
// {
// if (!$ProductClass instanceof ProductClass) {
// $ProductClassId = $ProductClass;
// $ProductClass = $this->entityManager
// ->getRepository(ProductClass::class)
// ->find($ProductClassId);
// if (is_null($ProductClass)) {
// return false;
// }
// }
//
// $ClassCategory1 = $ProductClass->getClassCategory1();
// if ($ClassCategory1 && !$ClassCategory1->isVisible()) {
// return false;
// }
// $ClassCategory2 = $ProductClass->getClassCategory2();
// if ($ClassCategory2 && !$ClassCategory2->isVisible()) {
// return false;
// }
//
// $newItem = new CartItem();
// $newItem->setQuantity($quantity);
// $newItem->setPrice($ProductClass->getPrice02IncTax());
// $newItem->setProductClass($ProductClass);
//
// $allCartItems = $this->mergeAllCartItems([$newItem]);
// $this->restoreCarts($allCartItems);
//
// return true;
// }
public function removeProduct()
{
$allCartItems = $this->mergeAllCartItems();
$foundIndex = array_key_last($allCartItems);
array_splice($allCartItems, $foundIndex, 1);
$this->restoreCarts($allCartItems);
return true;
}
public function save($Customer = null)
{
$cartKeys = [];
foreach ($this->estimateCarts as $Cart) {
if (is_null($Cart->getCustomer())) {
if ($Customer) {
$Cart->setCustomer($Customer);
} else {
$Cart->setCustomer($this->getUser());
}
}
$this->entityManager->persist($Cart);
foreach ($Cart->getCartItems() as $item) {
$this->entityManager->persist($item);
}
$this->entityManager->flush();
$cartKeys[] = $Cart->getCartKey();
}
$this->session->set('estimate_cart_keys', $cartKeys);
return;
}
/**
* @param string $pre_estimate_id
*
* @return $this
*/
public function setPreEstimateId($pre_estimate_id)
{
$this->getCart()->setPreEstimateId($pre_estimate_id);
return $this;
}
/**
* @return string|null
*/
public function getPreEstimateId()
{
$Cart = $this->getCart();
if (!empty($Cart)) {
return $Cart->getPreEstimateId();
}
return null;
}
/**
* @return $this
*/
public function clear()
{
$Carts = $this->getCarts();
if (!empty($Carts)) {
$removed = $this->getCart();
if ($removed && UnitOfWork::STATE_MANAGED === $this->entityManager->getUnitOfWork()->getEntityState($removed)) {
$this->entityManager->remove($removed);
$this->entityManager->flush();
$cartKeys = [];
foreach ($Carts as $key => $Cart) {
// テーブルから削除されたカートを除外する
if ($Cart == $removed) {
unset($Carts[$key]);
}
$cartKeys[] = $Cart->getCartKey();
}
$this->session->set('estimate_cart_keys', $cartKeys);
// 注文完了のカートキーをセッションから削除する
$this->session->remove('estimate_cart_keys');
$this->estimateCarts = $this->estimateCartRepository->findBy(['cart_key' => $cartKeys], ['id' => 'ASC']);
}
}
return $this;
}
/**
* @param CartItemComparator $cartItemComparator
*/
// public function setCartItemComparator($cartItemComparator)
// {
// $this->cartItemComparator = $cartItemComparator;
// }
/**
* カートキーで指定したインデックスにあるカートを優先にする
*
* @param string $cartKey カートキー
*/
public function setPrimary($cartKey)
{
$Carts = $this->getCarts();
$primary = $Carts[0];
$index = 0;
foreach ($Carts as $key => $Cart) {
if ($Cart->getCartKey() === $cartKey) {
$index = $key;
$primary = $Carts[$index];
break;
}
}
$prev = $Carts[0];
array_splice($Carts, 0, 1, [$primary]);
array_splice($Carts, $index, 1, [$prev]);
$this->estimateCarts = $Carts;
$this->save();
}
protected function getUser()
{
if (null === $token = $this->tokenStorage->getToken()) {
return;
}
if (!is_object($user = $token->getUser())) {
// e.g. anonymous authentication
return;
}
return $user;
}
public function allocate(EstimateCartItem $Item)
{
$ProductClass = $Item->getProductClass();
if ($ProductClass && $ProductClass->getSaleType()) {
return (string) $ProductClass->getSaleType()->getId();
}
// @CHANGED カッティング基本加工料
// throw new \InvalidArgumentException('ProductClass/SaleType not found');
}
/**
* @param string $allocatedId
*/
protected function createCartKey($allocatedId, Customer $Customer = null)
{
if ($Customer instanceof Customer) {
return $Customer->getId().'_'.$allocatedId;
}
if ($this->session->has('cart_key_prefix')) {
return $this->session->get('cart_key_prefix').'_'.$allocatedId;
}
do {
$random = StringUtil::random(32);
$cartKey = $random.'_'.$allocatedId;
$Cart = $this->estimateCartRepository->findOneBy(['cart_key' => $cartKey]);
} while ($Cart);
$this->session->set('cart_key_prefix', $random);
return $cartKey;
}
public function removeCartItem($CartItem)
{
$allCartItems = $this->mergeAllCartItems();
$foundIndex = -1;
foreach ($allCartItems as $index => $itemInCart) {
if ( $CartItem->getId() == $itemInCart->getId() ) {
$foundIndex = $index;
break;
}
}
array_splice($allCartItems, $foundIndex, 1);
$this->restoreCarts($allCartItems);
}
/**
* 商品をカートに追加
*
* @param $productClassId
* @param $Options
* @param $quantity
* @param $path
* @param $materials
* @param $price
* @param $original_file_name
* @return false|void
*/
public function addProductOption($productClassId, $Options, $quantity = 1, $path = null, $materials = null, $price = 0, $original_file_name = null)
{
$ProductClass = $this->productClassRepository->find($productClassId);
if(!$ProductClass) return false;
$EstimateItemType = $this->estimateItemTypeRepository->find(EstimateItemType::PRODUCT);
$newItem = new EstimateCartItem();
$newItem->setQuantity($quantity);
$newItem->setPrice($price);
$newItem->setProductClass($ProductClass);
$newItem->setOptions(json_encode($Options));
$newItem->setEstimateItemType($EstimateItemType);
if ( !empty($path) ) {
$newItem->setSubmitFilePath($path);
if ( !empty($original_file_name) ) {
$newItem->setSubmitFileName($original_file_name);
}
}
if ( !empty($materials) ) {
$newItem->setMaterials(json_encode($materials));
}
// $allCartItems = [];
// foreach($this->getCarts() as $Cart){
// $CartItems = $Cart->getCartItems();
// $allCartItems = array_merge($allCartItems, $CartItems->toArray());
// }
//
// $allCartItems[] = $newItem;
// $this->restoreCarts($allCartItems);
$allCartItems = $this->mergeAllCartItems([$newItem]);
$this->restoreCarts($allCartItems);
}
}