<?php declare(strict_types=1);
namespace Dvdw\Events\Content\Product;
use Dvdw\Events\Content\Product\Exception\ProductUrlAlreadyExistsException;
use Dvdw\Events\Content\Product\Exception\ProductUrlInvalidException;
use Shopware\Core\Content\Product\Aggregate\ProductTranslation\ProductTranslationDefinition;
use Shopware\Core\Defaults;
use Shopware\Core\Framework\Context;
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\Search\Filter\MultiFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\NotFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\PrefixFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Write\Validation\PreWriteValidationEvent;
use Shopware\Core\Framework\Uuid\Uuid;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class ProductUrlValidator implements EventSubscriberInterface
{
private EntityRepository $repo;
public function __construct(
EntityRepository $repo
)
{
$this->repo = $repo;
}
public static function getSubscribedEvents(): iterable
{
return [PreWriteValidationEvent::class => 'validate'];
}
public function validate(PreWriteValidationEvent $event): void
{
$context = $event->getContext();
if ($context->getVersionId() !== Defaults::LIVE_VERSION) {
return;
}
foreach ($event->getCommands() as $command) {
$payload = $command->getPayload();
if (!$command->getDefinition() instanceof ProductTranslationDefinition
|| ($url = $this->getUrlFromPayload($payload)) === null)
{
continue;
}
$this->validateUrl($url, $context, Uuid::fromBytesToHexList($command->getPrimaryKey()));
}
}
private function getUrlFromPayload(array $payload): ?string
{
if (isset($payload[ProductCustomFields::WEBSITE_FIELD_NAME])) {
return $payload[ProductCustomFields::WEBSITE_FIELD_NAME];
}
if (!isset($payload['custom_fields'])) {
return null;
}
$customFields = json_decode($payload['custom_fields'], true);
return $customFields[ProductCustomFields::WEBSITE_FIELD_NAME] ?? null;
}
private function validateUrl(string $url, Context $context, array $primaryKey): void
{
$parsedUrl = parse_url($url);
if (!isset($parsedUrl['host']) || !isset($parsedUrl['scheme']) || $parsedUrl['scheme'] !== 'https') {
throw new ProductUrlInvalidException($url, $primaryKey);
}
$criteria = new Criteria();
$criteria->setLimit(1);
$criteria->addFilter(new PrefixFilter(
'customFields.' . ProductCustomFields::WEBSITE_FIELD_NAME,
'https://' . $parsedUrl['host']
));
$languageId = $primaryKey['language_id'] ?? null;
if ($languageId !== null) {
$criteria->addFilter(new EqualsFilter('languageId', $languageId));
}
$productId = $primaryKey['product_id'] ?? null;
if ($productId !== null) {
$criteria->addFilter(new NotFilter(
MultiFilter::CONNECTION_AND,
[new EqualsFilter('productId', $productId)]
));
}
if (empty($this->repo->search($criteria, $context)->getElements())) {
return;
}
throw new ProductUrlAlreadyExistsException($url, $primaryKey);
}
}