<?php declare(strict_types=1);
namespace NewsletterSendinblue\Subscriber;
use NewsletterSendinblue\NewsletterSendinblue;
use NewsletterSendinblue\Service\BaseSyncService;
use Shopware\Core\Content\Category\Aggregate\CategoryTranslation\CategoryTranslationDefinition;
use Shopware\Core\Content\Category\CategoryDefinition;
use Shopware\Core\Content\Product\Aggregate\ProductTranslation\ProductTranslationDefinition;
use Shopware\Core\Content\Product\Aggregate\ProductVisibility\ProductVisibilityDefinition;
use Shopware\Core\Content\Product\ProductDefinition;
use Shopware\Core\Defaults;
use Shopware\Core\Framework\DataAbstractionLayer\Write\Command\ChangeSetAware;
use Shopware\Core\Framework\DataAbstractionLayer\Write\Command\DeleteCommand;
use Shopware\Core\Framework\DataAbstractionLayer\Write\Command\UpdateCommand;
use Shopware\Core\Framework\DataAbstractionLayer\Write\Validation\PreWriteValidationEvent;
use Shopware\Core\Framework\Struct\ArrayStruct;
use Shopware\Core\Framework\Uuid\Uuid;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class PreWriteValidationSubscriber implements EventSubscriberInterface
{
/** @var BaseSyncService */
private $productSyncService;
/** @var BaseSyncService */
private $categorySyncService;
private $deleteProducts = [];
private $deleteCategories = [];
private $visibilityAlsoRemoved = false;
private $onlyVisibilityRemoved = false;
public function __construct(
BaseSyncService $productSyncService,
BaseSyncService $categorySyncService
) {
$this->productSyncService = $productSyncService;
$this->categorySyncService = $categorySyncService;
}
public static function getSubscribedEvents(): array
{
return [
PreWriteValidationEvent::class => 'onPreWriteValidationEvent'
];
}
public function onPreWriteValidationEvent(PreWriteValidationEvent $event): void
{
if ($event->getContext()->getVersionId() !== Defaults::LIVE_VERSION) {
return;
}
if ($event->getContext()->hasExtension(NewsletterSendinblue::IGNORE_EVENT)) {
return;
}
$isProductFieldUpdated = false;
$productDeletableIds = [];
$categoryDeletableIds = [];
foreach ($event->getCommands() as $command) {
$entityName = $command->getDefinition()->getEntityName();
// Need to check if there are updating fields concerning to productEntity (e.g., stock, price, etc.).
// For example, there can be only product sales channel (visibility) change etc...
if (!$isProductFieldUpdated && ($entityName === ProductDefinition::ENTITY_NAME
|| $entityName === ProductTranslationDefinition::ENTITY_NAME)
) {
$isProductFieldUpdated = true;
}
if (!$command instanceof ChangeSetAware) {
continue;
}
// Handle change set requests
if ($this->shouldRequestChangeSet($entityName) && $command instanceof UpdateCommand) {
$command->requestChangeSet();
}
// Handle product visibility
// if one of the sales channels is removed from the product
if ($entityName === ProductVisibilityDefinition::ENTITY_NAME
&& $command instanceof DeleteCommand
) {
if ($isProductFieldUpdated) {
$this->visibilityAlsoRemoved = true;
} else {
$this->onlyVisibilityRemoved = true;
}
}
// Collect product deletions
if ($entityName === ProductDefinition::ENTITY_NAME
&& $command instanceof DeleteCommand
&& !empty($command->getPrimaryKey()['id'])
) {
$productId = Uuid::fromBytesToHex($command->getPrimaryKey()['id']);
$productDeletableIds[] = $productId;
$childrenIds = $this->productSyncService->getChildrenIds($productId, $event->getContext());
$productDeletableIds = array_merge($productDeletableIds, $childrenIds);
}
// Collect category deletion
if ($entityName === CategoryDefinition::ENTITY_NAME
&& $command instanceof DeleteCommand
&& !empty($command->getPrimaryKey()['id'])
) {
$catId = Uuid::fromBytesToHex($command->getPrimaryKey()['id']);
$categoryDeletableIds[] = $catId;
$childrenIds = $this->categorySyncService->getChildrenIds($catId, $event->getContext());
$categoryDeletableIds = array_merge($categoryDeletableIds, $childrenIds);
}
}
foreach ($productDeletableIds as $id) {
$this->deleteProducts[$id] = $this->productSyncService->getEntity($id, $event->getContext());
}
if (!empty($this->deleteProducts)) {
$this->productSyncService->setDeleteEntities($this->deleteProducts);
}
foreach ($categoryDeletableIds as $id) {
$this->deleteCategories[$id] = $this->categorySyncService->getEntity($id, $event->getContext());
}
if (!empty($this->deleteCategories)) {
$this->categorySyncService->setDeleteEntities($this->deleteCategories);
}
$event->getContext()->addExtension('brevoPreWriteEvent', new ArrayStruct([
'visibilityAlsoRemoved' => $this->visibilityAlsoRemoved,
'onlyVisibilityRemoved' => $this->onlyVisibilityRemoved,
'deleteProducts' => $this->deleteProducts,
'deleteCategories' => $this->deleteCategories
]));
}
private function shouldRequestChangeSet(string $entityName): bool
{
return in_array($entityName, [
ProductDefinition::ENTITY_NAME,
ProductTranslationDefinition::ENTITY_NAME,
CategoryDefinition::ENTITY_NAME,
CategoryTranslationDefinition::ENTITY_NAME
], true);
}
}