<?php declare(strict_types=1);
namespace Swkweb\ProductSet\Core\Content\ProductSet\Validation;
use Shopware\Core\Framework\DataAbstractionLayer\Write\Command\ChangeSetAware;
use Shopware\Core\Framework\DataAbstractionLayer\Write\Command\InsertCommand;
use Shopware\Core\Framework\DataAbstractionLayer\Write\Command\UpdateCommand;
use Shopware\Core\Framework\DataAbstractionLayer\Write\Command\WriteCommand;
use Shopware\Core\Framework\DataAbstractionLayer\Write\Validation\PostWriteValidationEvent;
use Shopware\Core\Framework\DataAbstractionLayer\Write\Validation\PreWriteValidationEvent;
use Shopware\Core\Framework\Validation\WriteConstraintViolationException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Validator\ConstraintViolation;
use Symfony\Component\Validator\ConstraintViolationList;
abstract class ChangeSetValidator implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
PreWriteValidationEvent::class => 'preValidate',
PostWriteValidationEvent::class => 'postValidate',
];
}
public function preValidate(PreWriteValidationEvent $event): void
{
foreach ($this->filterCommands($event->getCommands()) as $command) {
if ($command instanceof ChangeSetAware) {
$command->requestChangeSet();
}
}
}
public function postValidate(PostWriteValidationEvent $event): void
{
foreach ($this->filterCommands($event->getCommands()) as $command) {
$violations = new ConstraintViolationList();
$this->validate($command, $violations);
if ($violations->count()) {
$event->getExceptions()->add(
new WriteConstraintViolationException($violations, $command->getPath())
);
}
}
}
protected function isChanged(WriteCommand $command, string $field): bool
{
return isset($command->getPayload()[$field]);
}
/**
* @return array<string,mixed>|string|mixed|null
*/
protected function getValue(WriteCommand $command, string $field)
{
if ($command instanceof ChangeSetAware) {
$changeSet = $command->getChangeSet();
if ($changeSet) {
return $changeSet->getAfter($field) ?? $changeSet->getBefore($field);
}
}
return $command->getPayload()[$field] ?? null;
}
protected function getValueInt(WriteCommand $command, string $field): int
{
$value = $this->getValue($command, $field);
assert(is_numeric($value));
return (int) $value;
}
/**
* @param array<string,mixed> $parameters
* @param mixed $invalidValue
*/
protected function buildViolation(string $template, array $parameters, string $path, $invalidValue, string $code): ConstraintViolation
{
return new ConstraintViolation(strtr($template, $parameters), $template, $parameters, null, $path, $invalidValue, null, $code);
}
/**
* @param WriteCommand[] $commands
*
* @return WriteCommand[]
*/
private function filterCommands(array $commands): array
{
return array_filter($commands, function ($command) {
if (!($command instanceof InsertCommand || $command instanceof UpdateCommand)) {
return false;
}
if ($command->getDefinition()->getClass() !== $this->getDefinitionClass()) {
return false;
}
foreach ($this->getFields() as $field) {
if ($this->isChanged($command, $field)) {
return true;
}
}
return false;
});
}
abstract protected function getDefinitionClass(): string;
/**
* @return string[]
*/
abstract protected function getFields(): array;
abstract protected function validate(WriteCommand $command, ConstraintViolationList $violations): void;
}