src/Core/Content/Category/Validation/EntryPointValidator.php line 46

  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Content\Category\Validation;
  3. use Doctrine\DBAL\Connection;
  4. use Shopware\Core\Content\Category\CategoryDefinition;
  5. use Shopware\Core\Framework\DataAbstractionLayer\Write\Command\InsertCommand;
  6. use Shopware\Core\Framework\DataAbstractionLayer\Write\Command\UpdateCommand;
  7. use Shopware\Core\Framework\DataAbstractionLayer\Write\Command\WriteCommand;
  8. use Shopware\Core\Framework\DataAbstractionLayer\Write\Validation\PostWriteValidationEvent;
  9. use Shopware\Core\Framework\Log\Package;
  10. use Shopware\Core\Framework\Validation\WriteConstraintViolationException;
  11. use Shopware\Core\System\SalesChannel\SalesChannelDefinition;
  12. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  13. use Symfony\Component\Validator\ConstraintViolation;
  14. use Symfony\Component\Validator\ConstraintViolationList;
  15. use Symfony\Component\Validator\ConstraintViolationListInterface;
  16. /**
  17.  * @internal
  18.  */
  19. #[Package('content')]
  20. class EntryPointValidator implements EventSubscriberInterface
  21. {
  22.     private const ERROR_CODE 'CONTENT__INVALID_CATEGORY_TYPE_AS_ENTRY_POINT';
  23.     private const ALLOWED_TYPE_CHANGE = [
  24.         CategoryDefinition::TYPE_PAGE,
  25.         CategoryDefinition::TYPE_FOLDER,
  26.     ];
  27.     /**
  28.      * @internal
  29.      */
  30.     public function __construct(private readonly Connection $connection)
  31.     {
  32.     }
  33.     public static function getSubscribedEvents(): array
  34.     {
  35.         return [
  36.             PostWriteValidationEvent::class => 'postValidate',
  37.         ];
  38.     }
  39.     public function postValidate(PostWriteValidationEvent $event): void
  40.     {
  41.         $violationList = new ConstraintViolationList();
  42.         foreach ($event->getCommands() as $command) {
  43.             if (!($command instanceof InsertCommand || $command instanceof UpdateCommand)) {
  44.                 continue;
  45.             }
  46.             if ($command->getDefinition()->getClass() !== CategoryDefinition::class) {
  47.                 continue;
  48.             }
  49.             if (!isset($command->getPayload()['type'])) {
  50.                 continue;
  51.             }
  52.             $violationList->addAll($this->checkTypeChange($command$event));
  53.         }
  54.         if ($violationList->count() > 0) {
  55.             $event->getExceptions()->add(new WriteConstraintViolationException($violationList));
  56.             return;
  57.         }
  58.     }
  59.     private function checkTypeChange(WriteCommand $commandPostWriteValidationEvent $event): ConstraintViolationListInterface
  60.     {
  61.         $violationList = new ConstraintViolationList();
  62.         $payload $command->getPayload();
  63.         if (\in_array($payload['type'], self::ALLOWED_TYPE_CHANGEtrue)) {
  64.             return $violationList;
  65.         }
  66.         if ($this->isCategoryEntryPoint($command->getPrimaryKey()['id'], $event)) {
  67.             return $violationList;
  68.         }
  69.         $messageTemplate 'The type can not be assigned while category is entry point.';
  70.         $parameters = ['{{ value }}' => $payload['type']];
  71.         $violationList->add(new ConstraintViolation(
  72.             str_replace(array_keys($parameters), $parameters$messageTemplate),
  73.             $messageTemplate,
  74.             $parameters,
  75.             null,
  76.             sprintf('%s/type'$command->getPath()),
  77.             $payload['type'],
  78.             null,
  79.             self::ERROR_CODE
  80.         ));
  81.         return $violationList;
  82.     }
  83.     private function isCategoryEntryPoint(string $categoryIdPostWriteValidationEvent $event): bool
  84.     {
  85.         foreach ($event->getCommands() as $salesChannelCommand) {
  86.             if ($salesChannelCommand->getDefinition()->getClass() !== SalesChannelDefinition::class) {
  87.                 continue;
  88.             }
  89.             $payload $salesChannelCommand->getPayload();
  90.             if ((isset($payload['navigation_category_id']) && $payload['navigation_category_id'] === $categoryId)
  91.                 || (isset($payload['footer_category_id']) && $payload['footer_category_id'] === $categoryId)
  92.                 || (isset($payload['service_category_id']) && $payload['service_category_id'] === $categoryId)
  93.             ) {
  94.                 return false;
  95.             }
  96.         }
  97.         $result $this->connection->createQueryBuilder()
  98.             ->select('id')
  99.             ->from(SalesChannelDefinition::ENTITY_NAME)
  100.             ->where('navigation_category_id = :navigation_id')
  101.             ->orWhere('footer_category_id = :footer_id')
  102.             ->orWhere('service_category_id = :service_id')
  103.             ->setParameter('navigation_id'$categoryId)
  104.             ->setParameter('footer_id'$categoryId)
  105.             ->setParameter('service_id'$categoryId)
  106.             ->setMaxResults(1)
  107.             ->executeQuery();
  108.         return !$result->fetchOne();
  109.     }
  110. }