Strategien zur Extraktion vom Produkt über ein Event auf der Produkt Detail Seite

Hallo Community,

ich stehe vor der Herausforderung, aus einer URL, die von einem Referrer wie beispielsweise Idealo auf unseren Shop weiterleitet, eine spezifische ID herauszufiltern und diese URL anschließend zu speichern. Dieser Prozess ist relativ einfach, aber der Weg, um das entsprechende Produkt zu identifizieren, erscheint mir noch nicht optimal.

Gibt es ein Event, ähnlich wie ein hypothetisches „ProductPageLoadedEvent“, das mir direkten Zugang zum Produkt ermöglichen würde? Ich konnte bisher kein passendes finden. Alles, was ich bislang versucht habe, hat nicht wie gewünscht funktioniert.

Hat vielleicht jemand eine Idee, wie ich auf eine saubere und effiziente Weise zu diesem Ziel gelangen kann?

<?php

declare(strict_types=1);

namespace CCTracking\Subscriber;

use CCTracking\Service\ReferrerTrackingService;
use CCTracking\Service\Client;
use Psr\Log\LoggerInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\System\SalesChannel\Context\SalesChannelContextService;
use Shopware\Core\System\SalesChannel\Context\SalesChannelContextServiceParameters;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;

/**
 * Handles referral tracking by subscribing to request events.
 */
class ReferrerSubscriber implements EventSubscriberInterface
{
    private ReferrerTrackingService $referrerTrackingService;
    private LoggerInterface $logger;
    private Client $client;
    private EntityRepository $productRepository;
    private SalesChannelContextService $salesChannelContextService;

    /**
     * Constructor for ReferrerSubscriber.
     *
     * @param ReferrerTrackingService $referrerTrackingService Service for referrer tracking.
     * @param Client $client Client for API requests.
     * @param LoggerInterface $logger Logger interface for logging.
     * @param EntityRepository $productRepository Repository for product data.
     * @param SalesChannelContextService $salesChannelContextService Service for sales channel context.
     */
    public function __construct(
        ReferrerTrackingService    $referrerTrackingService,
        Client               $client,
        LoggerInterface            $logger,
        EntityRepository           $productRepository,
        SalesChannelContextService $salesChannelContextService
    )
    {
        $this->referrerTrackingService = $referrerTrackingService;
        $this->client = $client;
        $this->logger = $logger;
        $this->productRepository = $productRepository;
        $this->salesChannelContextService = $salesChannelContextService;
    }

    /**
     * Returns an array of events this subscriber wants to listen to.
     *
     * @return array The event listener mappings.
     */
    public static function getSubscribedEvents(): array
    {
        return [
            KernelEvents::REQUEST => 'onKernelRequest',
        ];
    }

    /**
     * Handles kernel request events.
     *
     * @param RequestEvent $event The request event.
     */
    public function onKernelRequest(RequestEvent $event): void
    {
        if (!$event->isMainRequest()) {
            return;
        }
        $request = $event->getRequest();
        $session = $request->getSession();
        if (!$session->isStarted()) {
            $session->start();
        }
        $clid = $request->query->get('clid');
        if ($clid) {
            $salesChannelContext = $this->getSalesChannelContext($session, $request);
            if (!$salesChannelContext) {
                return;
            }
            $productId = $this->extractProductIdFromUri($request->getPathInfo());
            $priceAndAvailable = $this->getProductPriceAndAvailable($productId, $salesChannelContext);

            $fullRequestUrl = $this->getFullRequestUrl($request);
            $this->referrerTrackingService->storeReferrer($session, $fullRequestUrl);
            $client = $this->client->call('/landing','GET',[
                'avail' => $priceAndAvailable['avail'] ?? 0,
                'price' => $priceAndAvailable['price'] ?? 0,
                'url' => $fullRequestUrl,
            ]);
            $result = $client;
            if (isset($result['httpCode'])) {
                if (!in_array($result['httpCode'], [200, 204])) {
                    $this->logger->error('Failed Client request', [
                        'statusCode' => $result['httpCode'],
                        'clid' => $clid,
                        'url' => $fullRequestUrl
                    ]);
                    $this->referrerTrackingService->deleteReferrer($session);
                } else {
                    $this->logger->debug('Successful Client request', [
                        'statusCode' => $result['httpCode'],
                        'clid' => $clid,
                        'url' => $fullRequestUrl
                    ]);
                }
            }
        }
    }

    /**
     * Retrieves product price and availability for a given product ID.
     *
     * @param string $productId The product ID.
     * @param SalesChannelContext $context The sales channel context.
     * @return array|null Returns an associative array with price and availability, or null if not found.
     */
    private function getProductPriceAndAvailable(string $productId, SalesChannelContext $context): ?array
    {
        $criteria = new Criteria([$productId]);
        $result = $this->productRepository->search($criteria, $context->getContext());

        if ($product = $result->getEntities()->first()) {
            $price = $product->getPrice()->first();
            if ($price) {
                return [
                    'price' => $price->getGross(),
                    'avail' => $product->getAvailable()
                ];
            }
        }

        return null;
    }

    /**
     * Extracts the product ID from a given URI.
     *
     * @param string $uri The URI to extract the product ID from.
     * @return string|null Returns the product ID or null if not found.
     */
    private function extractProductIdFromUri(string $uri): ?string
    {
        $parts = explode('/detail/', $uri);

        if (count($parts) > 1) {
            $detailPart = explode('?', $parts[1]);
            return $detailPart[0];
        }

        return null;
    }

    /**
     * Constructs the full URL for the current request.
     *
     * @param Request $request The current request.
     * @return string The full request URL.
     */
    private function getFullRequestUrl(Request $request): string
    {
        return $request->getSchemeAndHttpHost() . $_SERVER['REQUEST_URI'];
    }

    /**
     * Gets the SalesChannelContext for the current request.
     *
     * @param SessionInterface $session The current session.
     * @param Request $request The current request.
     * @return SalesChannelContext|null The sales channel context or null if not found.
     */
    private function getSalesChannelContext(SessionInterface $session, Request $request): ?SalesChannelContext
    {
        $salesChannelId = $session->get('sw-sales-channel-id');
        if (!$salesChannelId) {
            $this->logger->error('No sales channel ID found in session');
            return null;
        }
        $token = $session->get('session-id') ?? $request->headers->get('sw-context-token');
        $contextParameters = new SalesChannelContextServiceParameters($salesChannelId, $token);
        return $this->salesChannelContextService->get($contextParameters);
    }
}


Äh - wieso hypothetisch? ProductPageLoadedEvent gibt es und sollte auch das passende für dich sein.

public static function getSubscribedEvents(): array
    {
        return [
            ProductPageLoadedEvent::class => 'onProductLoaded'
        ];
    }

Ja hab ich schon versucht aber ich finde das Event nicht unter
use Shopware\Storefront\Page\Product\ProductPageLoadedEvent;

sowie in der doku finde ich es auch nicht

Dann hast du in deiner Entwicklungsumgebung was falsch konfiguriert. Das Event ist definitiv da wo es auch sein soll.

Danke dein hint mit der falsch konfigurierten Entwicklungsumgebung hat mich dazu gebracht das nochmal zu überprüfen und ich hatte in der composer.json

"shopware/storefront": "6.5.*"

nicht drin eingefügt jetzt läuft es und ich hab meinen Code massiv reduzieren können.

1 Like

Dieses Thema wurde automatisch 30 Tage nach der letzten Antwort geschlossen. Es sind keine neuen Antworten mehr erlaubt.