first commit
This commit is contained in:
@@ -0,0 +1,248 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\GTMBundle\ServerSideGTMEvent;
|
||||
|
||||
use KupShop\GTMBundle\ServerSideGTMEvent\DataLoaders\Products;
|
||||
use KupShop\KupShopBundle\Context\CurrencyContext;
|
||||
use KupShop\KupShopBundle\Context\DomainContext;
|
||||
use KupShop\KupShopBundle\Context\LanguageContext;
|
||||
use KupShop\KupShopBundle\Context\PriceLevelContext;
|
||||
use KupShop\KupShopBundle\Context\UserContext;
|
||||
use KupShop\KupShopBundle\Util\System\AsyncQueue;
|
||||
use KupShop\OrderingBundle\Event\OrderEvent;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
|
||||
abstract class AbstractServerSideGTMEvent
|
||||
{
|
||||
use AnalyticsSessionIDsTrait;
|
||||
use FetchUsersStatsOrderData;
|
||||
|
||||
public const ContainerConfigPath = '/_kyticka';
|
||||
|
||||
private $debugHash;
|
||||
private $serverUrl;
|
||||
|
||||
protected ?Request $request = null;
|
||||
|
||||
public function __construct(
|
||||
protected readonly DomainContext $domainContext,
|
||||
protected readonly CurrencyContext $currencyContext,
|
||||
protected readonly LanguageContext $languageContext,
|
||||
protected readonly UserContext $userContext,
|
||||
protected readonly Products $productLoader,
|
||||
protected readonly RequestStack $requestStack,
|
||||
protected readonly PriceLevelContext $priceLevelContext,
|
||||
protected readonly LoggerInterface $logger,
|
||||
protected readonly AsyncQueue $asyncQueue,
|
||||
) {
|
||||
$dbCfg = \Settings::getDefault();
|
||||
$this->setDebugHash($dbCfg['analytics']['google_tag_manager']['debugHash'] ?? false);
|
||||
$this->setServerUrl($dbCfg['analytics']['google_tag_manager']['serverUrl'] ?? false);
|
||||
}
|
||||
|
||||
public function setDebugHash($hash)
|
||||
{
|
||||
$this->debugHash = $hash;
|
||||
}
|
||||
|
||||
public function setServerUrl($url)
|
||||
{
|
||||
$this->serverUrl = $url;
|
||||
}
|
||||
|
||||
public static function getContainerConfigPath(): ?string
|
||||
{
|
||||
global $cfg;
|
||||
$hasUrl = $cfg['Addr']['print'] ?? null;
|
||||
if (findModule(\Modules::GTM, \Modules::SUB_CONTAINER_CONFIG) && $hasUrl) {
|
||||
return substr($cfg['Addr']['print'], 0, -1).AbstractServerSideGTMEvent::ContainerConfigPath;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getServerUrl(): string
|
||||
{
|
||||
$dbcfg = \Settings::getDefault();
|
||||
if (
|
||||
(($dbcfg['analytics']['google_tag_manager']['container_config_ss'] ?? false) === 'Y')
|
||||
&& ($domain = self::getContainerConfigPath())
|
||||
) {
|
||||
return $domain;
|
||||
}
|
||||
|
||||
return $this->serverUrl;
|
||||
}
|
||||
|
||||
public function start(Event $event)
|
||||
{
|
||||
$serverUrl = $this->getServerUrl();
|
||||
if (!$serverUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
$payload = $this->getPayload($event);
|
||||
$payloadJson = json_encode($payload);
|
||||
|
||||
// Protože skripty v údržbě končí exit(), tím pádem se tam neprovede async handling
|
||||
// A takovej divnej if proto, abysme si byli jisti ze to je udrzba, resp. ze to dela superadmin
|
||||
if (isAdministration() && isSuperuser()) {
|
||||
$this->sendData($serverUrl, $payloadJson, $this->getHeader());
|
||||
} else {
|
||||
$this->sendDataAsync($serverUrl, $payloadJson);
|
||||
}
|
||||
|
||||
$this->logger->notice('[GTM-SS] Purchase payload', $payload);
|
||||
}
|
||||
|
||||
public function setRequest(?Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
abstract protected function getPayload(Event $event): array;
|
||||
|
||||
final protected function sendDataAsync(string $url, string $payload)
|
||||
{
|
||||
$headers = $this->getHeader();
|
||||
$this->asyncQueue->addHandler(fn () => $this->sendData($url, $payload, $headers), '[GTM-SS] measure event');
|
||||
}
|
||||
|
||||
protected function sendData(string $url, string $payload, ?array $headers = null): void
|
||||
{
|
||||
$headers ??= $this->getHeader();
|
||||
if ($this->debugHash) {
|
||||
$headers[] = 'x-gtm-server-preview: '.$this->debugHash;
|
||||
}
|
||||
|
||||
$ch = curl_init('https://'.$url.'/measure'); // example: https://m.shop.wpj.cz/measure
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 1);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_exec($ch);
|
||||
curl_close($ch);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getHeader(): array
|
||||
{
|
||||
return [
|
||||
'Content-Type: application/json',
|
||||
"Cookie: _fbp={$this->getBrowserFBPixelSessionId($this->getRequest())}",
|
||||
];
|
||||
}
|
||||
|
||||
protected function getCommonData(Event $event): array
|
||||
{
|
||||
$domain = $this->domainContext->getActiveId();
|
||||
$request = $this->getRequest();
|
||||
|
||||
$referer = $request->headers->get('referer');
|
||||
|
||||
return [
|
||||
'client_id' => $this->getClientId(),
|
||||
'user_id' => $this->getUserId(),
|
||||
'ga_session_id' => $this->getBrowserGASessionId($request),
|
||||
'ga_session_id_2' => $this->getBrowserGASessionId($request, '_2'),
|
||||
'ga_session_id_3' => $this->getBrowserGASessionId($request, '_3'),
|
||||
'currency' => $this->currencyContext->getActiveId(),
|
||||
'language' => $this->languageContext->getActiveId(),
|
||||
'page_encoding' => 'UTF-8',
|
||||
'page_hostname' => $domain,
|
||||
'page_location' => "https://{$domain}",
|
||||
'ip_override' => $request->getClientIp(),
|
||||
'page_path' => $referer,
|
||||
'page_title' => null,
|
||||
'page_referrer' => $referer,
|
||||
'user_agent' => $request->headers->get('User-Agent'),
|
||||
'user_data' => $this->getUserData($event),
|
||||
'domain' => $domain,
|
||||
];
|
||||
}
|
||||
|
||||
protected function getUserId(): string
|
||||
{
|
||||
return (string) \Cart::getCartID(false);
|
||||
}
|
||||
|
||||
protected function getClientId(): string
|
||||
{
|
||||
return \Cart::getCartID(false).'_noCookie';
|
||||
}
|
||||
|
||||
protected function getUserData(Event $event): ?array
|
||||
{
|
||||
$prefixArray = [];
|
||||
if ($priceLevel = $this->priceLevelContext->getActive()) {
|
||||
$prefixArray['price_level'] = $priceLevel->name;
|
||||
}
|
||||
|
||||
if ($event instanceof OrderEvent) {
|
||||
$order = $event->getOrder();
|
||||
$user = $order->getUser();
|
||||
$prefixArray += [
|
||||
'id' => $user instanceof \User ? (string) $user->id : null,
|
||||
'groups' => is_array($user?->getGroups()) ? array_values(array_map(fn ($v) => $v['name'], $user->getGroups())) : null,
|
||||
];
|
||||
|
||||
if ($admin = getAdminUser()) {
|
||||
$prefixArray['admin'] = $admin['login'];
|
||||
}
|
||||
|
||||
$userStats = $this->fetchUsersStatsOrderData($order);
|
||||
|
||||
return $prefixArray + [
|
||||
'lastOrderDate' => $userStats['last_date'] ?? null,
|
||||
'firstOrderDate' => $userStats['first_date'] ?? null,
|
||||
'countOrders' => $userStats['count'] ?? null,
|
||||
'email_address' => $order->invoice_email,
|
||||
'phone_number' => $order->invoice_phone,
|
||||
'address' => [
|
||||
'first_name' => $order->invoice_name,
|
||||
'last_name' => $order->invoice_surname,
|
||||
'street' => $order->invoice_street,
|
||||
'city' => $order->invoice_city,
|
||||
'region' => $order->invoice_state,
|
||||
'postal_code' => $order->invoice_zip,
|
||||
'country' => $order->invoice_country,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
if ($user = $this->userContext->getActive()) {
|
||||
return $prefixArray + [
|
||||
'email_address' => $user->email ?? null,
|
||||
'phone_number' => $user->phone ?? null,
|
||||
'address' => [
|
||||
'first_name' => $user->name ?? null,
|
||||
'last_name' => $user->surname ?? null,
|
||||
'street' => $user->street ?? null,
|
||||
'city' => $user->city ?? null,
|
||||
'region' => $user->state ?? null,
|
||||
'postal_code' => $user->zip ?? null,
|
||||
'country' => $user->country ?? null,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function getRequest(): ?Request
|
||||
{
|
||||
$request = $this->request ?? $this->requestStack->getMainRequest();
|
||||
|
||||
if (!$request && !isFrontend()) {
|
||||
$request = new Request();
|
||||
}
|
||||
|
||||
return $request;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KupShop\GTMBundle\ServerSideGTMEvent;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
trait AnalyticsSessionIDsTrait
|
||||
{
|
||||
protected function getBrowserGASessionId(Request $request, string $number = '')
|
||||
{
|
||||
$dbcfg = \Settings::getDefault();
|
||||
$measurement_id = $dbcfg->analytics['google_tag_manager']['ga4_measurement_id'.$number] ?? '';
|
||||
$cookies = $request->cookies->all();
|
||||
// Cookie name example: '_ga_1YS1VWHG3V'.
|
||||
$cookie_name = '_ga_'.str_replace('G-', '', $measurement_id);
|
||||
if (isset($cookies[$cookie_name])) {
|
||||
// Cookie value example: 'GS2.1.s1746623129$o109$g1$t1746623938$j0$l0$h0'.
|
||||
// Cookie value example: 'GS1.1.1659710029.4.1.1659710504.0'.
|
||||
// Session Id: ^^^^^^^^^^.
|
||||
if (preg_match('/^GS\d+\.\d+\.?s?(\d+)/', $cookies[$cookie_name], $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
protected function getGA3SessionId(): string
|
||||
{
|
||||
return preg_replace("/^.+\.(.+?\..+?)$/", '\\1', $_COOKIE['_ga'] ?? '');
|
||||
}
|
||||
|
||||
protected function getBrowserFBPixelSessionId(?Request $request)
|
||||
{
|
||||
$cookies = $request?->cookies->all();
|
||||
if (isset($cookies['_fbp'])) {
|
||||
return $cookies['_fbp'];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\GTMBundle\ServerSideGTMEvent;
|
||||
|
||||
use KupShop\GTMBundle\Utils\DataLoaders\PurchaseState;
|
||||
use KupShop\OrderingBundle\Event\CartItemEvent;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
use Symfony\Contracts\Service\Attribute\Required;
|
||||
|
||||
class CartAddItemEvent extends AbstractServerSideGTMEvent
|
||||
{
|
||||
#[Required]
|
||||
public PurchaseState $purchaseState;
|
||||
|
||||
public function getPayload(Event $event): array
|
||||
{
|
||||
if (!($event instanceof CartItemEvent)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$product = $event->getProduct();
|
||||
$product->fetchVariations();
|
||||
if (empty($product->producer)) {
|
||||
$product->fetchProducer();
|
||||
}
|
||||
$product->fetchImages('product_gallery');
|
||||
|
||||
$item = $this->productLoader->loadProductObject($product);
|
||||
$item['quantity'] = abs($event->getDifferences()['pieces_diff'] ?? 1);
|
||||
$item['index'] = 1;
|
||||
if (($soldOut = $this->purchaseState->getSoldOut($product)) !== null) {
|
||||
$item['soldOut'] = $soldOut;
|
||||
}
|
||||
|
||||
$params = $this->getCommonData($event);
|
||||
$params['items'] = [$item];
|
||||
|
||||
return [
|
||||
'events' => [
|
||||
[
|
||||
'name' => $this->getEventName(),
|
||||
'user_id' => $this->getUserId(),
|
||||
'client_id' => $this->getClientId(),
|
||||
'ga3_session_id' => $this->getGA3SessionId(),
|
||||
'params' => $params,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected function getCommonData(Event $event): array
|
||||
{
|
||||
$dbcfg = \Settings::getDefault();
|
||||
$product = $event->getProduct();
|
||||
|
||||
$request = $this->getRequest();
|
||||
|
||||
return array_merge(parent::getCommonData($event), [
|
||||
'page_title' => $product->meta_title ?: $product->title.' - '.$dbcfg['shop_title'],
|
||||
'value' => $this->getEventPrice($event)->asFloat(),
|
||||
'event_id' => $request->cookies->get('gtm_event_uid'),
|
||||
]);
|
||||
}
|
||||
|
||||
private function getEventPrice(Event $event): \Decimal
|
||||
{
|
||||
/** @var CartItemEvent $event */
|
||||
$price = $event->getProduct()->getProductPrice()->getPriceWithVat();
|
||||
$pieces = (int) abs($event->getDifferences()['pieces_diff'] ?? 1);
|
||||
|
||||
return $price->mul(toDecimal($pieces));
|
||||
}
|
||||
|
||||
protected function getEventName(): string
|
||||
{
|
||||
return 'add_to_cart';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KupShop\GTMBundle\ServerSideGTMEvent;
|
||||
|
||||
use KupShop\OrderingBundle\Event\CartItemEvent;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
|
||||
class CartRemoveItemEvent extends CartAddItemEvent
|
||||
{
|
||||
public function getPayload(Event|CartItemEvent $event): array
|
||||
{
|
||||
if (!$event->getProduct()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return parent::getPayload($event);
|
||||
}
|
||||
|
||||
protected function getEventName(): string
|
||||
{
|
||||
return 'remove_from_cart';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\GTMBundle\ServerSideGTMEvent\DataLoaders;
|
||||
|
||||
use KupShop\GTMBundle\Utils\PriceComputer;
|
||||
use KupShop\KupShopBundle\Context\ContextManager;
|
||||
use KupShop\KupShopBundle\Context\LanguageContext;
|
||||
use KupShop\KupShopBundle\Util\StringUtil;
|
||||
use KupShop\KupShopBundle\Util\System\UrlFinder;
|
||||
use Symfony\Component\Routing\Router;
|
||||
use Symfony\Contracts\Service\Attribute\Required;
|
||||
|
||||
class Products
|
||||
{
|
||||
/**
|
||||
* @var ContextManager
|
||||
*/
|
||||
protected $contextManager;
|
||||
|
||||
/**
|
||||
* @var PriceComputer
|
||||
*/
|
||||
protected $priceComputer;
|
||||
|
||||
/**
|
||||
* @var LanguageContext
|
||||
*/
|
||||
protected $languageContext;
|
||||
|
||||
protected UrlFinder $urlFinder;
|
||||
|
||||
#[Required]
|
||||
final public function setUrlFinder(UrlFinder $urlFinder): void
|
||||
{
|
||||
$this->urlFinder = $urlFinder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Product|\Variation $product
|
||||
*/
|
||||
public function loadProductObject($product): array
|
||||
{
|
||||
// TODO: tmp hotfix - pompo.sk pada na pameti, velky pocet sekci a tady se loaduje SK strom a pak i CZ strom, takze toho je fakt hooodne :)
|
||||
// TODO: v jednu chvili se do pameti dokaze asi nacist cca 10k sekci, protoze APCu + memcached x2 protoze jeste strom v default jazyce
|
||||
increaseMemoryLimit(300);
|
||||
|
||||
$data = [
|
||||
'item_id' => join('_', array_filter([$product->id, $product->variationId ?? ''])),
|
||||
'product_code' => $product->productCode,
|
||||
'product_id' => $product->id,
|
||||
'variation_code' => $product->variationCode ?? '',
|
||||
'variation_id' => $product->variationId ?? '',
|
||||
'ean' => $product->ean,
|
||||
'item_name' => $product->title,
|
||||
'discount' => $product->discount->asFloat() ?? 0.0,
|
||||
'item_brand' => $product->producer['title'] ?? '',
|
||||
'item_variant' => $product->variationTitle ?? '',
|
||||
'price' => $this->priceComputer->getPrice($product->getProductPrice()),
|
||||
'price_with_vat' => $this->priceComputer->getPrice($product->getProductPrice(), true),
|
||||
'price_without_vat' => $this->priceComputer->getPrice($product->getProductPrice(), false),
|
||||
'price_vat' => $this->priceComputer->getPrice($product->getProductPrice()->getVatValue(), false, false),
|
||||
'vat_rate' => $product->getProductPrice()->getVat()->asInteger(),
|
||||
'description' => $product->descr,
|
||||
'url' => path('products', ['id' => $product->id, 'name' => StringUtil::slugify($product->title)], Router::ABSOLUTE_URL),
|
||||
];
|
||||
|
||||
if ($img_url = $product->image['src_big'] ?? null) {
|
||||
$data['img_url'] = $this->urlFinder->staticUrlAbsolute($img_url);
|
||||
}
|
||||
|
||||
$categories = $product->fetchSections();
|
||||
$category = $categories[0] ?? [];
|
||||
|
||||
foreach ($this->getCategoryPath($category) as $i => $value) {
|
||||
$data['item_category'.(($i) ? $i + 1 : '')] = $value;
|
||||
}
|
||||
|
||||
if (findModule(\Modules::PRODUCTS, \Modules::SUB_PRICE_BUY)) {
|
||||
$data['price_buy'] = $this->priceComputer->getPrice($product->price_buy);
|
||||
}
|
||||
|
||||
return array_merge($this->getDataInDefaultLanguage($product), $data);
|
||||
}
|
||||
|
||||
public function getCategoryPath($category)
|
||||
{
|
||||
if (!$category) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$categoryNames = [];
|
||||
do {
|
||||
$categoryNames[] = $category->getNameShort() ?? '';
|
||||
} while (($category = $category->getParent()) && count($categoryNames) < 5);
|
||||
|
||||
return array_reverse($categoryNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Product|\Variation $product
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getDataInDefaultLanguage($product)
|
||||
{
|
||||
$array = [];
|
||||
$this->contextManager->activateContexts([LanguageContext::class => $this->languageContext->getDefaultId()], function () use ($product, &$array) {
|
||||
$langProduct = new \Variation();
|
||||
$product = $langProduct->createProductOrVariation($product->id, $product->variationId ?? null);
|
||||
$product->createFromDB(); // in default language
|
||||
$product->fetchProducer();
|
||||
$array = [
|
||||
'default_item_name' => $product->title,
|
||||
'default_item_variant' => $product->variationTitle ?? null,
|
||||
'default_item_brand' => $product->producer['name'] ?? '',
|
||||
];
|
||||
$categories = $product->fetchSections();
|
||||
$category = $categories[0] ?? [];
|
||||
|
||||
foreach ($this->getCategoryPath($category) as $i => $value) {
|
||||
$array['default_item_category'.(($i) ? $i + 1 : '')] = $value;
|
||||
}
|
||||
});
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function setPriceComputer(PriceComputer $priceComputer): void
|
||||
{
|
||||
$this->priceComputer = $priceComputer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function setContextManager(ContextManager $contextManager): void
|
||||
{
|
||||
$this->contextManager = $contextManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function setLanguageContext(LanguageContext $languageContext): void
|
||||
{
|
||||
$this->languageContext = $languageContext;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KupShop\GTMBundle\ServerSideGTMEvent;
|
||||
|
||||
use Query\Operator;
|
||||
|
||||
trait FetchUsersStatsOrderData
|
||||
{
|
||||
public function fetchUsersStatsOrderData(\Order $order): array
|
||||
{
|
||||
return sqlQueryBuilder()
|
||||
->select('DATE(max(date_created)) last_date, DATE(min(date_created)) first_date, count(date_created) count')
|
||||
->from('orders')
|
||||
->where(Operator::like(['invoice_email' => $order->invoice_email]))
|
||||
->andWhere(Operator::not(Operator::equals(['id' => $order->id])))
|
||||
->execute()->fetchAssociative();
|
||||
}
|
||||
}
|
||||
144
bundles/KupShop/GTMBundle/ServerSideGTMEvent/OrderComplete.php
Normal file
144
bundles/KupShop/GTMBundle/ServerSideGTMEvent/OrderComplete.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\GTMBundle\ServerSideGTMEvent;
|
||||
|
||||
use KupShop\GTMBundle\Utils\DataLoaders\PurchaseState;
|
||||
use KupShop\GTMBundle\Utils\PriceComputer;
|
||||
use KupShop\OrderingBundle\Event\OrderEvent;
|
||||
use KupShop\OrderingBundle\Util\Order\DeliveryInfo;
|
||||
use KupShop\OrderingBundle\Util\Order\OrderInfo;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
use Symfony\Contracts\Service\Attribute\Required;
|
||||
|
||||
class OrderComplete extends AbstractServerSideGTMEvent
|
||||
{
|
||||
private PriceComputer $priceComputer;
|
||||
private DeliveryInfo $deliveryInfo;
|
||||
private PurchaseState $purchaseState;
|
||||
|
||||
public function getPayload(Event $event): array
|
||||
{
|
||||
if (!($event instanceof OrderEvent)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$dbcfg = \Settings::getDefault();
|
||||
$order = $event->getOrder();
|
||||
$items = [];
|
||||
$index = 1;
|
||||
foreach ($order->fetchItems() as $item) {
|
||||
if (!isset($item['product'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var \Product|\Variation $product */
|
||||
$product = $item['product'];
|
||||
$product->fetchProducer();
|
||||
$product->fetchVariations();
|
||||
|
||||
$newItem = $this->productLoader->loadProductObject($product);
|
||||
$newItem['index'] = $index++;
|
||||
$newItem['quantity'] = $item['pieces'];
|
||||
|
||||
$prices = $this->purchaseState->getPrices($item);
|
||||
$newItem['price'] = $prices['price'];
|
||||
$newItem['price_with_vat'] = $prices['priceWithVat'];
|
||||
$newItem['price_without_vat'] = $prices['priceWithoutVat'];
|
||||
$newItem['price_vat'] = $prices['priceVat'];
|
||||
$newItem['vat_rate'] = $prices['vatRate'];
|
||||
$newItem['discount'] = $product->discount->asFloat();
|
||||
if (($soldOut = $this->purchaseState->getSoldOut($product)) !== null) {
|
||||
$newItem['soldOut'] = $soldOut;
|
||||
}
|
||||
$items[] = $newItem;
|
||||
}
|
||||
|
||||
$deliveryType = $order->getDeliveryType();
|
||||
$shippingPrice = $this->getDeliveryPrice($order);
|
||||
$shippinPrice = $this->priceComputer->getPrice($shippingPrice ?? toDecimal(0));
|
||||
|
||||
if (($dbcfg->analytics['google_tag_manager']['withoutDelivery'] ?? 'N') == 'Y') {
|
||||
// Tohle je kvuli tomu, ze podle nastaveni nevime jestli ma byt bez nebo s dph, proto nechame rozhodnout funkce uvnitr a pak to akorat proste odectu
|
||||
$tp = toDecimal($this->priceComputer->getPrice($order->getTotalPrice()));
|
||||
$shipp = toDecimal($shippinPrice);
|
||||
$totalPrice = $tp->sub($shipp);
|
||||
} else {
|
||||
$totalPrice = $order->getTotalPrice();
|
||||
}
|
||||
|
||||
$commonData = $this->getCommonData($event);
|
||||
if ($coupons = OrderInfo::getUsedCoupons($order)) {
|
||||
$coupons = join(',', $coupons);
|
||||
}
|
||||
$params = $commonData + [
|
||||
'currency' => $order->currency,
|
||||
'transaction_id' => $order->order_no,
|
||||
'value' => $this->priceComputer->getPrice($totalPrice),
|
||||
'vat' => $this->priceComputer->getPrice($order->total_price_array['value_vat'], null, false),
|
||||
'delivery_type' => $deliveryType->getDelivery()->getClassName(),
|
||||
'payment_type' => $deliveryType->getPayment()->getClassName(),
|
||||
'payment' => $this->purchaseState->getPaymentObject($order),
|
||||
'items' => $items,
|
||||
'shipping' => $this->priceComputer->getPrice($order->getDeliveryPrice()),
|
||||
'discounts' => $this->purchaseState->getDiscounts($order->getPurchaseState(true)),
|
||||
'coupon' => $coupons ?: '',
|
||||
'charges' => $this->purchaseState->getCharges($order),
|
||||
'cjevent' => $order->getData('cjevent'),
|
||||
];
|
||||
|
||||
return [
|
||||
'user_id' => $commonData['user_id'],
|
||||
'client_id' => $commonData['client_id'],
|
||||
'ga3_session_id' => $this->getGA3SessionId(),
|
||||
'events' => [
|
||||
[
|
||||
'name' => 'purchase',
|
||||
'params' => $params,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected function getCommonData(Event $event): array
|
||||
{
|
||||
/** @var \Order $order */
|
||||
$order = $event->getOrder();
|
||||
|
||||
return array_merge(parent::getCommonData($event), [
|
||||
'user_id' => $order->getData('cart_id'),
|
||||
'client_id' => $order->getData('client_id') ?: $order->getData('cart_id').'_noCookie',
|
||||
'currency' => $order->getCurrency(),
|
||||
'ip_override' => $order->getData('ip_address'),
|
||||
'language' => $order->getLanguage(),
|
||||
'page_path' => path('kupshop_content_orders_order', ['id' => $order->id]),
|
||||
'page_title' => str_replace('%ORDERNO', $order->order_no, translate_shop('title', 'orderView')),
|
||||
'page_referrer' => $order->getData('referer'),
|
||||
'user_agent' => $order->getData('user_agent'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function getDeliveryPrice(\Order $order)
|
||||
{
|
||||
$delivery_item = $this->deliveryInfo->getDeliveryItem($order);
|
||||
|
||||
return $delivery_item ? $order->fetchItems()[$delivery_item]['total_price'] : null;
|
||||
}
|
||||
|
||||
#[Required]
|
||||
public function setPriceComputer(PriceComputer $priceComputer): void
|
||||
{
|
||||
$this->priceComputer = $priceComputer;
|
||||
}
|
||||
|
||||
#[Required]
|
||||
public function setDeliveryInfo(DeliveryInfo $deliveryInfo): void
|
||||
{
|
||||
$this->deliveryInfo = $deliveryInfo;
|
||||
}
|
||||
|
||||
#[Required]
|
||||
public function setPurchaseState(PurchaseState $purchaseState): void
|
||||
{
|
||||
$this->purchaseState = $purchaseState;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\GTMBundle\ServerSideGTMEvent;
|
||||
|
||||
use KupShop\GTMBundle\Utils\DataLoaders\PurchaseState;
|
||||
use KupShop\GTMBundle\Utils\PriceComputer;
|
||||
use KupShop\OrderingBundle\Util\Order\DeliveryInfo;
|
||||
use KupShop\OrderingBundle\Util\Order\OrderInfo;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
use Symfony\Contracts\Service\Attribute\Required;
|
||||
|
||||
class OrderRefundBaseEvent extends AbstractServerSideGTMEvent
|
||||
{
|
||||
public string $refundType;
|
||||
|
||||
#[Required]
|
||||
public PriceComputer $priceComputer;
|
||||
|
||||
#[Required]
|
||||
public DeliveryInfo $deliveryInfo;
|
||||
|
||||
#[Required]
|
||||
public PurchaseState $purchaseState;
|
||||
|
||||
public function getPayload(Event $event): array
|
||||
{
|
||||
$dbcfg = \Settings::getDefault();
|
||||
$order = $event->getOrder();
|
||||
|
||||
$items = [];
|
||||
|
||||
$index = 1;
|
||||
foreach ($order->fetchItems() as $item) {
|
||||
if (empty($item['product'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var \Product|\Variation $product */
|
||||
$product = $item['product'];
|
||||
$product->fetchProducer();
|
||||
|
||||
$newItem = $this->productLoader->loadProductObject($product);
|
||||
$newItem['index'] = $index++;
|
||||
$newItem['discount'] = $product->discount->asFloat();
|
||||
$newItem['quantity'] = $item['pieces'];
|
||||
|
||||
$prices = $this->purchaseState->getPrices($item);
|
||||
$newItem['price'] = $prices['price'];
|
||||
$newItem['price_with_vat'] = $prices['priceWithVat'];
|
||||
$newItem['price_without_vat'] = $prices['priceWithoutVat'];
|
||||
$newItem['price_vat'] = $prices['priceVat'];
|
||||
$newItem['vat_rate'] = $prices['vatRate'];
|
||||
$items[] = $newItem;
|
||||
}
|
||||
|
||||
$deliveryType = $order->getDeliveryType();
|
||||
$shippingPrice = $this->getDeliveryPrice($order);
|
||||
$shippinPrice = $this->priceComputer->getPrice($shippingPrice ?? toDecimal(0));
|
||||
|
||||
if (($dbcfg->analytics['google_tag_manager']['withoutDelivery'] ?? 'N') == 'Y') {
|
||||
$tp = toDecimal($this->priceComputer->getPrice($order->getTotalPrice()));
|
||||
$shipp = toDecimal($shippinPrice);
|
||||
$totalPrice = $tp->sub($shipp);
|
||||
} else {
|
||||
$totalPrice = $order->getTotalPrice();
|
||||
}
|
||||
|
||||
if ($coupons = OrderInfo::getUsedCoupons($order)) {
|
||||
$coupons = join(',', $coupons);
|
||||
}
|
||||
|
||||
$params = [
|
||||
'currency' => $order->currency,
|
||||
'transaction_id' => $order->order_no,
|
||||
'value' => $this->priceComputer->getPrice($totalPrice),
|
||||
'coupon' => $coupons ?: '',
|
||||
'shipping' => $this->priceComputer->getPrice($order->getDeliveryPrice()),
|
||||
'tax' => $this->priceComputer->getPrice($order->total_price_array['value_vat'], null, false),
|
||||
'delivery_type' => $deliveryType->getDelivery()?->getClassName() ?? '',
|
||||
'payment_type' => $deliveryType->getPayment()?->getClassName() ?? '',
|
||||
'refund_type' => $this->refundType,
|
||||
'items' => $items,
|
||||
'user_data' => array_merge(['sha_email' => hash('sha256', $order->invoice_email.($dbcfg->analytics['google_tag_manager']['sha_salt'] ?? false ?: ''))], $this->getUserData($event)),
|
||||
'cjevent' => $order->getData('cjevent'),
|
||||
];
|
||||
|
||||
return [
|
||||
'events' => [
|
||||
[
|
||||
'name' => 'refund',
|
||||
'params' => $params,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function getDeliveryPrice(\Order $order)
|
||||
{
|
||||
$delivery_item = $this->deliveryInfo->getDeliveryItem($order);
|
||||
|
||||
return $delivery_item ? $order->fetchItems()[$delivery_item]['total_price'] : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\GTMBundle\ServerSideGTMEvent;
|
||||
|
||||
class OrderStornoEvent extends OrderRefundBaseEvent
|
||||
{
|
||||
public string $refundType = 'storno';
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\GTMBundle\ServerSideGTMEvent;
|
||||
|
||||
use KupShop\GTMBundle\Utils\PriceComputer;
|
||||
use KupShop\ReservationBundle\Entity\Reservation;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
use Symfony\Contracts\Service\Attribute\Required;
|
||||
|
||||
class ReservationCreatedEvent extends AbstractServerSideGTMEvent
|
||||
{
|
||||
public PriceComputer $priceComputer;
|
||||
|
||||
public function getPayload(Event $event): array
|
||||
{
|
||||
/** @var Reservation $reservation */
|
||||
$reservation = $event->reservation;
|
||||
$dbcfg = \Settings::getDefault();
|
||||
|
||||
$items = [
|
||||
$this->productLoader->loadProductObject($reservation->getProduct())
|
||||
+ ['index' => 1, 'quantity' => 1],
|
||||
];
|
||||
|
||||
$commonData = $this->getCommonData($event);
|
||||
|
||||
$allDeliveryTypes = \DeliveryType::getAll(true);
|
||||
|
||||
$deliveryType = $allDeliveryTypes[$dbcfg->reservations['delivery_type']];
|
||||
|
||||
$params = $commonData + [
|
||||
'currency' => $this->currencyContext->getActiveId(),
|
||||
'transaction_id' => 'RES'.$reservation->id,
|
||||
'value' => $this->priceComputer->getPrice($reservation->getPrice()),
|
||||
'vat' => $this->priceComputer->getPrice($reservation->getPrice()->getVatPrice(), null, false),
|
||||
'delivery_type' => $deliveryType ? $deliveryType->getDelivery()->getName() : '',
|
||||
'payment_type' => $deliveryType ? $deliveryType->getPayment()->getName() : '',
|
||||
'items' => $items,
|
||||
'shipping' => \DecimalConstants::zero(),
|
||||
];
|
||||
|
||||
return [
|
||||
'user_id' => $commonData['user_id'],
|
||||
'client_id' => $commonData['client_id'],
|
||||
'ga3_session_id' => $this->getGA3SessionId(),
|
||||
'events' => [
|
||||
[
|
||||
'name' => 'purchase',
|
||||
'params' => $params,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected function getCommonData(Event $event): array
|
||||
{
|
||||
$dbcfg = \Settings::getDefault();
|
||||
/** @var Reservation $Reservation */
|
||||
$Reservation = $event->reservation;
|
||||
|
||||
$request = $this->requestStack->getCurrentRequest();
|
||||
|
||||
return array_merge(parent::getCommonData($event), [
|
||||
'ip_override' => $request->getClientIp(),
|
||||
'page_path' => createScriptURL(['s' => 'product', 'IDproduct' => $Reservation->getProduct()->id, 'TITLE' => $Reservation->getProduct()->title]),
|
||||
'page_title' => $Reservation->getProduct()->title,
|
||||
'page_referrer' => $request->getSession()->get('referer'),
|
||||
'user_agent' => $request->server->get('HTTP_USER_AGENT'),
|
||||
'shaEmail' => hash('sha256', $Reservation->email.($dbcfg->analytics['google_tag_manager']['sha_salt'] ?? false ?: '')),
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getUserData(Event $event): array
|
||||
{
|
||||
return [
|
||||
'email_address' => $event->reservation['email'] ?? '',
|
||||
'phone_number' => $event->reservation['phone'] ?? '',
|
||||
'address' => [
|
||||
'first_name' => $event->reservation['name'] ?? '',
|
||||
'last_name' => $event->reservation['surname'] ?? '',
|
||||
'street' => null,
|
||||
'city' => null,
|
||||
'region' => null,
|
||||
'postal_code' => null,
|
||||
'country' => null,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[Required]
|
||||
public function setPriceComputer(PriceComputer $priceComputer): void
|
||||
{
|
||||
$this->priceComputer = $priceComputer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KupShop\GTMBundle\ServerSideGTMEvent;
|
||||
|
||||
use KupShop\GTMBundle\Utils\DataLoaders\PurchaseState;
|
||||
use KupShop\UserBundle\Event\UserRegisteredEvent;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
|
||||
class UserSignUpEvent extends AbstractServerSideGTMEvent
|
||||
{
|
||||
protected function getPayload(Event $event): array
|
||||
{
|
||||
if (!($event instanceof UserRegisteredEvent)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->getCommonData($event) +
|
||||
[
|
||||
'events' => [
|
||||
'name' => 'sign_up',
|
||||
'params' => [
|
||||
'method' => $event->getMethod(),
|
||||
'email' => $event->getUser()->email,
|
||||
'shaEmail' => PurchaseState::shaEmail($event->getUser()->email),
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KupShop\GTMBundle\ServerSideGTMEvent;
|
||||
|
||||
use KupShop\GTMBundle\Utils\DataLoaders\PurchaseState;
|
||||
use KupShop\UserBundle\Event\UserNewsletterEvent;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
|
||||
class UserSubscribeEvent extends AbstractServerSideGTMEvent
|
||||
{
|
||||
protected function getPayload(Event $event): array
|
||||
{
|
||||
if (!($event instanceof UserNewsletterEvent)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->getCommonData($event) +
|
||||
[
|
||||
'events' => [
|
||||
'name' => $this->getEventName(),
|
||||
'params' => [
|
||||
'email' => $event->getEmail(),
|
||||
'shaEmail' => PurchaseState::shaEmail($event->getEmail()),
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function getEventName(): string
|
||||
{
|
||||
return 'success_newsletter_subscribe';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KupShop\GTMBundle\ServerSideGTMEvent;
|
||||
|
||||
class UserUnsubscribeEvent extends UserSubscribeEvent
|
||||
{
|
||||
public function getEventName(): string
|
||||
{
|
||||
return 'success_newsletter_unsubscribe';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user