Skip to content

Commit

Permalink
Added sending product metadata to brevo
Browse files Browse the repository at this point in the history
  • Loading branch information
Franck Allimant committed Nov 17, 2023
1 parent 275b971 commit 7458bd9
Show file tree
Hide file tree
Showing 10 changed files with 275 additions and 110 deletions.
119 changes: 46 additions & 73 deletions Api/BrevoClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@
use Brevo\Client\Model\RemoveContactFromList;
use Brevo\Client\Model\UpdateContact;
use Brevo\Model\BrevoNewsletterQuery;
use Brevo\Trait\DataExtractorTrait;
use GuzzleHttp\Client;
use Propel\Runtime\Connection\ConnectionWrapper;
use Propel\Runtime\Propel;
use Thelia\Core\Event\Newsletter\NewsletterEvent;
use Thelia\Exception\TheliaProcessException;
use Thelia\Log\Tlog;
use Thelia\Model\ConfigQuery;
use Thelia\Model\Customer;
use Thelia\Model\CustomerQuery;
use Thelia\Model\NewsletterQuery;

/**
* Class BrevoClient.
Expand All @@ -43,6 +43,8 @@
*/
class BrevoClient
{
use DataExtractorTrait;

protected ContactsApi $contactApi;
private mixed $newsletterId;

Expand All @@ -65,7 +67,8 @@ public function subscribe(NewsletterEvent $event)
if ($apiException->getCode() !== 404) {
throw $apiException;
}
$contact = $this->createContact($event->getId());

return $this->createContact($event->getEmail());
}

$this->update($event, $contact);
Expand All @@ -78,16 +81,21 @@ public function checkIfContactExist($email)
return $this->contactApi->getContactInfoWithHttpInfo($email);
}

public function createContact(Customer $customer)
public function createContact(string $email)
{
$contactAttribute = $this->getCustomerAttribute($customer->getId());
$contactAttribute = [];

if (null !== $customer = CustomerQuery::create()->findOneByEmail($email)) {
$contactAttribute = $this->getCustomerAttribute($customer->getId());
}

$createContact = new CreateContact();
$createContact['email'] = $customer->getEmail();
$createContact['email'] = $email;
$createContact['attributes'] = $contactAttribute;
$createContact['listIds'] = [$this->newsletterId];
$this->contactApi->createContactWithHttpInfo($createContact);

return $this->contactApi->getContactInfoWithHttpInfo($customer->getEmail());
return $this->contactApi->getContactInfoWithHttpInfo($email);
}

public function updateContact($identifier, Customer $customer)
Expand All @@ -110,13 +118,16 @@ public function update(NewsletterEvent $event, $contact = null)
if (!$contact) {
$sibObject = BrevoNewsletterQuery::create()->findPk($event->getId());
if (null === $sibObject) {
$sibObject = BrevoNewsletterQuery::create()->findOneByEmail($previousEmail);
$sibObject = NewsletterQuery::create()->findPk($event->getId());
}
$previousEmail = $sibObject->getEmail();
$contact = $this->contactApi->getContactInfoWithHttpInfo($previousEmail);

$updateContact['email'] = $event->getEmail();
$updateContact['attributes'] = ['PRENOM' => $event->getFirstname(), 'NOM' => $event->getLastname()];
if (null !== $sibObject) {
$previousEmail = $sibObject->getEmail();
$contact = $this->contactApi->getContactInfoWithHttpInfo($previousEmail);

$updateContact['email'] = $event->getEmail();
$updateContact['attributes'] = ['PRENOM' => $event->getFirstname(), 'NOM' => $event->getLastname()];
}
}

$updateContact['listIds'] = [$this->newsletterId];
Expand All @@ -125,80 +136,42 @@ public function update(NewsletterEvent $event, $contact = null)
return $this->contactApi->getContactInfoWithHttpInfo($previousEmail);
}

public function unsubscribe(NewsletterEvent $event)
public function unsubscribe(string $email)
{
$contact = $this->contactApi->getContactInfoWithHttpInfo($event->getEmail());
$contact = $this->contactApi->getContactInfoWithHttpInfo($email);
$change = false;

if (\in_array($this->newsletterId, $contact[0]['listIds'], true)) {
$contactIdentifier = new RemoveContactFromList();
$contactIdentifier['emails'] = [$event->getEmail()];
$contactIdentifier['emails'] = [$email];
$this->contactApi->removeContactFromList($this->newsletterId, $contactIdentifier);
$change = true;
}

return $change ? $this->contactApi->getContactInfoWithHttpInfo($event->getEmail()) : $contact;
return $change ? $this->contactApi->getContactInfoWithHttpInfo($email) : $contact;
}

