<?php declare(strict_types=1);namespace Shopware\Core\Checkout\Shipping\Validator;use Doctrine\DBAL\Connection;use Shopware\Core\Checkout\Shipping\ShippingMethodDefinition;use Shopware\Core\Checkout\Shipping\ShippingMethodEntity;use Shopware\Core\Framework\DataAbstractionLayer\Write\Command\InsertCommand;use Shopware\Core\Framework\DataAbstractionLayer\Write\Command\UpdateCommand;use Shopware\Core\Framework\DataAbstractionLayer\Write\Validation\PreWriteValidationEvent;use Shopware\Core\Framework\Log\Package;use Shopware\Core\Framework\Validation\WriteConstraintViolationException;use Symfony\Component\EventDispatcher\EventSubscriberInterface;use Symfony\Component\Validator\ConstraintViolation;use Symfony\Component\Validator\ConstraintViolationInterface;use Symfony\Component\Validator\ConstraintViolationList;/** * @deprecated tag:v6.5.0 - reason:becomes-internal - EventSubscribers will become internal in v6.5.0 */#[Package('checkout')]class ShippingMethodValidator implements EventSubscriberInterface{ public const VIOLATION_TAX_TYPE_INVALID = 'tax_type_invalid'; public const VIOLATION_TAX_ID_REQUIRED = 'c1051bb4-d103-4f74-8988-acbcafc7fdc3'; private Connection $connection; /** * @internal */ public function __construct(Connection $connection) { $this->connection = $connection; } /** * @return array<string, string|array{0: string, 1: int}|list<array{0: string, 1?: int}>> */ public static function getSubscribedEvents() { return [ PreWriteValidationEvent::class => 'preValidate', ]; } public function preValidate(PreWriteValidationEvent $event): void { $allowTypes = [ ShippingMethodEntity::TAX_TYPE_FIXED, ShippingMethodEntity::TAX_TYPE_AUTO, ShippingMethodEntity::TAX_TYPE_HIGHEST, ]; $writeCommands = $event->getCommands(); foreach ($writeCommands as $command) { $violations = new ConstraintViolationList(); if (!$command instanceof InsertCommand && !$command instanceof UpdateCommand) { continue; } if ($command->getDefinition()->getClass() !== ShippingMethodDefinition::class) { continue; } $shippingMethod = $this->findShippingMethod($command->getPrimaryKey()['id']); $payload = $command->getPayload(); /** @var string|null $taxType */ $taxType = $this->getValue($payload, 'tax_type', $shippingMethod); /** @var string|null $taxId */ $taxId = $this->getValue($payload, 'tax_id', $shippingMethod); if ($taxType && !\in_array($taxType, $allowTypes, true)) { $violations->add( $this->buildViolation( 'The selected tax type {{ type }} is invalid.', ['{{ type }}' => $taxType], '/taxType', $taxType, self::VIOLATION_TAX_TYPE_INVALID ) ); } if ($taxType === ShippingMethodEntity::TAX_TYPE_FIXED && !$taxId) { $violations->add( $this->buildViolation( 'The defined tax rate is required when fixed tax present', ['{{ taxId }}' => null], '/taxId', $taxType, self::VIOLATION_TAX_ID_REQUIRED ) ); } if ($violations->count() > 0) { $event->getExceptions()->add(new WriteConstraintViolationException($violations, $command->getPath())); } } } /** * @return array<string, mixed> */ private function findShippingMethod(string $shippingMethodId): array { $shippingMethod = $this->connection->executeQuery( 'SELECT `tax_type`, `tax_id` FROM `shipping_method` WHERE `id` = :id', ['id' => $shippingMethodId] ); return $shippingMethod->fetchAssociative() ?: []; } /** * @param array<string, mixed> $parameters */ private function buildViolation( string $messageTemplate, array $parameters, string $propertyPath, string $invalidValue, string $code ): ConstraintViolationInterface { return new ConstraintViolation( str_replace(array_keys($parameters), array_values($parameters), $messageTemplate), $messageTemplate, $parameters, null, $propertyPath, $invalidValue, null, $code ); } /** * Gets a value from an array. It also does clean checks if * the key is set, and also provides the option for default values. * * @param array<string, mixed> $data the data array * @param string $key the requested key in the array * @param array<string, mixed> $dbRow the db row of from the database * * @return mixed the object found in the key, or the default value */ private function getValue(array $data, string $key, array $dbRow) { // try in our actual data set if (isset($data[$key])) { return $data[$key]; } // try in our db row fallback if (isset($dbRow[$key])) { return $dbRow[$key]; } // use default return null; }}