<?php
/*
* GtmGaEEc42: GTM版 Google Analytics eコマース/拡張eコマース対応プラグイン
* Copyright (C) 2017-2022 Freischtide Inc. All Rights Reserved.
* http://freischtide.tumblr.com/
*
* License: see LICENSE.txt
*/
namespace Plugin\GtmGaEEc42;
use Eccube\Common\EccubeConfig;
use Eccube\Event\TemplateEvent;
use Eccube\Entity\Category;
use Eccube\Entity\Order;
use Eccube\Entity\OrderItem;
use Eccube\Entity\ItemHolderInterface;
use Eccube\Entity\ProductClass;
use Eccube\Entity\TaxRule;
use Eccube\Service\TaxRuleService;
use Plugin\GtmGaEEc42\Entity\GtmGaEEc;
use Plugin\GtmGaEEc42\Repository\GtmGaEEcRepository;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Finder\Finder;
use Plugin\Coupon42\Entity\Coupon;
use Plugin\Coupon42\Entity\CouponOrder;
class Event implements EventSubscriberInterface
{
/**
* @var GtmEEcRepository
*/
private $repo;
/**
* @var EccubeConfig
*/
private $eccubeConfig;
/**
* @var TokenStorageInterface
*/
private $tokenStorage;
/**
* @var AuthorizationCheckerInterface
*/
private $authorizationChecker;
/**
* @var RequestStack
*/
private $requestStack;
/**
* @var ContainerInterface
*/
private $container;
/**
* @var GtmGaEEc
*/
private $gtmgaeec;
private $gtmgaeec_tag_rendered = false;
/**
* Event constructor.
* @param GtmGaEEcRepository $repo
* @param EccubeConfig $eccubeConfig
* @param TokenStorageInterface $tokenStorage
* @param AuthorizationCheckerInterface $authorizationChecker
* @param RequestStack $requestStack
* @param ContainerInterface $container
* @param \Twig_Environment $environment
*/
public function __construct(
GtmGaEEcRepository $repo,
EccubeConfig $eccubeConfig,
TokenStorageInterface $tokenStorage,
AuthorizationCheckerInterface $authorizationChecker,
RequestStack $requestStack,
ContainerInterface $container
) {
$this->repo = $repo;
$this->eccubeConfig = $eccubeConfig;
$this->tokenStorage = $tokenStorage;
$this->authorizationChecker = $authorizationChecker;
$this->requestStack = $requestStack;
$this->container = $container;
$this->gtmgaeec = $this->repo->get();
}
/**
* @return array
*/
public static function getSubscribedEvents()
{
$template_path = __DIR__ . '/../../../src/Eccube/Resource/template/default/';
$subscribed_events = [];
foreach ((new Finder())->in($template_path)->name('*.twig')->files() as $fileinfo) {
$subscribed_events[str_replace($template_path, '', $fileinfo->getPathname())] = 'all';
}
$subscribed_events['Product/list.twig'] = 'productList';
$subscribed_events['Mypage/favorite.twig'] = 'mypageFavorite';
$subscribed_events['Product/detail.twig'] = 'productDetail';
$subscribed_events['Cart/index.twig'] = 'cart';
$subscribed_events['Entry/index.twig'] = 'entry';
$subscribed_events['Entry/confirm.twig'] = 'entry_confirm';
$subscribed_events['Entry/complete.twig'] = 'entry_complete';
$subscribed_events['Mypage/index.twig'] = 'mypage';
$subscribed_events['Mypage/login.twig'] = 'mypage_login';
$subscribed_events['Shopping/login.twig'] = 'shopping_login';
$subscribed_events['Shopping/nonmember.twig'] = 'shopping_nonmember';
$subscribed_events['Shopping/index.twig'] = 'shopping';
$subscribed_events['Shopping/confirm.twig'] = 'shopping';
$subscribed_events['Shopping/complete.twig'] = 'shoppingComplete';
return $subscribed_events;
}
private function setVars(&$parameters)
{
if ($this->gtmgaeec->getTrackUserId() == GtmGaEEc::GTMGAEEC_OP_INCLUDE_USER_ID) {
if (!is_null($this->tokenStorage->getToken()) &&
$this->authorizationChecker->isGranted('ROLE_USER')) {
$customer_id = $this->tokenStorage->getToken()->getUser()->getId();
$parameters['user_id'] = $this->getUuid5($this->eccubeConfig->get('GTMGAEEC_UUID'),
$this->gtmgaeec->getTid(),
$customer_id);
}
}
$pid = $this->requestStack->getCurrentRequest()->get('ugep_id');
$pname = $this->requestStack->getCurrentRequest()->get('ugep_name');
if ($pid && $pname) {
$promo['id'] = addslashes($pid);
$promo['name'] = addslashes($pname);
$promo['creative'] = addslashes($this->requestStack->getCurrentRequest()->get('ugep_creative'));
$promo['position'] = addslashes($this->requestStack->getCurrentRequest()->get('ugep_position'));
$parameters['promo'] = $promo;
}
}
public function all(TemplateEvent $event)
{
$tid = $this->gtmgaeec->getTid();
if (empty($tid)) {
return;
}
if ($this->gtmgaeec_tag_rendered) {
return;
}
$parameters = $event->getParameters();
$this->setVars($parameters);
$parameters['gtmgaeec'] = $this->gtmgaeec;
$event->setParameters($parameters);
$this->addAsset($event);
}
public function productList(TemplateEvent $event)
{
$tid = $this->gtmgaeec->getTid();
if (empty($tid)) {
return;
}
$parameters = $event->getParameters();
$this->setVars($parameters);
$name = $this->requestStack->getCurrentRequest()->get('name');
if (!empty($name)) {
$list = 'Search Results';
} else {
$list = 'Category List';
}
$items = $parameters['pagination']->getItems();
$impressions = [];
if ($this->gtmgaeec->getImpTrack() == GtmGaEEc::GTMGAEEC_OP_WITH_IMP_TRACK) {
if ($this->gtmgaeec->getCategory() == GtmGaEEc::GTMGAEEC_OP_CATEGORY_ON &&
is_object($parameters['Category'])) {
$category = $parameters['Category']->getName();
} else {
$category = '';
}
foreach ($items as $item) {
$impressions[] = $this->buildEcImpression($item, $list, $category);
}
}
$parameters['gtmgaeec'] = $this->gtmgaeec;
$parameters['impressions'] = $impressions;
$parameters['list_name'] = $list;
$event->setParameters($parameters);
$this->addAsset($event);
}
public function mypageFavorite(TemplateEvent $event)
{
$tid = $this->gtmgaeec->getTid();
if (empty($tid)) {
return;
}
$parameters = $event->getParameters();
$this->setVars($parameters);
$list = 'Favorite List';
$items = $parameters['pagination']->getItems();
$impressions = [];
if ($this->gtmgaeec->getImpTrack() == GtmGaEEc::GTMGAEEC_OP_WITH_IMP_TRACK) {
foreach ($items as $item) {
// CustomerFavoriteProduct => Product
$product = $item->getProduct($item->getId());
$category = $this->getCategory($product);
$impressions[] = $this->buildEcImpression($product, $list, $category);
}
}
$parameters['gtmgaeec'] = $this->gtmgaeec;
$parameters['impressions'] = $impressions;
$parameters['list_name'] = $list;
$event->setParameters($parameters);
$this->addAsset($event);
}
public function productDetail(TemplateEvent $event)
{
$tid = $this->gtmgaeec->getTid();
if (empty($tid)) {
return;
}
$parameters = $event->getParameters();
$this->setVars($parameters);
$list = 'Item Detail';
$product = $parameters['Product'];
$category = $this->getCategory($product);
$products = [$this->buildEcProduct($product, $category)];
$action = ['action' => 'detail', 'option' => ''];
$clickFrom = $this->decideList($_SERVER);
$impressions = [$this->buildEcImpression($product, $list, $category)];
$ga4Impressions = [];
if ($this->gtmgaeec->getImpTrack() == GtmGaEEc::GTMGAEEC_OP_WITH_IMP_TRACK) {
// related product from RelatedProduct plugin by EC-CUBE
// https://github.com/EC-CUBE/related-product-plugin
if (class_exists('Plugin\RelatedProduct4\Entity\RelatedProduct')) {
$relatedProductPlugin = false;
$enabledPlugins = $this->container->getParameter('eccube.plugins.enabled');
foreach ($enabledPlugins as $plugin) {
if ($plugin == 'RelatedProduct4') {
$relatedProductPlugin = true;
break;
}
}
if ($relatedProductPlugin) {
foreach ($product->getRelatedProducts() as $relatedProduct) {
$childProduct = $relatedProduct->getChildProduct();
if ($childProduct->getStatus()->getId() == \Eccube\Entity\Master\ProductStatus::DISPLAY_SHOW) {
$childCategory = $this->getCategory($childProduct);
$impressions[] = $this->buildEcImpression($childProduct, 'Related Item', $childCategory);
$ga4Impressions[] = $this->buildEcImpression($childProduct, 'Related Item', $childCategory);
}
}
}
}
}
$parameters['gtmgaeec'] = $this->gtmgaeec;
$parameters['impressions'] = $impressions;
$parameters['ga4_impressions'] = $ga4Impressions;
$parameters['products'] = $products;
$parameters['action'] = $action;
$parameters['list_name'] = $clickFrom;
$event->setParameters($parameters);
$this->addAsset($event);
}
public function cart(TemplateEvent $event)
{
$tid = $this->gtmgaeec->getTid();
if (empty($tid)) {
return;
}
$parameters = $event->getParameters();
$this->setVars($parameters);
$carts = $parameters['Carts'];
$products = array();
foreach ($carts as $cart) {
foreach ($cart->getCartItems() as $cartItem) {
$productClass = $cartItem->getProductClass();
$product = $productClass->getProduct();
$category = $this->getCategory($product);
$variant = $this->getVariant($productClass);
$products[] = $this->buildEcProduct($product,
$category,
$cartItem->getPrice(),
$cartItem->getQuantity(),
$variant);
}
}
$action = ['action' => 'add', 'option' => ''];
$parameters['gtmgaeec'] = $this->gtmgaeec;
$parameters['products'] = $products;
$parameters['action'] = $action;
$event->setParameters($parameters);
$this->addAsset($event);
}
// 会員登録
public function entry(TemplateEvent $event)
{
$tid = $this->gtmgaeec->getTid();
if (empty($tid)) {
return;
}
$parameters = $event->getParameters();
$this->setVars($parameters);
$parameters['gtmgaeec'] = $this->gtmgaeec;
$parameters['event_name'] = 'sign_up_initiated';
$event->setParameters($parameters);
$this->addAsset($event);
}
// 会員登録 確認
public function entry_confirm(TemplateEvent $event)
{
$tid = $this->gtmgaeec->getTid();
if (empty($tid)) {
return;
}
$parameters = $event->getParameters();
$this->setVars($parameters);
$parameters['gtmgaeec'] = $this->gtmgaeec;
$parameters['event_name'] = 'sign_up_confirm';
$event->setParameters($parameters);
$this->addAsset($event);
}
// 会員登録 完了
public function entry_complete(TemplateEvent $event)
{
$tid = $this->gtmgaeec->getTid();
if (empty($tid)) {
return;
}
$parameters = $event->getParameters();
$this->setVars($parameters);
$parameters['gtmgaeec'] = $this->gtmgaeec;
$parameters['event_name'] = 'sign_up';
$event->setParameters($parameters);
$this->addAsset($event);
}
// マイページ
public function mypage(TemplateEvent $event)
{
$tid = $this->gtmgaeec->getTid();
if (empty($tid)) {
return;
}
$parameters = $event->getParameters();
$this->setVars($parameters);
$parameters['gtmgaeec'] = $this->gtmgaeec;
$parameters['event_name'] = 'mypage';
$event->setParameters($parameters);
$this->addAsset($event);
}
// 通常時のログイン
public function mypage_login(TemplateEvent $event)
{
$tid = $this->gtmgaeec->getTid();
if (empty($tid)) {
return;
}
$parameters = $event->getParameters();
$this->setVars($parameters);
$parameters['gtmgaeec'] = $this->gtmgaeec;
$parameters['event_name'] = 'mypage_login';
$event->setParameters($parameters);
$this->addAsset($event);
}
// 買い物時のログイン
public function shopping_login(TemplateEvent $event)
{
$tid = $this->gtmgaeec->getTid();
if (empty($tid)) {
return;
}
$parameters = $event->getParameters();
$this->setVars($parameters);
$parameters['gtmgaeec'] = $this->gtmgaeec;
$parameters['event_name'] = 'shopping_login';
$event->setParameters($parameters);
$this->addAsset($event);
}
// お客様情報
public function shopping_nonmember(TemplateEvent $event)
{
$tid = $this->gtmgaeec->getTid();
if (empty($tid)) {
return;
}
$parameters = $event->getParameters();
$this->setVars($parameters);
$parameters['gtmgaeec'] = $this->gtmgaeec;
$parameters['event_name'] = 'shopping_nonmember';
$event->setParameters($parameters);
$this->addAsset($event);
}
// step 1: ご注文手続き(GET), step 2: ご注文内容確認(POST)
public function shopping(TemplateEvent $event)
{
$tid = $this->gtmgaeec->getTid();
if (empty($tid)) {
return;
}
$parameters = $event->getParameters();
$this->setVars($parameters);
// order id を一時的に保持(ペイジェント等の外部決済システムが order id を返さないため)
$order = $parameters['Order'];
$cookie = $this->getGtmGaEEcCookie();
if ($order->getId()) {
$this->setGtmGaEEcCookie($cookie, array('orderId' => $order->getId()));
}
$couponCode = null;
$couponDiscounts = [];
if (isset($parameters['CouponOrder'])) {
$couponOrder = $parameters['CouponOrder'];
if ($couponOrder) {
if ($this->requestStack->getCurrentRequest()->getMethod() == 'POST' && $this->gtmgaeec->getConfirmationAsPurchase() == GtmGaEEc::GTMGAEEC_OP_CONFIRMATION_AS_PURCHASE) {
list($couponCode, $couponDiscounts) = $this->getCouponDiscounts($order, $couponOrder->getId());
} else {
$this->setGtmGaEEcCookie($cookie, array('couponOrderId' => $couponOrder->getId()));
}
}
}
$orderDetails = $order->getOrderItems();
$products = $this->getProducts($orderDetails, $couponDiscounts);
$action = ['action' => 'checkout', 'option' => $this->getOptionsStr([
'step' => $this->requestStack->getCurrentRequest()->getMethod() == 'POST' ? 2 : 1
])];
$parameters['gtmgaeec'] = $this->gtmgaeec;
$parameters['products'] = $products;
$parameters['action'] = $action;
$parameters['event'] = $this->requestStack->getCurrentRequest()->getMethod() == 'POST' ? 'checkout_progress' : 'begin_checkout';
$parameters['checkout_step'] = $this->requestStack->getCurrentRequest()->getMethod() == 'POST' ? 2 : 1;
if ($this->requestStack->getCurrentRequest()->getMethod() == 'POST' && $this->gtmgaeec->getConfirmationAsPurchase() == GtmGaEEc::GTMGAEEC_OP_CONFIRMATION_AS_PURCHASE) {
$parameters['transaction'] = $this->getTransaction($order);
$parameters['coupon_code'] = $couponCode;
}
$event->setParameters($parameters);
$this->addAsset($event);
}
public function shoppingComplete(TemplateEvent $event)
{
$tid = $this->gtmgaeec->getTid();
if (empty($tid)) {
return;
}
if ($this->gtmgaeec->getConfirmationAsPurchase() == GtmGaEEc::GTMGAEEC_OP_CONFIRMATION_AS_PURCHASE) {
return;
}
$parameters = $event->getParameters();
$this->setVars($parameters);
// 必要に応じて cookie より order id を復元
$order = $parameters['Order'];
$cookie = $this->getGtmGaEEcCookie();
if (empty($order)) {
if (isset($cookie['orderId'])) {
$orderId = $cookie['orderId'];
$entityManager = $this->container->get('doctrine')->getManager();
$orderRepository = $entityManager->getRepository(Order::class);
$order = $orderRepository->find($orderId);
$this->setGtmGaEEcCookie($cookie, array('orderId' => null));
} else {
return;
}
}
$orderDetails = $order->getOrderItems();
$couponCode = null;
$couponDiscounts = [];
if (isset($cookie['couponOrderId'])) {
list($couponCode, $couponDiscounts) = $this->getCouponDiscounts($order, $cookie['couponOrderId']);
$this->setGtmGaEEcCookie($cookie, array('couponOrderId' => null));
}
$products = $this->getProducts($orderDetails, $couponDiscounts);
$transaction = $this->getTransaction($order);
$action = ['action' => 'purchase', 'option' => $this->getOptionsStr($transaction)];
$parameters['gtmgaeec'] = $this->gtmgaeec;
$parameters['products'] = $products;
$parameters['action'] = $action;
$parameters['transaction'] = $transaction;
$parameters['coupon_code'] = $couponCode;
$parameters['order'] = $order;
$event->setParameters($parameters);
$this->addAsset($event);
}
private function addAsset($event)
{
if ($this->gtmgaeec->getTagVersion() & GtmGaEEc::GTMGAEEC_TAG_UA) {
$event->addAsset('@GtmGaEEc42/default/gtmgaeec_ua.twig');
}
if ($this->gtmgaeec->getTagVersion() & GtmGaEEc::GTMGAEEC_TAG_GA4) {
$event->addAsset('@GtmGaEEc42/default/gtmgaeec_ga4.twig');
}
if ($this->gtmgaeec->getTag() == GtmGaEEc::GTMGAEEC_USE_GTM_TAG) {
$event->addAsset('@GtmGaEEc42/default/gtmgaeec_tag.twig');
}
$event->addSnippet('@GtmGaEEc42/default/gtmgaeec_noscript.twig');
$this->gtmgaeec_tag_rendered = true;
}
private function getCategory($product)
{
$category = '';
if ($this->gtmgaeec->getCategory() == GtmGaEEc::GTMGAEEC_OP_CATEGORY_ON) {
$productCategories = $product->getProductCategories();
$categories = array();
foreach ($productCategories as $productCategory) {
if (empty($productCategory['Category']->getParents())) {
$categories[] = implode('/', $productCategory['Category']->getSelfAndDescendants());
}
}
$category = implode(', ', $categories);
}
return $category;
}
private function getVariant($productClass)
{
$variants = array();
if ($productClass->hasClassCategory1()) {
$variants[] = $productClass->getClassCategory1()->getName();
}
if ($productClass->hasClassCategory2()) {
$variants[] = $productClass->getClassCategory2()->getName();
}
return implode('/', $variants);
}
private function getOptionsStr($options)
{
$results = array();
foreach ($options as $k => $v) {
$results[] = "'" . $k . "': " . $v;
}
return implode(', ', $results);
}
private function decideList($serverEnv)
{
if(!empty($serverEnv['HTTP_REFERER'])) {
$referrer = $serverEnv['HTTP_REFERER'];
} else {
return 'Direct Access';
}
$url = parse_url($referrer);
if ($url['host'] == $serverEnv['SERVER_NAME']) {
switch (true) {
case $url['path'] == '/':
return 'TOP';
case stristr($url['path'], '/mypage/favorite'):
return 'Favorite List';
case stristr($url['path'], '/products/list'):
$name = $this->requestStack->getCurrentRequest()->get('name');
if (!empty($name)) {
return 'Search Results';
} else {
return 'Category List';
}
case stristr($url['path'], '/products/detail'):
return 'Item Detail';
default:
return 'Internal Site';
}
} else {
if ($this->gtmgaeec->getUseCustomReferrer() == GtmGaEEc::GTMGAEEC_OP_USE_CUSTOM_REFERRER) {
foreach ($this->gtmgaeec->getCustomReferrers() as $customer_referrer) {
if (!empty($customer_referrer[0]) && stristr($referrer, $customer_referrer[0])) {
return addslashes($customer_referrer[1]);
}
}
}
return 'External Site';
}
}
private function setGtmGaEEcCookie($cookie, $values)
{
foreach ($values as $k => $v) {
if (is_null($v)) {
unset($cookie[$k]);
} else {
$cookie[$k] = $v;
}
}
setcookie('eccube_gtmgaeec', json_encode($cookie), time()+60*60, '/');
}
private function getGtmGaEEcCookie()
{
return isset($_COOKIE['eccube_gtmgaeec']) ? json_decode($_COOKIE['eccube_gtmgaeec'], true) : [];
}
private function getProducts($orderDetails, $couponDiscounts = [])
{
$products = [];
foreach ($orderDetails as $orderDetail) {
if (!$orderDetail->isProduct()) {
continue;
}
$product = $orderDetail->getProduct();
$category = $this->getCategory($product);
$variant = $this->getVariant($orderDetail->getProductClass());
$products[] = $this->buildEcProduct(
$product,
$category,
$orderDetail->getPriceIncTax(),
$orderDetail->getQuantity(),
$variant,
isset($couponDiscounts[$orderDetail->getProductClass()->getId()]) ? $couponDiscounts[$orderDetail->getProductClass()->getId()] : null
);
}
return $products;
}
private function getTransaction($order)
{
$transaction = [
'id' => "'" . $order['id'] . "'",
'revenue' => "'" . $order['total'] . "'",
'tax' => "'" . $order['tax'] . "'",
'shipping' => "'" . $order['delivery_fee_total'] . "'"
];
return $transaction;
}
private function getUuid5($namespace_id, $ga_tid, $customer_id)
{
$customer_hash = sha1($namespace_id . $ga_tid . $customer_id);
$time_low = substr($customer_hash, 0, 8);
$time_mid = substr($customer_hash, 8, 4);
$time_hi_and_version = substr($customer_hash, 12, 4);
$time_hi_and_version = sprintf('%04x', (intval('0x' . $time_hi_and_version, 16) & 0x0fff) | 0x3000);
$clk_seq_hi_res = substr($customer_hash, 16, 2);
$clk_seq_hi_res = sprintf('%02x', (intval('0x' . $clk_seq_hi_res, 16) & 0x3f) | 0x80);
$clk_seq_low = substr($customer_hash, 18, 2);
$node = substr($customer_hash, 20, 12);
return sprintf('%8s-%4s-%4s-%2s%2s-%12s', $time_low, $time_mid, $time_hi_and_version, $clk_seq_hi_res, $clk_seq_low, $node);
}
private function buildEcImpression($item, $list, $category)
{
return array('id' => $item->getId(),
'name' => addslashes($item->getName()),
'list' => $list,
'category' => addslashes($category),
'price' => $item->getPrice02IncTaxMax());
}
private function buildEcProduct($product, $category, $price = null, $quantity = 0, $variant = '', $discount = null)
{
return array('id' => $product->getId(),
'name' => addslashes($product->getName()),
'price' => $price == null ? $product->getPrice02IncTaxMax() : $price,
'quantity' => $quantity,
'variant' => $variant,
'category' => addslashes($category),
'discount' => $discount);
}
/**
* クーポンコードと ProductClass ごとの discount を返す
*
* @param ItemHolderInterface $order
* @param couponOrderId $couponOrderId
* @return array $couponCode, $couponDiscounts クーポンコードと ProductClass::id をキーにした discount
*/
private function getCouponDiscounts($order, $couponOrderId)
{
$entityManager = $this->container->get('doctrine')->getManager();
$couponOrderRepository = $entityManager->getRepository(CouponOrder::class);
$couponRepository = $entityManager->getRepository(Coupon::class);
$couponOrder = $couponOrderRepository->find($couponOrderId);
$couponCode = $couponOrder->getCouponCd();
$coupon = $couponRepository->findActiveCoupon($couponCode);
$couponProducts = $this->existsCouponProduct($coupon, $order);
$couponDiscounts = $this->couponDiscounts($coupon, $couponProducts);
return [$couponCode, $couponDiscounts];
}
/**
* 注文にクーポン対象商品が含まれているか確認する.
*
* @param Coupon $Coupon
* @param ItemHolderInterface $Order
*
* @return array
*/
private function existsCouponProduct(Coupon $Coupon, ItemHolderInterface $Order)
{
$couponProducts = [];
if (!is_null($Coupon)) {
// 対象商品の存在確認
if ($Coupon->getCouponType() == Coupon::PRODUCT) {
// 商品の場合
$couponProducts = $this->containsProduct($Coupon, $Order);
} elseif ($Coupon->getCouponType() == Coupon::CATEGORY) {
// カテゴリの場合
$couponProducts = $this->containsCategory($Coupon, $Order);
} elseif ($Coupon->getCouponType() == Coupon::ALL) {
// all product
// 一致する商品IDがあればtrueを返す
/** @var OrderItem $detail */
foreach ($Order->getItems()->getProductClasses() as $detail) {
$couponProducts = $this->getCouponProducts($detail, $couponProducts);
}
}
}
return $couponProducts;
}
/**
* 合計、値引きを再計算する.
*
* 税率が 0 以下の場合は、TaxRule から取得し直して再計算する
*
* @param Coupon $Coupon
* @param array $couponProducts ProductClass::id をキーにした単価, 数量, 税率の連想配列
* @return array $couponDiscounts ProductClass::id をキーにした discount
*/
public function couponDiscounts(Coupon $Coupon, $couponProducts)
{
$couponDiscounts = [];
// クーポンコードが存在する場合カートに入っている商品の値引き額を設定する
if ($Coupon) {
// 対象商品の存在確認.
// 割引対象商品が存在する場合は値引き額を取得し、クーポンの対象となる ProductClass の数で値引き額を割る
// 割引対象商品がある場合は値引き額を ProductClass ごとに計算する
if ($Coupon->getDiscountType() == Coupon::DISCOUNT_PRICE) {
$discount = $Coupon->getDiscountPrice();
foreach ($couponProducts as $productClassId => $value) {
$couponDiscounts[$productClassId] = $discount / count($couponProducts);
}
} else {
$entityManager = $this->container->get('doctrine')->getManager();
$productClassRepository = $entityManager->getRepository(ProductClass::class);
$taxRuleRepository = $entityManager->getRepository(TaxRule::class);
$taxRuleService = $this->container->get(TaxRuleService::class);
// 値引き前の金額で割引率を算出する
// /** @var TaxRule $DefaultTaxRule */
$DefaultTaxRule = $taxRuleRepository->getByRule();
// include tax
foreach ($couponProducts as $productClassId => $value) {
if ($value['tax_rate'] < 1 || $value['rounding_type_id'] === null) {
$ProductClass = $productClassRepository->find($productClassId);
$TaxRule = $taxRuleRepository->getByRule($ProductClass->getProduct(), $ProductClass);
$value['tax_rate'] = $TaxRule->getTaxRate();
$value['rounding_type_id'] = $TaxRule->getRoundingType()->getId();
}
$subTotal = ($value['price'] + $taxRuleService->calcTax($value['price'], $value['tax_rate'], $value['rounding_type_id'])) * $value['quantity'];
// // 丸め規則はデフォルトの課税規則に従う
$discount = $taxRuleService->calcTax(
$subTotal,
$Coupon->getDiscountRate(),
$DefaultTaxRule->getRoundingType()->getId(),
$DefaultTaxRule->getTaxAdjust()
);
$couponDiscounts[$productClassId] = $discount;
}
}
}
return $couponDiscounts;
}
/**
* 商品がクーポン適用の対象か調査する.
*
* @param Coupon $Coupon
* @param ItemHolderInterface $Order
*
* @return array
*/
private function containsProduct(Coupon $Coupon, ItemHolderInterface $Order)
{
// クーポンの対象商品IDを配列にする
$targetProductIds = [];
$couponProducts = [];
foreach ($Coupon->getCouponDetails() as $detail) {
$targetProductIds[] = $detail->getProduct()->getId();
}
// 一致する商品IDがあればtrueを返す
/* @var $detail OrderItem */
foreach ($Order->getProductOrderItems() as $detail) {
if (in_array($detail->getProduct()->getId(), $targetProductIds)) {
$couponProducts = $this->getCouponProducts($detail, $couponProducts);
}
}
return $couponProducts;
}
/**
* カテゴリがクーポン適用の対象か調査する.
* 下位のカテゴリから上位のカテゴリに向けて検索する.
*
* @param Coupon $Coupon
* @param ItemHolderInterface $Order
*
* @return array
*/
private function containsCategory(Coupon $Coupon, ItemHolderInterface $Order)
{
// クーポンの対象カテゴリIDを配列にする
$targetCategoryIds = [];
$couponProducts = [];
foreach ($Coupon->getCouponDetails() as $detail) {
$targetCategoryIds[] = $detail->getCategory()->getId();
}
// 受注データからカテゴリIDを取得する
/* @var $orderDetail OrderItem */
foreach ($Order->getProductOrderItems() as $orderDetail) {
foreach ($orderDetail->getProduct()->getProductCategories() as $productCategory) {
if ($this->existsDepthCategory($targetCategoryIds, $productCategory->getCategory())) {
$couponProducts = $this->getCouponProducts($orderDetail, $couponProducts);
break;
}
}
}
return $couponProducts;
}
/**
* クーポン対象のカテゴリが存在するか確認にする.
*
* @param array $targetCategoryIds
* @param Category $Category
*
* @return bool
*/
private function existsDepthCategory(&$targetCategoryIds, Category $Category)
{
// Categoryがnullならfalse
if (is_null($Category)) {
return false;
}
// 対象カテゴリか確認
if (in_array($Category->getId(), $targetCategoryIds)) {
return true;
}
// Categoryをテーブルから取得
if (is_null($Category->getParent())) {
return false;
}
// 親カテゴリをテーブルから取得
/** @var Category $ParentCategory */
$entityManager = $this->container->get('doctrine')->getManager();
$categoryRepository = $entityManager->getRepository(Category::class);
$ParentCategory = $categoryRepository->find($Category->getParent());
if ($ParentCategory) {
return false;
}
return $this->existsDepthCategory($targetCategoryIds, $ParentCategory);
}
/**
* @param OrderItem $orderItem
* @param array $couponProducts
*
* @return mixed
*/
private function getCouponProducts(OrderItem $orderItem, array $couponProducts = [])
{
if (array_key_exists($orderItem->getProductClass()->getId(), $couponProducts)) {
$couponProducts[$orderItem->getProductClass()->getId()]['quantity'] += $orderItem->getQuantity();
} else {
$couponProducts[$orderItem->getProductClass()->getId()] = [
'price' => $orderItem->getPrice(),
'quantity' => $orderItem->getQuantity(),
'tax_rate' => $orderItem->getTaxRate(),
'rounding_type_id' => $orderItem->getRoundingType() ? $orderItem->getRoundingType()->getId() : null,
];
}
return $couponProducts;
}
}