public function getCustomerAttribute($customerId)
/**
* @throws \JsonException
*/
public function getCustomerAttribute($customerId): array
{
try {
if (null === $mapping = json_decode(ConfigQuery::read(Brevo::BREVO_ATTRIBUTES_MAPPING), true, 512, \JSON_THROW_ON_ERROR)) {
throw new TheliaProcessException("Customer attribute mapping error: JSON data seems invalid, pleas echeck syntax.");
}

if (empty($mapping)) {
return [];
}
$mappingString = ConfigQuery::read(Brevo::BREVO_ATTRIBUTES_MAPPING);

if (!\array_key_exists('customer_query', $mapping)) {
throw new TheliaProcessException("Customer attribute mapping error : 'customer_query' element is missing in JSON data");
}

$attributes = [];

/** @var ConnectionWrapper $con */
$con = Propel::getConnection();

foreach ($mapping['customer_query'] as $key => $customerDataQuery) {
if (!\array_key_exists('select', $customerDataQuery)) {
throw new \Exception("Customer attribute mapping error : 'select' element missing in ".$key.' query');
}

try {
$sql = 'SELECT '.$customerDataQuery['select'].' AS '.$key.' FROM customer';

if (\array_key_exists('join', $customerDataQuery)) {
foreach ($customerDataQuery['join'] as $join) {
$sql .= ' LEFT JOIN '.$join;
}
}

$sql .= ' WHERE customer.id = :customerId';

if (\array_key_exists('groupBy', $customerDataQuery)) {
$sql .= ' GROUP BY '.$customerDataQuery['groupBy'];
}

$stmt = $con->prepare($sql);
$stmt->bindValue(':customerId', $customerId, \PDO::PARAM_INT);
$stmt->execute();

while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
$attributes[$key] = $row[$key];
if (\array_key_exists($key, $mapping) && \array_key_exists($row[$key], $mapping[$key])) {
$attributes[$key] = $mapping[$key][$row[$key]];
}
}
} catch (\Exception $ex) {
Tlog::getInstance()->error(
'Failed to execute SQL request to map Brevo attribute. Error is '.$ex->getMessage().", request is : $sql");
}
}
if (empty($mappingString)) {
return [];
}

return $attributes;
} catch (\Exception $ex) {
throw new TheliaProcessException('Customer attribute mapping error : configuration is missing or invalid, please go to the module configuration and define the JSON mapping to match thelia attribute with brevo attribute');
if (null === $mapping = json_decode($mappingString, true)) {
throw new TheliaProcessException('Customer attribute mapping error: JSON data seems invalid, pleas echeck syntax.');
}

return $this->getMappedValues(
$mapping,
'customer_query',
'customer',
'customer.id',
$customerId,
);
}
}
1 change: 1 addition & 0 deletions Brevo.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class Brevo extends BaseModule
const CONFIG_AUTOMATION_KEY= "brevo.automation.key";
const CONFIG_THROW_EXCEPTION_ON_ERROR = "brevo.throw_exception_on_error";
const BREVO_ATTRIBUTES_MAPPING = "brevo.brevo_attributes_mapping";
const BREVO_METADATA_MAPPING = "brevo.brevo_metadata_mapping";

