diff --git a/README.md b/README.md
index ce6d1eb..d3a792f 100644
--- a/README.md
+++ b/README.md
@@ -30,6 +30,7 @@ in `config/bundles.php` file of your project before (!) `SyliusGridBundle`:
$bundles = [
Setono\ClientIdBundle\SetonoClientIdBundle::class => ['all' => true],
Setono\ConsentBundle\SetonoConsentBundle::class => ['all' => true],
+ Setono\BotDetectionBundle\SetonoBotDetectionBundle::class => ['all' => true],
Setono\SyliusFacebookPlugin\SetonoSyliusFacebookPlugin::class => ['all' => true],
Sylius\Bundle\GridBundle\SyliusGridBundle::class => ['all' => true],
];
diff --git a/UPGRADE.md b/UPGRADE.md
index cd63d5e..b103c80 100644
--- a/UPGRADE.md
+++ b/UPGRADE.md
@@ -3,7 +3,7 @@
1. As we're moving to server side tracking - we no longer need `SetonoTagBagBundle`
and `SetonoSyliusTagBagPlugin`.
-- Remove them from `config/bundles.php`:
+- Remove them from `config/bundles.php` and add ones we're using:
```diff
$bundles = [
@@ -11,6 +11,7 @@
- Setono\SyliusTagBagPlugin\SetonoSyliusTagBagPlugin::class => ['all' => true],
+ Setono\ClientIdBundle\SetonoClientIdBundle::class => ['all' => true],
+ Setono\ConsentBundle\SetonoConsentBundle::class => ['all' => true],
+ + Setono\BotDetectionBundle\SetonoBotDetectionBundle::class => ['all' => true],
Setono\SyliusFacebookPlugin\SetonoSyliusFacebookPlugin::class => ['all' => true],
Sylius\Bundle\GridBundle\SyliusGridBundle::class => ['all' => true],
diff --git a/composer.json b/composer.json
index f6e9ff9..36ffba4 100644
--- a/composer.json
+++ b/composer.json
@@ -19,6 +19,7 @@
"fzaninotto/faker": "^1.6",
"knplabs/knp-menu": "^3.0",
"psr/log": "^1.1",
+ "setono/bot-detection-bundle": "^1.1",
"setono/client-id-bundle": "^0.2",
"setono/client-id-contracts": "^0.2",
"setono/consent-bundle": "^0.1",
diff --git a/src/EventListener/AbstractSubscriber.php b/src/EventListener/AbstractSubscriber.php
index 1de29f2..1f35d98 100644
--- a/src/EventListener/AbstractSubscriber.php
+++ b/src/EventListener/AbstractSubscriber.php
@@ -4,11 +4,9 @@
namespace Setono\SyliusFacebookPlugin\EventListener;
-use Doctrine\ORM\EntityManagerInterface;
+use Setono\BotDetectionBundle\BotDetector\BotDetectorInterface;
use Setono\SyliusFacebookPlugin\Context\PixelContextInterface;
-use Setono\SyliusFacebookPlugin\DataMapper\DataMapperInterface;
-use Setono\SyliusFacebookPlugin\Factory\PixelEventFactoryInterface;
-use Setono\SyliusFacebookPlugin\ServerSide\ServerSideEventFactoryInterface;
+use Setono\SyliusFacebookPlugin\Generator\PixelEventsGeneratorInterface;
use Symfony\Bundle\SecurityBundle\Security\FirewallMap;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
@@ -16,41 +14,28 @@
abstract class AbstractSubscriber implements EventSubscriberInterface
{
- protected PixelContextInterface $pixelContext;
-
protected RequestStack $requestStack;
protected FirewallMap $firewallMap;
- protected ServerSideEventFactoryInterface $serverSideFactory;
-
- protected DataMapperInterface $dataMapper;
+ protected PixelContextInterface $pixelContext;
- protected PixelEventFactoryInterface $pixelEventFactory;
+ protected PixelEventsGeneratorInterface $pixelEventsGenerator;
- protected EntityManagerInterface $entityManager;
+ protected BotDetectorInterface $botDetector;
public function __construct(
- PixelContextInterface $pixelContext,
RequestStack $requestStack,
FirewallMap $firewallMap,
- ServerSideEventFactoryInterface $serverSideFactory,
- DataMapperInterface $dataMapper,
- PixelEventFactoryInterface $pixelEventFactory,
- EntityManagerInterface $entityManager
+ PixelContextInterface $pixelContext,
+ PixelEventsGeneratorInterface $pixelEventsGenerator,
+ BotDetectorInterface $botDetector
) {
- $this->pixelContext = $pixelContext;
$this->requestStack = $requestStack;
$this->firewallMap = $firewallMap;
- $this->serverSideFactory = $serverSideFactory;
- $this->dataMapper = $dataMapper;
- $this->pixelEventFactory = $pixelEventFactory;
- $this->entityManager = $entityManager;
- }
-
- protected function getMasterRequest(): ?Request
- {
- return $this->requestStack->getMasterRequest();
+ $this->pixelContext = $pixelContext;
+ $this->pixelEventsGenerator = $pixelEventsGenerator;
+ $this->botDetector = $botDetector;
}
protected function isShopContext(Request $request = null): bool
@@ -70,26 +55,24 @@ protected function isShopContext(Request $request = null): bool
return $firewallConfig->getName() === 'shop';
}
- /**
- * @param object $source
- */
- protected function generatePixelEvents($source, string $eventName, Request $request = null): void
+ protected function isRequestEligible(): bool
{
- $serverSideEvent = $this->serverSideFactory->create($eventName);
- $this->dataMapper->map($source, $serverSideEvent, [
- 'request' => $request ?? $this->getMasterRequest(),
- 'event' => $eventName,
- ]);
-
- $pixels = $this->pixelContext->getPixels();
- foreach ($pixels as $pixel) {
- // @todo Maybe its better to just clone
- $pixelEvent = $this->pixelEventFactory->createFromServerSideEvent($serverSideEvent);
- $pixelEvent->setPixel($pixel);
-
- $this->entityManager->persist($pixelEvent);
+ // As one main request can have multiple subrequests
+ // we don't want things to be tracked multiple times
+ // So, having in mind that `If current Request is the master request, it returns null`
+ // we expect getParentRequest to be null to proceed
+ if (null !== $this->requestStack->getParentRequest()) {
+ return false;
+ }
+
+ if (!$this->isShopContext()) {
+ return false;
+ }
+
+ if ($this->botDetector->isBotRequest()) {
+ return false;
}
- $this->entityManager->flush();
+ return true;
}
}
diff --git a/src/EventListener/AddToCartSubscriber.php b/src/EventListener/AddToCartSubscriber.php
index dfa858b..cba47a0 100644
--- a/src/EventListener/AddToCartSubscriber.php
+++ b/src/EventListener/AddToCartSubscriber.php
@@ -4,11 +4,9 @@
namespace Setono\SyliusFacebookPlugin\EventListener;
-use Doctrine\ORM\EntityManagerInterface;
+use Setono\BotDetectionBundle\BotDetector\BotDetectorInterface;
use Setono\SyliusFacebookPlugin\Context\PixelContextInterface;
-use Setono\SyliusFacebookPlugin\DataMapper\DataMapperInterface;
-use Setono\SyliusFacebookPlugin\Factory\PixelEventFactoryInterface;
-use Setono\SyliusFacebookPlugin\ServerSide\ServerSideEventFactoryInterface;
+use Setono\SyliusFacebookPlugin\Generator\PixelEventsGeneratorInterface;
use Setono\SyliusFacebookPlugin\ServerSide\ServerSideEventInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Order\Context\CartContextInterface;
@@ -20,23 +18,19 @@ final class AddToCartSubscriber extends AbstractSubscriber
protected CartContextInterface $cartContext;
public function __construct(
- PixelContextInterface $pixelContext,
RequestStack $requestStack,
FirewallMap $firewallMap,
- ServerSideEventFactoryInterface $serverSideFactory,
- DataMapperInterface $dataMapper,
- PixelEventFactoryInterface $pixelEventFactory,
- EntityManagerInterface $entityManager,
+ PixelContextInterface $pixelContext,
+ PixelEventsGeneratorInterface $pixelEventsGenerator,
+ BotDetectorInterface $botDetector,
CartContextInterface $cartContext
) {
parent::__construct(
- $pixelContext,
$requestStack,
$firewallMap,
- $serverSideFactory,
- $dataMapper,
- $pixelEventFactory,
- $entityManager
+ $pixelContext,
+ $pixelEventsGenerator,
+ $botDetector
);
$this->cartContext = $cartContext;
@@ -53,7 +47,7 @@ public static function getSubscribedEvents(): array
public function track(): void
{
- if (!$this->isShopContext() || !$this->pixelContext->hasPixels()) {
+ if (!$this->isRequestEligible() || !$this->pixelContext->hasPixels()) {
return;
}
@@ -62,7 +56,7 @@ public function track(): void
return;
}
- $this->generatePixelEvents(
+ $this->pixelEventsGenerator->generatePixelEvents(
$order,
ServerSideEventInterface::EVENT_ADD_TO_CART
);
diff --git a/src/EventListener/InitiateCheckoutSubscriber.php b/src/EventListener/InitiateCheckoutSubscriber.php
index 0611caf..61058a0 100644
--- a/src/EventListener/InitiateCheckoutSubscriber.php
+++ b/src/EventListener/InitiateCheckoutSubscriber.php
@@ -4,11 +4,9 @@
namespace Setono\SyliusFacebookPlugin\EventListener;
-use Doctrine\ORM\EntityManagerInterface;
+use Setono\BotDetectionBundle\BotDetector\BotDetectorInterface;
use Setono\SyliusFacebookPlugin\Context\PixelContextInterface;
-use Setono\SyliusFacebookPlugin\DataMapper\DataMapperInterface;
-use Setono\SyliusFacebookPlugin\Factory\PixelEventFactoryInterface;
-use Setono\SyliusFacebookPlugin\ServerSide\ServerSideEventFactoryInterface;
+use Setono\SyliusFacebookPlugin\Generator\PixelEventsGeneratorInterface;
use Setono\SyliusFacebookPlugin\ServerSide\ServerSideEventInterface;
use Sylius\Component\Order\Context\CartContextInterface;
use Symfony\Bundle\SecurityBundle\Security\FirewallMap;
@@ -21,23 +19,19 @@ final class InitiateCheckoutSubscriber extends AbstractSubscriber
protected CartContextInterface $cartContext;
public function __construct(
- PixelContextInterface $pixelContext,
RequestStack $requestStack,
FirewallMap $firewallMap,
- ServerSideEventFactoryInterface $serverSideFactory,
- DataMapperInterface $dataMapper,
- PixelEventFactoryInterface $pixelEventFactory,
- EntityManagerInterface $entityManager,
+ PixelContextInterface $pixelContext,
+ PixelEventsGeneratorInterface $pixelEventsGenerator,
+ BotDetectorInterface $botDetector,
CartContextInterface $cartContext
) {
parent::__construct(
- $pixelContext,
$requestStack,
$firewallMap,
- $serverSideFactory,
- $dataMapper,
- $pixelEventFactory,
- $entityManager
+ $pixelContext,
+ $pixelEventsGenerator,
+ $botDetector
);
$this->cartContext = $cartContext;
@@ -52,18 +46,7 @@ public static function getSubscribedEvents(): array
public function track(RequestEvent $requestEvent): void
{
- $request = $requestEvent->getRequest();
-
- if (!$requestEvent->isMasterRequest()) {
- return;
- }
-
- if (!$request->attributes->has('_route')) {
- return;
- }
-
- $route = $request->attributes->get('_route');
- if ('sylius_shop_checkout_start' !== $route) {
+ if (!$this->isRequestEligible() || !$this->pixelContext->hasPixels()) {
return;
}
@@ -72,13 +55,27 @@ public function track(RequestEvent $requestEvent): void
return;
}
- if (!$this->pixelContext->hasPixels()) {
- return;
- }
-
- $this->generatePixelEvents(
+ $this->pixelEventsGenerator->generatePixelEvents(
$cart,
ServerSideEventInterface::EVENT_INITIATE_CHECKOUT
);
}
+
+ /**
+ * Request is eligible when:
+ * - We are on the 'checkout start' page
+ */
+ protected function isRequestEligible(): bool
+ {
+ if (!parent::isRequestEligible()) {
+ return false;
+ }
+
+ $request = $this->requestStack->getCurrentRequest();
+ if (null === $request) {
+ return false;
+ }
+
+ return 'sylius_shop_checkout_start' === $request->attributes->get('_route');
+ }
}
diff --git a/src/EventListener/PurchaseSubscriber.php b/src/EventListener/PurchaseSubscriber.php
index e8c97ad..98d27cd 100644
--- a/src/EventListener/PurchaseSubscriber.php
+++ b/src/EventListener/PurchaseSubscriber.php
@@ -4,11 +4,9 @@
namespace Setono\SyliusFacebookPlugin\EventListener;
-use Doctrine\ORM\EntityManagerInterface;
+use Setono\BotDetectionBundle\BotDetector\BotDetectorInterface;
use Setono\SyliusFacebookPlugin\Context\PixelContextInterface;
-use Setono\SyliusFacebookPlugin\DataMapper\DataMapperInterface;
-use Setono\SyliusFacebookPlugin\Factory\PixelEventFactoryInterface;
-use Setono\SyliusFacebookPlugin\ServerSide\ServerSideEventFactoryInterface;
+use Setono\SyliusFacebookPlugin\Generator\PixelEventsGeneratorInterface;
use Setono\SyliusFacebookPlugin\ServerSide\ServerSideEventInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Order\Repository\OrderRepositoryInterface;
@@ -22,23 +20,19 @@ final class PurchaseSubscriber extends AbstractSubscriber
protected OrderRepositoryInterface $orderRepository;
public function __construct(
- PixelContextInterface $pixelContext,
RequestStack $requestStack,
FirewallMap $firewallMap,
- ServerSideEventFactoryInterface $serverSideFactory,
- DataMapperInterface $dataMapper,
- PixelEventFactoryInterface $pixelEventFactory,
- EntityManagerInterface $entityManager,
+ PixelContextInterface $pixelContext,
+ PixelEventsGeneratorInterface $pixelEventsGenerator,
+ BotDetectorInterface $botDetector,
OrderRepositoryInterface $orderRepository
) {
parent::__construct(
- $pixelContext,
$requestStack,
$firewallMap,
- $serverSideFactory,
- $dataMapper,
- $pixelEventFactory,
- $entityManager
+ $pixelContext,
+ $pixelEventsGenerator,
+ $botDetector
);
$this->orderRepository = $orderRepository;
@@ -53,41 +47,48 @@ public static function getSubscribedEvents(): array
public function track(RequestEvent $requestEvent): void
{
- $order = $this->resolveOrder($requestEvent);
- if (null === $order) {
+ if (!$this->isRequestEligible() || !$this->pixelContext->hasPixels()) {
return;
}
- if (!$this->pixelContext->hasPixels()) {
+ $order = $this->resolveOrder();
+ if (null === $order) {
return;
}
- $this->generatePixelEvents(
+ $this->pixelEventsGenerator->generatePixelEvents(
$order,
ServerSideEventInterface::EVENT_PURCHASE
);
}
/**
- * This method will return an OrderInterface if
+ * Request is eligible when:
* - We are on the 'thank you' page
- * - A session exists with the order id
- * - The order can be found in the order repository
*/
- private function resolveOrder(RequestEvent $requestEvent): ?OrderInterface
+ protected function isRequestEligible(): bool
{
- $request = $requestEvent->getRequest();
-
- if (!$requestEvent->isMasterRequest()) {
- return null;
+ if (!parent::isRequestEligible()) {
+ return false;
}
- if (!$request->attributes->has('_route')) {
- return null;
+ $request = $this->requestStack->getCurrentRequest();
+ if (null === $request) {
+ return false;
}
- $route = $request->attributes->get('_route');
- if ('sylius_shop_order_thank_you' !== $route) {
+ return 'sylius_shop_order_thank_you' === $request->attributes->get('_route');
+ }
+
+ /**
+ * This method will return an OrderInterface if
+ * - A session exists with the order id
+ * - The order can be found in the order repository
+ */
+ private function resolveOrder(): ?OrderInterface
+ {
+ $request = $this->requestStack->getCurrentRequest();
+ if (null === $request) {
return null;
}
diff --git a/src/EventListener/ViewCategorySubscriber.php b/src/EventListener/ViewCategorySubscriber.php
index c3333b8..4399bb8 100644
--- a/src/EventListener/ViewCategorySubscriber.php
+++ b/src/EventListener/ViewCategorySubscriber.php
@@ -4,12 +4,10 @@
namespace Setono\SyliusFacebookPlugin\EventListener;
-use Doctrine\ORM\EntityManagerInterface;
+use Setono\BotDetectionBundle\BotDetector\BotDetectorInterface;
use Setono\SyliusFacebookPlugin\Context\PixelContextInterface;
use Setono\SyliusFacebookPlugin\Data\ViewCategoryData;
-use Setono\SyliusFacebookPlugin\DataMapper\DataMapperInterface;
-use Setono\SyliusFacebookPlugin\Factory\PixelEventFactoryInterface;
-use Setono\SyliusFacebookPlugin\ServerSide\ServerSideEventFactoryInterface;
+use Setono\SyliusFacebookPlugin\Generator\PixelEventsGeneratorInterface;
use Setono\SyliusFacebookPlugin\ServerSide\ServerSideEventInterface;
use Sylius\Bundle\ResourceBundle\Event\ResourceControllerEvent;
use Sylius\Bundle\ResourceBundle\Grid\View\ResourceGridView;
@@ -32,24 +30,20 @@ final class ViewCategorySubscriber extends AbstractSubscriber
protected TaxonRepositoryInterface $taxonRepository;
public function __construct(
- PixelContextInterface $pixelContext,
RequestStack $requestStack,
FirewallMap $firewallMap,
- ServerSideEventFactoryInterface $serverSideFactory,
- DataMapperInterface $dataMapper,
- PixelEventFactoryInterface $pixelEventFactory,
- EntityManagerInterface $entityManager,
+ PixelContextInterface $pixelContext,
+ PixelEventsGeneratorInterface $pixelEventsGenerator,
+ BotDetectorInterface $botDetector,
LocaleContextInterface $localeContext,
TaxonRepositoryInterface $taxonRepository
) {
parent::__construct(
- $pixelContext,
$requestStack,
$firewallMap,
- $serverSideFactory,
- $dataMapper,
- $pixelEventFactory,
- $entityManager
+ $pixelContext,
+ $pixelEventsGenerator,
+ $botDetector
);
$this->localeContext = $localeContext;
@@ -67,7 +61,7 @@ public static function getSubscribedEvents(): array
public function trackCustom(ResourceControllerEvent $event): void
{
- if (!$this->isShopContext() || !$this->pixelContext->hasPixels()) {
+ if (!$this->isRequestEligible() || !$this->pixelContext->hasPixels()) {
return;
}
@@ -81,7 +75,7 @@ public function trackCustom(ResourceControllerEvent $event): void
$this->getTaxon($gridView),
);
- $this->generatePixelEvents(
+ $this->pixelEventsGenerator->generatePixelEvents(
$viewCategoryData,
ServerSideEventInterface::CUSTOM_EVENT_VIEW_CATEGORY
);
diff --git a/src/EventListener/ViewContentSubscriber.php b/src/EventListener/ViewContentSubscriber.php
index 0e4d369..61a6ba9 100644
--- a/src/EventListener/ViewContentSubscriber.php
+++ b/src/EventListener/ViewContentSubscriber.php
@@ -21,7 +21,7 @@ public static function getSubscribedEvents(): array
public function track(ResourceControllerEvent $event): void
{
- if (!$this->isShopContext() || !$this->pixelContext->hasPixels()) {
+ if (!$this->isRequestEligible() || !$this->pixelContext->hasPixels()) {
return;
}
@@ -30,7 +30,7 @@ public function track(ResourceControllerEvent $event): void
return;
}
- $this->generatePixelEvents(
+ $this->pixelEventsGenerator->generatePixelEvents(
$product,
ServerSideEventInterface::EVENT_VIEW_CONTENT
);
diff --git a/src/Generator/PixelEventsGenerator.php b/src/Generator/PixelEventsGenerator.php
new file mode 100644
index 0000000..e497087
--- /dev/null
+++ b/src/Generator/PixelEventsGenerator.php
@@ -0,0 +1,67 @@
+pixelContext = $pixelContext;
+ $this->requestStack = $requestStack;
+ $this->serverSideFactory = $serverSideFactory;
+ $this->dataMapper = $dataMapper;
+ $this->pixelEventFactory = $pixelEventFactory;
+ $this->entityManager = $entityManager;
+ }
+
+ /**
+ * @param object $source
+ */
+ public function generatePixelEvents($source, string $eventName, Request $request = null): void
+ {
+ $serverSideEvent = $this->serverSideFactory->create($eventName);
+ $this->dataMapper->map($source, $serverSideEvent, [
+ 'request' => $request ?? $this->requestStack->getMasterRequest(),
+ 'event' => $eventName,
+ ]);
+
+ $pixels = $this->pixelContext->getPixels();
+ foreach ($pixels as $pixel) {
+ // @todo Maybe its better to just clone
+ $pixelEvent = $this->pixelEventFactory->createFromServerSideEvent($serverSideEvent);
+ $pixelEvent->setPixel($pixel);
+
+ $this->entityManager->persist($pixelEvent);
+ }
+
+ $this->entityManager->flush();
+ }
+}
diff --git a/src/Generator/PixelEventsGeneratorInterface.php b/src/Generator/PixelEventsGeneratorInterface.php
new file mode 100644
index 0000000..867bc60
--- /dev/null
+++ b/src/Generator/PixelEventsGeneratorInterface.php
@@ -0,0 +1,15 @@
+
+
diff --git a/src/Resources/config/services/event_listener.xml b/src/Resources/config/services/event_listener.xml
index c835405..0c7292b 100644
--- a/src/Resources/config/services/event_listener.xml
+++ b/src/Resources/config/services/event_listener.xml
@@ -6,13 +6,11 @@
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/Application/config/bundles.php b/tests/Application/config/bundles.php
index aeecb07..0630a0e 100644
--- a/tests/Application/config/bundles.php
+++ b/tests/Application/config/bundles.php
@@ -54,5 +54,6 @@
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true],
Setono\ClientIdBundle\SetonoClientIdBundle::class => ['all' => true],
Setono\ConsentBundle\SetonoConsentBundle::class => ['all' => true],
+ Setono\BotDetectionBundle\SetonoBotDetectionBundle::class => ['all' => true],
FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true, 'test_cached' => true],
];