vendor/shopware/core/Content/Cms/Subscriber/CmsPageDefaultChangeSubscriber.php line 52

  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Content\Cms\Subscriber;
  3. use Doctrine\DBAL\ArrayParameterType;
  4. use Doctrine\DBAL\Connection;
  5. use Shopware\Core\Content\Category\CategoryDefinition;
  6. use Shopware\Core\Content\Cms\CmsException;
  7. use Shopware\Core\Content\Cms\CmsPageDefinition;
  8. use Shopware\Core\Content\Cms\Exception\PageNotFoundException;
  9. use Shopware\Core\Content\Product\ProductDefinition;
  10. use Shopware\Core\Defaults;
  11. use Shopware\Core\Framework\DataAbstractionLayer\Event\BeforeDeleteEvent;
  12. use Shopware\Core\Framework\Log\Package;
  13. use Shopware\Core\Framework\Uuid\Uuid;
  14. use Shopware\Core\System\SystemConfig\Event\BeforeSystemConfigChangedEvent;
  15. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  16. /**
  17.  * @internal
  18.  */
  19. #[Package('content')]
  20. class CmsPageDefaultChangeSubscriber implements EventSubscriberInterface
  21. {
  22.     /**
  23.      * @var array<string>
  24.      */
  25.     public static array $defaultCmsPageConfigKeys = [
  26.         ProductDefinition::CONFIG_KEY_DEFAULT_CMS_PAGE_PRODUCT,
  27.         CategoryDefinition::CONFIG_KEY_DEFAULT_CMS_PAGE_CATEGORY,
  28.     ];
  29.     /**
  30.      * @internal
  31.      */
  32.     public function __construct(private readonly Connection $connection)
  33.     {
  34.     }
  35.     public static function getSubscribedEvents(): array
  36.     {
  37.         return [
  38.             BeforeSystemConfigChangedEvent::class => 'validateChangeOfDefaultCmsPage',
  39.             BeforeDeleteEvent::class => 'beforeDeletion',
  40.         ];
  41.     }
  42.     /**
  43.      * @throws CmsException
  44.      * @throws \JsonException
  45.      */
  46.     public function beforeDeletion(BeforeDeleteEvent $event): void
  47.     {
  48.         $cmsPageIds $event->getIds(CmsPageDefinition::ENTITY_NAME);
  49.         // no cms page is affected by this deletion event
  50.         if (empty($cmsPageIds)) {
  51.             return;
  52.         }
  53.         $defaultPages $this->cmsPageIsDefault($cmsPageIds);
  54.         // count !== 0 indicates that there are some cms pages which would be deleted but are currently a default
  55.         if (\count($defaultPages) !== 0) {
  56.             throw CmsException::deletionOfDefault($defaultPages);
  57.         }
  58.     }
  59.     /**
  60.      * @throws CmsException
  61.      * @throws PageNotFoundException
  62.      */
  63.     public function validateChangeOfDefaultCmsPage(BeforeSystemConfigChangedEvent $event): void
  64.     {
  65.         $newDefaultCmsPageId $event->getValue();
  66.         $systemConfigKey $event->getKey();
  67.         $salesChannelId $event->getSalesChannelId();
  68.         if (!\in_array($systemConfigKeyself::$defaultCmsPageConfigKeystrue)) {
  69.             return;
  70.         }
  71.         // prevent deleting the overall default (salesChannelId === null)
  72.         // a sales channel specific default can still be deleted (salesChannelId !== null)
  73.         if ($newDefaultCmsPageId === null && $salesChannelId === null) {
  74.             $oldCmsPageId $this->getCurrentOverallDefaultCmsPageId($systemConfigKey);
  75.             throw CmsException::overallDefaultSystemConfigDeletion($oldCmsPageId);
  76.         }
  77.         if (!\is_string($newDefaultCmsPageId) && $newDefaultCmsPageId !== null) {
  78.             throw new PageNotFoundException('invalid page');
  79.         }
  80.         // prevent changing the default to an invalid cms page id
  81.         if (\is_string($newDefaultCmsPageId) && !$this->cmsPageExists($newDefaultCmsPageId)) {
  82.             throw new PageNotFoundException($newDefaultCmsPageId);
  83.         }
  84.     }
  85.     private function getCurrentOverallDefaultCmsPageId(string $systemConfigKey): string
  86.     {
  87.         $result $this->connection->fetchOne(
  88.             'SELECT configuration_value FROM system_config WHERE configuration_key = :configKey AND sales_channel_id is NULL;',
  89.             [
  90.                 'configKey' => $systemConfigKey,
  91.             ]
  92.         );
  93.         $config json_decode((string) $resulttrue512\JSON_THROW_ON_ERROR);
  94.         return $config['_value'];
  95.     }
  96.     /**
  97.      * @param array<string> $cmsPageIds
  98.      *
  99.      * @return array<string>
  100.      */
  101.     private function cmsPageIsDefault(array $cmsPageIds): array
  102.     {
  103.         $configurations $this->connection->fetchAllAssociative(
  104.             'SELECT DISTINCT configuration_value FROM system_config WHERE configuration_key IN (:configKeys);',
  105.             [
  106.                 'configKeys' => self::$defaultCmsPageConfigKeys,
  107.             ],
  108.             [
  109.                 'configKeys' => ArrayParameterType::STRING,
  110.             ]
  111.         );
  112.         $defaultIds = [];
  113.         foreach ($configurations as $configuration) {
  114.             $configValue $configuration['configuration_value'];
  115.             $config json_decode((string) $configValuetrue512\JSON_THROW_ON_ERROR);
  116.             $defaultIds[] = $config['_value'];
  117.         }
  118.         // returns from all provided cms pages the ones which are default
  119.         return array_intersect($cmsPageIds$defaultIds);
  120.     }
  121.     private function cmsPageExists(string $cmsPageId): bool
  122.     {
  123.         $count $this->connection->fetchOne(
  124.             'SELECT count(*) FROM cms_page WHERE id = :cmsPageId AND version_id = :versionId LIMIT 1;',
  125.             [
  126.                 'cmsPageId' => Uuid::fromHexToBytes($cmsPageId),
  127.                 'versionId' => Uuid::fromHexToBytes(Defaults::LIVE_VERSION),
  128.             ]
  129.         );
  130.         return $count === '1';
  131.     }
  132. }