public function postActivation(ConnectionInterface $con = null): void
{
Expand Down
2 changes: 1 addition & 1 deletion Config/module.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<language>en_US</language>
<language>fr_FR</language>
</languages>
<version>1.1.10</version>
<version>1.1.11</version>
<authors>
<author>
<name>Chabreuil Antoine</name>
Expand Down
1 change: 1 addition & 0 deletions Controller/BrevoConfigController.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public function saveAction(Request $request, ParserContext $parserContext, Brevo
ConfigQuery::write(Brevo::CONFIG_NEWSLETTER_ID, $data['newsletter_list']);
ConfigQuery::write(Brevo::CONFIG_THROW_EXCEPTION_ON_ERROR, (bool) $data['exception_on_errors']);
ConfigQuery::write(Brevo::BREVO_ATTRIBUTES_MAPPING, $data['attributes_mapping']);
ConfigQuery::write(Brevo::BREVO_METADATA_MAPPING, $data['metadata_mapping']);

$brevoApiService->enableEcommerce();

Expand Down
15 changes: 4 additions & 11 deletions EventListeners/NewsletterListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public function subscribe(NewsletterEvent $event)

public function update(NewsletterEvent $event)
{
if (null === BrevoNewsletterQuery::create()->findPk($event->getId()) || null !== NewsletterQuery::create()->findPk($event->getId())) {
if (null === BrevoNewsletterQuery::create()->findPk($event->getId()) && null === NewsletterQuery::create()->findPk($event->getId())) {
return;
}

Expand Down Expand Up @@ -107,21 +107,14 @@ public function update(NewsletterEvent $event)

public function unsubscribe(NewsletterEvent $event)
{
if ((null === $model = BrevoNewsletterQuery::create()->findPk($event->getId())) || null !== NewsletterQuery::create()->findPk($event->getId())) {
return;
}

try {
$contact = $this->api->unsubscribe($event);
$status = $contact[1];
if (null === $model) {
$model = BrevoNewsletterQuery::create()->findOneByEmail($event->getEmail());
}
$contact = $this->api->unsubscribe($event->getEmail());

if (null === $model) {
if (null === $model = BrevoNewsletterQuery::create()->findOneByEmail($event->getEmail())) {
return;
}

$status = $contact[1];
$data = ["id" => $model->getRelationId()];
$logMessage = $this->logAfterAction(
sprintf("The email address '%s' was successfully unsubscribed from the list", $event->getEmail()),
Expand Down
54 changes: 45 additions & 9 deletions Form/BrevoConfigurationForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,19 @@ protected function buildForm(): void
{
$translator = Translator::getInstance();

$defaultMapping = <<< END
$defaultCustomerMapping = <<< END
{
"customer_query": {
"EMAIL" : {
"select" : "customer.email"
}
"EMAIL" : {
"select" : "customer.email"
}
}
}
END;
$defaultMetadataMapping = <<< END
{
"product_query": {
}
}
END;
$this->formBuilder
Expand Down Expand Up @@ -131,9 +137,29 @@ protected function buildForm(): void
'required' => false,
'constraints' => [
new NotBlank(),
new Callback([$this, 'checkJsonValidity']),
new Callback([$this, 'checkCustomerJsonValidity']),
],
'data' => ConfigQuery::read(Brevo::BREVO_ATTRIBUTES_MAPPING, $defaultMapping),
'data' => ConfigQuery::read(Brevo::BREVO_ATTRIBUTES_MAPPING, $defaultCustomerMapping),
])
->add('metadata_mapping', TextareaType::class, [
'label' => $translator->trans('Products metadata attributes mapping', [], Brevo::MESSAGE_DOMAIN),
'attr' => [
'rows' => 10
],
'label_attr' => [
'for' => 'attributes_mapping',
'help' => Translator::getInstance()->trans(
'This is a mapping of Brevo products meta-data attributes with Thelia products attributes. Do not change anything here if you do not know exactly what you are doing',
[],
Brevo::MESSAGE_DOMAIN
)
],
'required' => false,
'constraints' => [
new NotBlank(),
new Callback([$this, 'checkProductJsonValidity']),
],
'data' => ConfigQuery::read(Brevo::BREVO_METADATA_MAPPING, $defaultMetadataMapping),
])
->add('exception_on_errors', CheckboxType::class, [
'label' => $translator->trans('Throw exception on Brevo error', [], Brevo::MESSAGE_DOMAIN),
Expand All @@ -149,7 +175,17 @@ protected function buildForm(): void
])
;
}
public function checkJsonValidity($value, ExecutionContextInterface $context): void

public function checkCustomerJsonValidity($value, ExecutionContextInterface $context): void
{
$this->checkJsonValidity('customer_query', $value, $context);
}
public function checkProductJsonValidity($value, ExecutionContextInterface $context): void
{
$this->checkJsonValidity('product_query', $value, $context);
}

public function checkJsonValidity(string $expectedNode, $value, ExecutionContextInterface $context): void
{
if (empty($value)) {
return;
Expand All @@ -165,10 +201,10 @@ public function checkJsonValidity($value, ExecutionContextInterface $context): v
);
}

if (! isset($jsonData['customer_query'])) {
if (! isset($jsonData[$expectedNode])) {
$context->addViolation(
Translator::getInstance()->trans(
"The customer attributes mapping JSON should contain a 'customer_query' field.",
"The customer attributes mapping JSON should contain a '$expectedNode' field.",
[],
Brevo::MESSAGE_DOMAIN
)
Expand Down
6 changes: 1 addition & 5 deletions Services/BrevoCustomerService.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@ public function __construct(private BrevoClient $brevoClient)

public function createUpdateContact($customerId)
{
if (empty(ConfigQuery::read(Brevo::BREVO_ATTRIBUTES_MAPPING, ''))) {
return null;
}

$customer = CustomerQuery::create()->findPk($customerId);

try {
Expand All @@ -40,7 +36,7 @@ public function createUpdateContact($customerId)
throw $exception;
}

return $this->brevoClient->createContact($customer);
return $this->brevoClient->createContact($customer->getEmail());
}
}
}
Loading

0 comments on commit 7458bd9

Please sign in to comment.