src/Core/Content/Cms/Subscriber/CmsPageDefaultChangeSubscriber.php line 72

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