custom/plugins/theme/src/Subscriber/DynamicAccessSubscriber.php line 46

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace DonCarneTheme\Subscriber;
  3. use Shopware\Core\Content\Category\Exception\CategoryNotFoundException;
  4. use Shopware\Core\Content\Product\Exception\ProductNotFoundException;
  5. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  6. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  7. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  8. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  9. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  10. use Symfony\Component\HttpFoundation\RedirectResponse;
  11. use Symfony\Component\HttpKernel\Event\ExceptionEvent;
  12. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  13. use Symfony\Component\HttpKernel\KernelEvents;
  14. use Symfony\Component\Routing\RouterInterface;
  15. class DynamicAccessSubscriber implements EventSubscriberInterface
  16. {
  17.     private EntityRepositoryInterface $categoryRepository;
  18.     private EntityRepositoryInterface $productRepository;
  19.     private RouterInterface $router;
  20.     /**
  21.      * The URL for the Beef Club login page
  22.      */
  23.     private const BEEF_CLUB_URL 'https://doncarne.de/c/beef-club/';
  24.     public function __construct(
  25.         EntityRepositoryInterface $categoryRepository,
  26.         EntityRepositoryInterface $productRepository,
  27.         RouterInterface $router
  28.     ) {
  29.         $this->categoryRepository $categoryRepository;
  30.         $this->productRepository $productRepository;
  31.         $this->router $router;
  32.     }
  33.     public static function getSubscribedEvents(): array
  34.     {
  35.         return [
  36.             KernelEvents::EXCEPTION => ['onKernelException'0]
  37.         ];
  38.     }
  39.     public function onKernelException(ExceptionEvent $event): void
  40.     {
  41.         $exception $event->getThrowable();
  42.         $request $event->getRequest();
  43.         
  44.         // Check if we have a sales channel context and a logged-in user
  45.         if (!$request->attributes->has('sw-sales-channel-context')) {
  46.             return;
  47.         }
  48.         
  49.         /** @var SalesChannelContext $context */
  50.         $context $request->attributes->get('sw-sales-channel-context');
  51.         
  52.         // Only handle 404 exceptions, CategoryNotFoundException, and ProductNotFoundException
  53.         if (!($exception instanceof NotFoundHttpException) && 
  54.             !($exception instanceof CategoryNotFoundException) &&
  55.             !($exception instanceof ProductNotFoundException)) {
  56.             return;
  57.         }
  58.         
  59.         // Check for typical URL patterns that might be restricted by Dynamic Access
  60.         $pathInfo $request->getPathInfo();
  61.         
  62.         // Handle category not found exceptions - check if category actually exists
  63.         if (($exception instanceof CategoryNotFoundException) || $this->isCategoryPath($pathInfo)) {
  64.             $categoryId $this->extractCategoryIdFromPath($pathInfo);
  65.             if ($categoryId && $this->categoryExists($categoryId$context)) {
  66.                 // Category exists but user doesn't have access, redirect to Beef Club
  67.                 $response = new RedirectResponse(self::BEEF_CLUB_URL);
  68.                 $event->setResponse($response);
  69.                 return;
  70.             }
  71.         }
  72.         
  73.         // Handle product not found exceptions - check if product actually exists
  74.         if (($exception instanceof ProductNotFoundException) || $this->isProductPath($pathInfo)) {
  75.             $productId $this->extractProductIdFromPath($pathInfo);
  76.             if ($productId && $this->productExists($productId$context)) {
  77.                 // Product exists but user doesn't have access, redirect to Beef Club
  78.                 $response = new RedirectResponse(self::BEEF_CLUB_URL);
  79.                 $event->setResponse($response);
  80.                 return;
  81.             }
  82.         }
  83.     }
  84.     
  85.     /**
  86.      * Determine if the path looks like a category page
  87.      */
  88.     private function isCategoryPath(string $pathInfo): bool
  89.     {
  90.         // Category paths typically don't have extensions and don't contain /detail/ or /checkout/
  91.         return !preg_match('/(\.[\w\d]+$|\/detail\/|\/checkout\/)/'$pathInfo);
  92.     }
  93.     
  94.     /**
  95.      * Determine if the path looks like a product detail page
  96.      */
  97.     private function isProductPath(string $pathInfo): bool
  98.     {
  99.         // Product paths typically contain /detail/ 
  100.         return strpos($pathInfo'/detail/') !== false;
  101.     }
  102.     
  103.     /**
  104.      * Extract a potential category ID from a URL path
  105.      */
  106.     private function extractCategoryIdFromPath(string $pathInfo): ?string
  107.     {
  108.         // Extract the last segment of the URL which should be the category ID
  109.         if (preg_match('/\/([a-f0-9]{32})(?:\/|$)/'$pathInfo$matches)) {
  110.             return $matches[1];
  111.         }
  112.         
  113.         return null;
  114.     }
  115.     
  116.     /**
  117.      * Extract a potential product ID from a URL path
  118.      */
  119.     private function extractProductIdFromPath(string $pathInfo): ?string
  120.     {
  121.         // Extract the ID from a path like '/detail/a1b2c3.../'
  122.         if (preg_match('/\/detail\/([a-f0-9]{32})(?:\/|$)/'$pathInfo$matches)) {
  123.             return $matches[1];
  124.         }
  125.         
  126.         return null;
  127.     }
  128.     
  129.     /**
  130.      * Check if a category exists in the database without applying customer group filters
  131.      */
  132.     private function categoryExists(string $categoryIdSalesChannelContext $context): bool
  133.     {
  134.         $criteria = new Criteria([$categoryId]);
  135.         $criteria->addFilter(new EqualsFilter('active'true));
  136.         $criteria->addAssociation('type');
  137.         
  138.         // Query directly against the repository to bypass sales channel filters
  139.         $result $this->categoryRepository->search($criteria$context->getContext());
  140.         
  141.         return $result->getTotal() > 0;
  142.     }
  143.     
  144.     /**
  145.      * Check if a product exists in the database without applying customer group filters
  146.      */
  147.     private function productExists(string $productIdSalesChannelContext $context): bool
  148.     {
  149.         $criteria = new Criteria([$productId]);
  150.         $criteria->addFilter(new EqualsFilter('active'true));
  151.         
  152.         // Query directly against the repository to bypass sales channel filters
  153.         $result $this->productRepository->search($criteria$context->getContext());
  154.         
  155.         return $result->getTotal() > 0;
  156.     }
  157. }