<?php
namespace Abas\AbasConnector\Subscribers\Customer;
use Abas\AbasConnector\Service\Core\Content\CustomerRole\CustomerRoleEntity;
use Abas\AbasConnector\Service\Core\Content\CustomerRole\CustomerRoleRelationEntity;
use Abas\AbasConnector\Service\Core\Enums\CustomerRole;
use Abas\AbasConnector\Service\Core\Enums\Duration;
use Abas\AbasConnector\Service\Log\AbasLogger;
use Abas\AbasConnector\Service\Utils\Benchmark\Benchmark;
use Shopware\Core\Checkout\Customer\CustomerEntity;
use Shopware\Core\Checkout\Customer\CustomerEvents;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityLoadedEvent;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\Routing\Event\SalesChannelContextResolvedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\ItemInterface;
class CustomerRoleSubscriber implements EventSubscriberInterface
{
/**
* @var string
*/
protected const CACHE_PREFIX = 'CustomerRole_';
/**
* @var EntityRepositoryInterface
*/
protected $customerRoleRelationRepository;
/**
* @var EntityRepositoryInterface
*/
protected $customerRoleRepository;
/**
* @var CustomerRoleEntity[]
*/
protected $customerRoleEntities = [];
/**
* @var AbasLogger
*/
protected AbasLogger $logger;
/**
* @var CacheInterface
*/
protected CacheInterface $cache;
/**
* @param EntityRepositoryInterface $customerRoleRelationRepository
* @param EntityRepositoryInterface $customerRoleRepository
* @param AbasLogger $logger
* @param CacheInterface $cache
*/
public function __construct(
EntityRepositoryInterface $customerRoleRelationRepository,
EntityRepositoryInterface $customerRoleRepository,
AbasLogger $logger,
CacheInterface $cache
) {
$this->customerRoleRelationRepository = $customerRoleRelationRepository;
$this->customerRoleRepository = $customerRoleRepository;
$this->logger = $logger;
$this->cache = $cache;
}
/**
* @inheritDoc
*/
public static function getSubscribedEvents(): array
{
return [
SalesChannelContextResolvedEvent::class => 'assignCustomerRoleToContext',
CustomerEvents::CUSTOMER_LOADED_EVENT => 'assignCustomerRoleToCustomers'
];
}
/**
* @param SalesChannelContextResolvedEvent $event
*
* @return void
*/
public function assignCustomerRoleToContext(SalesChannelContextResolvedEvent $event): void
{
$salesChannelContext = $event->getSalesChannelContext();
$customer = $salesChannelContext->getCustomer();
if ($customer !== null) {
$customerRole = $customer->getExtension('customerRole');
} else {
$customerRole = $this->getDefaultCustomerRole($salesChannelContext->getContext());
}
$salesChannelContext->assign(['customerRole' => $customerRole]);
}
/**
* @param EntityLoadedEvent $event
*
* @return void
*/
public function assignCustomerRoleToCustomers(EntityLoadedEvent $event): void
{
$startTime = microtime(true);
$context = $event->getContext();
$customers = $event->getEntities();
if (empty($customers)) {
return;
}
/** @var CustomerEntity $customer */
foreach ($customers as $customer) {
$this->assignCustomerRoleToCustomer($customer, $context);
}
// commented because this floods public/var/log/app.log
//$this->logger->info('CustomerRoleSubscriber: assigned customer roles for ' . count($customers)
// . ' customers. Took ' . Benchmark::getFormattedTime($startTime));
}
/**
* @param CustomerEntity $customer
* @param Context $context
*
* @return void
*/
private function assignCustomerRoleToCustomer(CustomerEntity $customer, Context $context): void
{
$customerId = $customer->getId();
$customerRoleRelation = $this->getCustomerRoleRelationByCustomer($customerId, $context);
$customer->addExtensions([
'customerRole' => $this->getCustomerRole($customerId, $context, $customerRoleRelation),
'customerRoleRelation' => $customerRoleRelation
]);
}
/**
* @param string $customerId
* @param Context $context
* @param null|CustomerRoleRelationEntity $customerRoleRelationEntity
*
* @return CustomerRoleEntity
*/
private function getCustomerRole(
string $customerId,
Context $context,
?CustomerRoleRelationEntity $customerRoleRelationEntity
): CustomerRoleEntity {
// Create new entity and retrieve it.
if ($customerRoleRelationEntity === null) {
$this->createCustomerRoleRelation($customerId, $context);
$customerRoleRelationEntity = $this->getCustomerRoleRelationByCustomer($customerId, $context);
}
return $customerRoleRelationEntity->getCustomerRole();
}
/**
* @param string $customerId
* @param Context $context
*
* @return CustomerRoleRelationEntity|null
*/
private function getCustomerRoleRelationByCustomer(
string $customerId,
Context $context
): ?CustomerRoleRelationEntity {
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('customerId', $customerId));
$criteria->addAssociation('customerRole.permissions');
$criteria->setLimit(1);
return $this->customerRoleRelationRepository->search($criteria, $context)->first();
}
/**
* Derzeit nicht in Benutzung, da er nicht korrekt cached.
* @param string $customerId
* @param Context $context
*
* @return CustomerRoleRelationEntity|null
*/
private function getCachedCustomerRoleRelationByCustomer(
string $customerId,
Context $context
): ?CustomerRoleRelationEntity {
$cacheKey = self::CACHE_PREFIX . $customerId;
return $this->cache->get(
$cacheKey,
function (ItemInterface $item) use ($customerId, $context) {
$item->expiresAfter(Duration::FOUR_HOURS);
return $this->getCustomerRoleRelationByCustomer($customerId, $context);
}
);
}
/**
* @param string $customerId
* @param Context $context
*
* @return void
*/
private function createCustomerRoleRelation(string $customerId, Context $context): void
{
$this->customerRoleRelationRepository->upsert(
[
[
'customerId' => $customerId,
'customerRoleId' => $this->getCustomerRoleByKey(CustomerRole::USER, $context)->getId()
]
],
$context
);
}
/**
* @param Context $context
*
* @return CustomerRoleEntity
*/
private function getDefaultCustomerRole(Context $context): CustomerRoleEntity
{
return $this->getCustomerRoleByKey(CustomerRole::GUEST, $context);
}
/**
* @param string $key
* @param Context $context
*
* @return CustomerRoleEntity
*/
private function getCustomerRoleByKey(string $key, Context $context): CustomerRoleEntity
{
if (empty($this->customerRoleEntities[$key])) {
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('roleKey', $key));
$criteria->addAssociation('permissions');
$criteria->setLimit(1);
$this->customerRoleEntities[$key] = $this->customerRoleRepository->search($criteria, $context)->first();
}
return $this->customerRoleEntities[$key];
}
}