<?php declare(strict_types=1);
namespace Schoettler\SteincoProducts\Subscriber;
use Schoettler\SteincoProducts\SharedFunctions;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Shopware\Core\Checkout\Customer\Event\CustomerRegisterEvent;
use Shopware\Core\Checkout\Customer\CustomerEvents;
use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenEvent;
use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityDeletedEvent;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityPreWriteEvent;
class CustomerEventsSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
// Return the events to listen to as array like this: <event to listen to> => <method to execute>
return [
CustomerEvents::CUSTOMER_REGISTER_EVENT => 'onCustomerRegistered',
CustomerEvents::CUSTOMER_WRITTEN_EVENT => 'onCustomerWritten',
CustomerEvents::CUSTOMER_DELETED_EVENT => 'onCustomerDeleted'
/*
CustomerEvents::CUSTOMER_ADDRESS_WRITTEN_EVENT => 'onCustomerAddressWritten',
CustomerEvents::CUSTOMER_ADDRESS_DELETED_EVENT => 'onCustomerAddressDeleted',
*/
];
}
private $systemConfigService;
private $customerRepository;
private $addressRepository;
private $countryRepository;
private $stateRepository;
private $languageRepository;
public function __construct(
SystemConfigService $systemConfigService,
EntityRepository $customerRepository,
EntityRepository $addressRepository,
EntityRepository $countryRepository,
EntityRepository $stateRepository,
EntityRepository $languageRepository
)
{
$this->systemConfigService = $systemConfigService;
$this->customerRepository = $customerRepository;
$this->addressRepository = $addressRepository;
$this->countryRepository = $countryRepository;
$this->stateRepository = $stateRepository;
$this->languageRepository = $languageRepository;
}
public function onCustomerRegistered(CustomerRegisterEvent $event): void
{
$customerId = $event->getPayLoads()[0]['id'];
$this->registerInExternalDatabase($customerId, 0);
$this->adjustSalesChannel($customerId);
}
public function onCustomerWritten(EntityWrittenEvent $event): void
{
SharedFunctions::dumpToLog("onCustomerWritten()");
$payload = $event->getPayLoads()[0];
if ($payload && array_key_exists('id', $payload)) {
$customerId = $event->getPayLoads()[0]['id'];
} else {
// we need to get the id from the writeResult
$writeResult = $event->getWriteResults()[0];
$customerId = $writeResult->getPrimaryKey();
}
if ($customerId) {
$this->registerInExternalDatabase($customerId, 1);
$this->adjustSalesChannel($customerId);
}
}
public function onCustomerDeleted(EntityDeletedEvent $event): void
{
$customerId = $event->getPayLoads()[0]['id'];
$this->registerInExternalDatabase($customerId, 2);
}
public function onCustomerAddressWritten(EntityWrittenEvent $event): void
{
$writeResults = $event->getWriteResults();
$writeResult = reset($writeResults);
$customerId = $writeResult->getPayload()['customerId'];
$this->registerInExternalDatabase($customerId, 1);
}
public function onCustomerAddressDeleted(EntityDeletedEvent $event): void
{
//dump($event); die();
$this->registerInExternalDatabase(1);
}
private function registerInExternalDatabase($customerId, $created_or_changed_or_deleted)
{
// create a connection to the external database
SharedFunctions::dumpToLog("registerInExternalDatabase()");
$dbHost = $this->systemConfigService->get('SteincoProductsController.config.externalDatabaseHost');
$dbUser = $this->systemConfigService->get('SteincoProductsController.config.externalDatabaseUser');
$dbPass = $this->systemConfigService->get('SteincoProductsController.config.externalDatabasePassword');
$dbName = $this->systemConfigService->get('SteincoProductsController.config.externalDatabaseName');
$mySql = new \mysqli($dbHost, $dbUser, $dbPass, $dbName);
if ($mySql->connect_errno) {
// Log the error somehow
//dump($mysqli->connect_errno);
}
// get the user or set dummy data
$customerSearchResult = $this->customerRepository->search(
(new Criteria([]))->addAssociation('salutation')->addAssociation('title')->addFilter(new EqualsFilter('id', $customerId)),
\Shopware\Core\Framework\Context::createDefaultContext()
);
$elements = $customerSearchResult->getEntities()->getElements();
$customer = reset($elements);
// Sprache des Kunden mit Locale-Assoziation abrufen
$languageSearchResult = $this->languageRepository->search(
(new Criteria([]))
->addFilter(new EqualsFilter('id', $customer->getLanguageId()))
->addAssociation('locale'),
\Shopware\Core\Framework\Context::createDefaultContext()
);
$language = $languageSearchResult->first();
if ($language && $language->getLocale()) {
$language_name = $language->getName();
// Extrahiere den Ländercode nach dem Bindestrich
$locale_parts = explode('-', $language->getLocale()->getCode());
$language_locale = end($locale_parts); // Nimmt den letzten Teil nach dem Bindestrich
} else {
// Fallback-Werte setzen
$language_name = 'Deutsch';
$language_locale = 'DE';
}
// we need to get some data from the db:
$customer_group = null; // no customergroups yet, will be from $customer->groupId
// Salutation
$salutation = $customer->getSalutation();
// address-data from the billing address
$addressId = $customer->getDefaultBillingAddressId();
$address_searchresult = $this->addressRepository->search(
(new Criteria([]))->addAssociation('countryState')->addFilter(new EqualsFilter('id', $addressId)),
\Shopware\Core\Framework\Context::createDefaultContext());
$address_elements = $address_searchresult->getEntities()->getElements();
$address = reset($address_elements);
$department = $address->getDepartment(); // part of address
$iso_3 = null;
// search for countryId
$country_searchresult = $this->countryRepository->search(
(new Criteria([]))->addFilter(new EqualsFilter('id', $address->getCountryId())),
\Shopware\Core\Framework\Context::createDefaultContext());
$country_elements = $country_searchresult->getEntities()->getElements();
$country_element = reset($country_elements);
if ($country_element) {
$country = $country_element->getName(); //part of address
$iso_3 = $country_element->getIso3();
} else {
$country = "";
}
// search for countryStateId
if ($address->getCountryState()) {
$state = $address->getCountryState()->getName(); // bundesland, part of address
} else {
$state = "";
}
if ($address->getCountryStateId()) {
$state_searchresult = $this->stateRepository->search(
(new Criteria([]))->addFilter(new EqualsFilter('id', $address->getCountryStateId())),
\Shopware\Core\Framework\Context::createDefaultContext());
$state_elements = $state_searchresult->getEntities()->getElements();
$state_element = reset($state_elements);
$state_element->getName();
}
$street = $address->getStreet(); // part of address
$postal_code = $address->getZipcode(); // part of address
$city = $address->getCity(); // part of address
$address_addition_1 = $address->getAdditionalAddressLine1(); // part of address
$address_addition_2 = $address->getAdditionalAddressLine2(); // part of address
$phone_number = $address->getPhoneNumber(); // part of address
$webpage = null; // ???
$id = '0x0';
$transaktions_id = null; // auto-increment
$art = $created_or_changed_or_deleted;
$kundengruppe = $customer_group;
$sprache_name = $language_name;
$sprache_gebietsschema = $language_locale;
$kundennummer = $customer->getCustomerNumber();
$titel = $customer->getTitle();
$anrede = $salutation->getDisplayName();
$vorname = $customer->getFirstName();
$nachname = $customer->getLastName();
$firma = $customer->getCompany();
if (empty($firma))
$firma = $address->getCompany();
$abteilung = $address->getDepartment(); // part of address
$USt_id = $customer->getVatIds()[0] ?? "";
$land = $country;
$bundesland = $state;
$strasse = $street;
$postleitzahl = $postal_code;
$ort = $city;
$adresse_Zusatz_Zeile1 = $address_addition_1;
$adresse_Zusatz_Zeile2 = $address_addition_2;
$email = $customer->getEmail();
$telefon = $phone_number;
$geburtstag = $customer->getBirthday();
$aktiv = $customer->getActive();
$werbezusage = $customer->getNewsletter() ? 1 : 0;
$IP_Adresse = $customer->getRemoteAddress(); // $customer->getNewsletter() ? 'TODO' : null;//ip only if werbezusage
/* The function gets the remote IP which is stored by nginx in
* the headers send to Apache.
*/
$IP_Adresse = SharedFunctions::getRemoteIP();
$email_alt = null;//should be done by a trigger in db
$webseite = $webpage;
$zu_uebertragen = 0; //(always 0 for us)
$gast = $customer->getGuest() ? 1 : 0;
$erste_Anmeldung = $customer->getFirstLogin();
$letzte_Anmeldung = $customer->getLastLogin();
$erstellt_am = $customer->getCreatedAt();
$geaendert_am = $customer->getUpdatedAt();
$gesperrt = 0;
if (isset($customer->getCustomFields()['custom_customer_blocked'])) {
$gesperrt = $customer->getCustomFields()['custom_customer_blocked'];
}
if ($firma) {
$firma = substr($firma, 0, 40); // Firma darf maximal 40 Zeichen lang sein
$firma = str_replace('"', "'", $firma); // Damit die D.3 Schnittstelle funktioniert
}
// if geaendert_am is null this is the registration of the customer
if ($geaendert_am == null) {
$art = 0;
}
// avoid double entries
$last_entry_sql = 'SELECT NOT EXISTS( SELECT `geaendert_am` FROM `Kundendaten_Shop` WHERE `Email` = ' . $this->valueString($email)
. ' AND `geaendert_am` BETWEEN '
. $this->dateString($geaendert_am) . ' - INTERVAL 2 SECOND AND '
. $this->dateString($geaendert_am) . ' ) AS `lastEntry`';
if ($result = $mySql->query($last_entry_sql)) {
// $result indicates that no entry for the customer with that name exists which is less than 2 seconds old
// so we can insert the data
$row = mysqli_fetch_array($result);
$no_entry_exists = $row['lastEntry'];
$result->close();
// create the query and send it to the external database
// no need for a prepared statement
if ($no_entry_exists == "1") {
$sql = 'INSERT INTO `Kundendaten_Shop` (`id`, `Transaktions_Id`, `Art`, `Kundengruppe`,'
. ' `Sprache_Name`, `Sprache_Gebietsschema`, `Kundennummer`, `Titel`,'
. ' `Anrede`, `Vorname`, `Nachname`, `Firma`, `Abteilung`, `USt_id`,'
. ' `Land`, `Bundesland`, `Strasse`, `Postleitzahl`, `Ort`, `Adresse_Zusatz_Zeile1`,'
. ' `Adresse_Zusatz_Zeile2`, `Email`, `Telefon`, `Geburtstag`, `aktiv`, `gesperrt`,'
. ' `Werbezusage`, `IP_Adresse`, `Email_alt`, `Webseite`, `zu_uebertragen`,'
. ' `Gast`, `erste_Anmeldung`, `letzte_Anmeldung`, `erstellt_am`, `geaendert_am`, `Land_ISO_3`)'
. ' VALUES (' . $id . ', ' . $this->valueString($transaktions_id) . ', ' . $art . ', ' . $this->valueString($kundengruppe) . ', '
. $this->valueString($sprache_name) . ',' . $this->valueString($sprache_gebietsschema) . ', '
. $this->valueString($kundennummer) . ', ' . $this->valueString($titel) . ', ' . $this->valueString($anrede)
. ', ' . $this->valueString($vorname) . ', ' . $this->valueString($nachname) . ', '
. $this->valueString($firma, 40) . ', ' . $this->valueString($abteilung, 35) . ', '
. $this->valueString($USt_id) . ' ,' . $this->valueString($land) . ', '
. $this->valueString($bundesland) . ', ' . $this->valueString($strasse) . ', '
. $this->valueString($postleitzahl) . ', ' . $this->valueString($ort) . ', '
. $this->valueString($adresse_Zusatz_Zeile1) . ', ' . $this->valueString($adresse_Zusatz_Zeile2) . ', '
. $this->valueString($email) . ', ' . $this->valueString($telefon) . ', ' . $this->dateString($geburtstag) . ', '
. $this->booleanString($aktiv) . ', ' . $this->booleanString($gesperrt) . ', ' . $werbezusage . ', ' . $this->valueString($IP_Adresse) . ', '
. $this->valueString($email_alt) . ', ' . $this->valueString($webseite) . ', 0, '
. $gast . ', ' . $this->dateString($erste_Anmeldung) . ', '
. $this->dateString($letzte_Anmeldung) . ', ' . $this->dateString($erstellt_am)
. ', ' . $this->dateString($geaendert_am)
. ', ' . $this->valueString($iso_3) . ')';
if (!$mySql->query($sql)) {
//query did not succeed, log this somehow
//dump(mysqli_error($mysqli), $sql, $customer);
}
}
} else {
// either there is an entry for the customer or there was an error
// dump(mysqli_error($mysqli));
}
}
private function adjustSalesChannel($customerId)
{
/* Hier wird der Standard-Saleschannel für den Kunden korrigiert.
Es muss sich immer um den Shop-channel handeln, da für den Kunden sonst
keine manuellen Bestellungen erzeugt werden können. Leider ist dieser
Eintrag im Backend nicht bearbeitbar und wird bei Anlage des Kunden auf
den in diesem Moment aktuellen Channel gesetzt.
*/
SharedFunctions::dumpToLog("adjustSalesChannel()");
$dbHost = $this->systemConfigService->get('SteincoProductsController.config.externalDatabaseHost');
$dbUser = $this->systemConfigService->get('SteincoProductsController.config.externalDatabaseUser');
$dbPass = $this->systemConfigService->get('SteincoProductsController.config.externalDatabasePassword');
$dbName = $this->systemConfigService->get('SteincoProductsController.config.shopDatabaseName');
$mySql = new \mysqli($dbHost, $dbUser, $dbPass, $dbName);
if ($mySql->connect_errno) {
SharedFunctions::dumpToLog('Could not connect to MySQL: ' . $mySql->connect_error);
return;
}
$salesChannelId = $this->systemConfigService->get('SteincoProductsController.config.scShopUUID');
SharedFunctions::dumpToLog($salesChannelId);
try {
// Vorher prüfen, ob ein Update notwendig ist
$checkSql = "SELECT HEX(sales_channel_id) AS sales_channel_id FROM `customer` WHERE id = UNHEX('" . $customerId . "')";
$result = $mySql->query($checkSql);
if ($result) {
$row = $result->fetch_assoc();
if ($row && $row['sales_channel_id'] !== strtoupper($salesChannelId)) {
// Update durchführen, wenn der neue Wert unterschiedlich ist
$updateSql = "UPDATE `customer` SET sales_channel_id = UNHEX('" . $salesChannelId . "') WHERE id = UNHEX('" . $customerId . "')";
if (!$mySql->query($updateSql)) {
// Query did not succeed, log this somehow
SharedFunctions::dumpToLog('Error executing: ' . $updateSql);
SharedFunctions::dumpToLog('SQL-Error: ' . $mySql->error);
} else {
SharedFunctions::dumpToLog('Aktualisierung des SalesChannels erfolgt. ' . $mySql->affected_rows . ' Zeile(n).');
}
SharedFunctions::dumpToLog($updateSql);
} else {
SharedFunctions::dumpToLog('Kein Update notwendig. SalesChannelId ist bereits gesetzt.');
}
} else {
SharedFunctions::dumpToLog('Error executing: ' . $checkSql);
SharedFunctions::dumpToLog('SQL-Error: ' . $mySql->error);
}
} catch (\Exception $e) {
// Logge die Fehlermeldung in eine Datei oder verarbeite den Fehler entsprechend
SharedFunctions::dumpToLog('Aktualisierung des SalesChannels fehlgeschlagen.');
SharedFunctions::dumpToLog($e->getMessage());
}
}
private function valueString($value, $maxLength = null)
{
$result = 'NULL';
if ($value) {
$valueString = '"' . $value . '" ';
if (is_null($maxLength)) {
$result = $valueString;
} else {
// Beschränke die Länge des Ergebnisses auf $maxLength Zeichen
$result = substr($valueString, 0, $maxLength);
}
}
return $result;
}
private function dateString($date)
{
$result = 'NULL';
if ($date) {
$result = '"' . $date->format('Y-m-d H:i:s') . '" ';
}
return $result;
}
private function booleanString($a_boolean)
{
if ($a_boolean) {
return '1';
}
return '0';
}
}