Skip to content

Commit 8c86857

Browse files
committed
Add cart data to conversion
1 parent 1a82f9b commit 8c86857

File tree

19 files changed

+377
-1
lines changed

19 files changed

+377
-1
lines changed

src/DependencyInjection/Configuration.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@
66

77
use Setono\SyliusGoogleAdsPlugin\Form\Type\ConnectionMappingType;
88
use Setono\SyliusGoogleAdsPlugin\Form\Type\ConnectionType;
9+
use Setono\SyliusGoogleAdsPlugin\Form\Type\MerchantMappingType;
910
use Setono\SyliusGoogleAdsPlugin\Model\Connection;
1011
use Setono\SyliusGoogleAdsPlugin\Model\ConnectionMapping;
1112
use Setono\SyliusGoogleAdsPlugin\Model\Conversion;
13+
use Setono\SyliusGoogleAdsPlugin\Model\MerchantMapping;
1214
use Setono\SyliusGoogleAdsPlugin\Repository\ConnectionMappingRepository;
1315
use Setono\SyliusGoogleAdsPlugin\Repository\ConnectionRepository;
1416
use Setono\SyliusGoogleAdsPlugin\Repository\ConversionRepository;
17+
use Setono\SyliusGoogleAdsPlugin\Repository\MerchantMappingRepository;
1518
use Sylius\Bundle\ResourceBundle\Controller\ResourceController;
1619
use Sylius\Bundle\ResourceBundle\Form\Type\DefaultResourceType;
1720
use Sylius\Component\Resource\Factory\Factory;
@@ -103,6 +106,22 @@ private function addResourcesSection(ArrayNodeDefinition $node): void
103106
->scalarNode('repository')->defaultValue(ConversionRepository::class)->cannotBeEmpty()->end()
104107
->scalarNode('factory')->defaultValue(Factory::class)->end()
105108
->scalarNode('form')->defaultValue(DefaultResourceType::class)->cannotBeEmpty()->end()
109+
->end()
110+
->end()
111+
->end()
112+
->end()
113+
->arrayNode('merchant_mapping')
114+
->addDefaultsIfNotSet()
115+
->children()
116+
->variableNode('options')->end()
117+
->arrayNode('classes')
118+
->addDefaultsIfNotSet()
119+
->children()
120+
->scalarNode('model')->defaultValue(MerchantMapping::class)->cannotBeEmpty()->end()
121+
->scalarNode('controller')->defaultValue(ResourceController::class)->cannotBeEmpty()->end()
122+
->scalarNode('repository')->defaultValue(MerchantMappingRepository::class)->cannotBeEmpty()->end()
123+
->scalarNode('factory')->defaultValue(Factory::class)->end()
124+
->scalarNode('form')->defaultValue(MerchantMappingType::class)->cannotBeEmpty()->end()
106125
;
107126
}
108127
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Setono\SyliusGoogleAdsPlugin\EventSubscriber\ConversionProcessing;
6+
7+
use Setono\SyliusGoogleAdsPlugin\Event\PreSetClickConversionDataEvent;
8+
use Setono\SyliusGoogleAdsPlugin\Repository\MerchantMappingRepositoryInterface;
9+
use Sylius\Component\Core\Model\OrderInterface;
10+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
11+
12+
final class AddCartDataSubscriber implements EventSubscriberInterface
13+
{
14+
public function __construct(private readonly MerchantMappingRepositoryInterface $merchantMappingRepository)
15+
{
16+
}
17+
18+
public static function getSubscribedEvents(): array
19+
{
20+
return [
21+
PreSetClickConversionDataEvent::class => 'add',
22+
];
23+
}
24+
25+
public function add(PreSetClickConversionDataEvent $event): void
26+
{
27+
$order = $event->conversion->getOrder();
28+
if (null === $order) {
29+
return;
30+
}
31+
32+
$channel = $order->getChannel();
33+
if (null === $channel) {
34+
return;
35+
}
36+
37+
$localeCode = $order->getLocaleCode();
38+
if (null === $localeCode) {
39+
return;
40+
}
41+
42+
$merchantId = $this->merchantMappingRepository->findOneByChannel($channel)?->getMerchantId();
43+
if (null === $merchantId) {
44+
return;
45+
}
46+
47+
$countryCode = $order->getBillingAddress()?->getCountryCode();
48+
if (null === $countryCode) {
49+
return;
50+
}
51+
52+
$items = self::getItems($order);
53+
if ([] === $items) {
54+
return;
55+
}
56+
57+
$event->data['cart_data'] = [
58+
'merchant_id' => $merchantId,
59+
'feed_country_code' => $countryCode,
60+
'feed_language_code' => $localeCode,
61+
'local_transaction_cost' => 0, // TODO: Sum of all transaction level discounts, such as free shipping and coupon discounts for the whole cart. The currency code is the same as that in the ClickConversion message.
62+
'items' => $items,
63+
];
64+
}
65+
66+
private static function getItems(OrderInterface $order): array
67+
{
68+
$items = [];
69+
70+
foreach ($order->getItems() as $item) {
71+
$productId = $item->getVariant()?->getCode();
72+
if (null === $productId) {
73+
continue;
74+
}
75+
76+
$items[] = [
77+
'product_id' => $productId,
78+
'quantity' => $item->getQuantity(),
79+
'unit_price' => round($item->getUnitPrice() / 100, 2),
80+
];
81+
}
82+
83+
return $items;
84+
}
85+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Setono\SyliusGoogleAdsPlugin\Form\Type;
6+
7+
use Sylius\Bundle\ChannelBundle\Form\Type\ChannelChoiceType;
8+
use Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType;
9+
use Symfony\Component\Form\Extension\Core\Type\TextType;
10+
use Symfony\Component\Form\FormBuilderInterface;
11+
12+
final class MerchantMappingType extends AbstractResourceType
13+
{
14+
public function buildForm(FormBuilderInterface $builder, array $options): void
15+
{
16+
$builder
17+
->add('channel', ChannelChoiceType::class, [
18+
'multiple' => false,
19+
'expanded' => false,
20+
'label' => 'sylius.ui.channel',
21+
'placeholder' => '',
22+
])
23+
->add('merchantId', TextType::class, [
24+
'label' => 'setono_sylius_google_ads.form.merchant_mapping.merchant_id',
25+
])
26+
;
27+
}
28+
}

src/Model/MerchantMapping.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Setono\SyliusGoogleAdsPlugin\Model;
6+
7+
use Sylius\Component\Channel\Model\ChannelInterface;
8+
9+
class MerchantMapping implements MerchantMappingInterface
10+
{
11+
protected ?int $id = null;
12+
13+
protected ?ChannelInterface $channel = null;
14+
15+
protected ?string $merchantId = null;
16+
17+
public function getId(): ?int
18+
{
19+
return $this->id;
20+
}
21+
22+
public function getChannel(): ?ChannelInterface
23+
{
24+
return $this->channel;
25+
}
26+
27+
public function setChannel(?ChannelInterface $channel): void
28+
{
29+
$this->channel = $channel;
30+
}
31+
32+
public function getMerchantId(): ?string
33+
{
34+
return $this->merchantId;
35+
}
36+
37+
public function setMerchantId(?string $merchantId): void
38+
{
39+
$this->merchantId = $merchantId;
40+
}
41+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Setono\SyliusGoogleAdsPlugin\Model;
6+
7+
use Sylius\Component\Channel\Model\ChannelAwareInterface;
8+
use Sylius\Component\Resource\Model\ResourceInterface;
9+
10+
interface MerchantMappingInterface extends ResourceInterface, ChannelAwareInterface
11+
{
12+
public function getId(): ?int;
13+
14+
public function getMerchantId(): ?string;
15+
16+
public function setMerchantId(?string $merchantId): void;
17+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Setono\SyliusGoogleAdsPlugin\Repository;
6+
7+
use Setono\SyliusGoogleAdsPlugin\Model\MerchantMappingInterface;
8+
use Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository;
9+
use Sylius\Component\Channel\Model\ChannelInterface;
10+
use Webmozart\Assert\Assert;
11+
12+
class MerchantMappingRepository extends EntityRepository implements MerchantMappingRepositoryInterface
13+
{
14+
public function findOneByChannel(ChannelInterface $channel): ?MerchantMappingInterface
15+
{
16+
$obj = $this->findOneBy(['channel' => $channel]);
17+
18+
Assert::nullOrIsInstanceOf($obj, MerchantMappingInterface::class);
19+
20+
return $obj;
21+
}
22+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Setono\SyliusGoogleAdsPlugin\Repository;
6+
7+
use Setono\SyliusGoogleAdsPlugin\Model\MerchantMappingInterface;
8+
use Sylius\Component\Channel\Model\ChannelInterface;
9+
use Sylius\Component\Resource\Repository\RepositoryInterface;
10+
11+
/**
12+
* @extends RepositoryInterface<MerchantMappingInterface>
13+
*/
14+
interface MerchantMappingRepositoryInterface extends RepositoryInterface
15+
{
16+
public function findOneByChannel(ChannelInterface $channel): ?MerchantMappingInterface;
17+
}

src/Resources/config/app/config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
imports:
22
- "@SetonoSyliusGoogleAdsPlugin/Resources/config/grids/setono_sylius_google_ads_admin_connection.yaml"
33
- "@SetonoSyliusGoogleAdsPlugin/Resources/config/grids/setono_sylius_google_ads_admin_conversion.yaml"
4+
- "@SetonoSyliusGoogleAdsPlugin/Resources/config/grids/setono_sylius_google_ads_admin_merchant_mapping.yaml"
45

56
framework:
67
messenger:
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<doctrine-mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
5+
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
6+
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
7+
<mapped-superclass name="Setono\SyliusGoogleAdsPlugin\Model\MerchantMapping"
8+
table="setono_sylius_google_ads__merchant_mapping">
9+
<id name="id" type="integer">
10+
<generator strategy="AUTO"/>
11+
</id>
12+
13+
<field name="merchantId" column="merchant_id" type="bigint"/>
14+
15+
<one-to-one field="channel" target-entity="Sylius\Component\Channel\Model\ChannelInterface">
16+
<join-column name="channel_id" referenced-column-name="id" nullable="false" on-delete="CASCADE"/>
17+
</one-to-one>
18+
</mapped-superclass>
19+
</doctrine-mapping>

src/Resources/config/grids/setono_sylius_google_ads_admin_conversion.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
sylius_grid:
22
templates:
33
action:
4+
map_merchants: "@SetonoSyliusGoogleAdsPlugin/grid/action/map_merchants.html.twig"
45
retry_conversion: "@SetonoSyliusGoogleAdsPlugin/grid/action/retry_conversion.html.twig"
56
setup_connections: "@SetonoSyliusGoogleAdsPlugin/grid/action/setup_connections.html.twig"
67
grids:
@@ -61,6 +62,9 @@ sylius_grid:
6162
template: "@SetonoSyliusGoogleAdsPlugin/conversion/grid/field/log_messages.html.twig"
6263
actions:
6364
main:
65+
map_merchants:
66+
type: map_merchants
67+
label: setono_sylius_google_ads.ui.map_merchants
6468
setup_connections:
6569
type: setup_connections
6670
label: setono_sylius_google_ads.ui.setup_connections

0 commit comments

Comments
 (0)