Przejdź do głównej zawartości

Jak renderują się sceny

Ta strona śledzi scenę od załadowania strony storefrontu do momentu zamontowania jej w DOM. Przydatna, gdy debugujesz “dlaczego mój popup się nie pojawia” albo projektujesz custom integrację motywu.

P2Lab Stage rozszerza jeden template Shopware: footer layoutu storefrontu. Plugin dodaje ukryty kontener kontekstu do istniejącego bloku layout_footer_inner_container:

{% sw_extends '@Storefront/storefront/layout/footer/footer.html.twig' %}
{% block layout_footer_inner_container %}
{{ parent() }}
{% if p2labStageContext %}
<div data-p2lab-stage
data-p2lab-stage-context="{{ context|json_encode }}"
hidden></div>
{% endif %}
{% endblock %}

Rozszerzenie p2labStageContext jest wypełniane przez subscribera nasłuchującego każdego ładowania strony storefrontu i obliczającego tożsamość bieżącej strony (typ strony, ID produktu / kategorii / producenta / strony CMS, URL żądania, skonfigurowany tryb GDPR, route URLs które plugin JS musi wywołać).

Żadne inne base templates nie są nadpisywane. Popupy nie są embedded w Twig — są pobierane client-side z /p2lab-stage/resolve.

P2LabStagePlugin jest zarejestrowany na selektor [data-p2lab-stage]. Na każdej stronie, gdzie ten kontener istnieje, PluginManager Shopware instancjuje plugin. Plugin następnie:

  1. Czyta data-p2lab-stage-context do obiektu JS.
  2. Czyta ID suppressed popupów z sessionStorage / localStorage (zależnie od trybu GDPR — zobacz niżej).
  3. POST-uje /p2lab-stage/resolve z kontekstem strony + suppressed IDs.
  4. Dla każdego zwróconego popupu planuje go zgodnie z triggerem.

POST /p2lab-stage/resolve robi ciężką robotę server-side.

{
"pageType": "product",
"requestUrl": "/product/spring-jacket",
"productId": "<uuid>",
"productIds": [],
"categoryId": "<uuid>",
"manufacturerId": "<uuid>",
"productStreamIds": [],
"cmsPageId": null,
"clientSuppressedIds": ["<popup-uuid-1>", "<popup-uuid-2>"]
}

Kanał sprzedaży i grupa klientów nie są wysyłane — są odczytywane server-side z SalesChannelContext, którego nie da się okłamać z klienta.

Server-side, plugin chodzi po każdej aktywnej scenie i stosuje te filtry w kolejności:

  1. Przełącznik Active + harmonogram w zakresie (active_from / active_to).
  2. Kanał sprzedaży match.
  3. Grupa klientów match (przeciwko grupie zalogowanego klienta, albo domyślnej grupie dla anonymous).
  4. Reguła ewaluacja (z rule_operator OR / AND).
  5. Kategorie (z includeChildren per wiersz, uwzględniając ścieżkę kategorii).
  6. Produkty / Producenci / Product streamy / Strony CMS matche.
  7. Typy stron + wzorce URL z pages_operator.
  8. Client-suppressed IDs (popupy, które klient mówi że już odrzucił w tej sesji).
  9. Server-side dismiss state (sesja PHP, gdy zgoda GDPR brakuje dla storage).

Przeżywające sceny są sortowane po priorytecie.

Pozostała lista jest następnie cap’owana przez ustawienia systemowe:

  • displayMode = only-first (domyślnie) — pozostaje tylko pierwsza (najwyższego priorytetu) scena; reszta jest dropowana dla tej strony.
  • displayMode = queue — do maxPerSession scen jest zachowane (albo wszystkie jeśli maxPerSession = 0).

Ten limit to optymalizacja bandwidth — klient też trackuje własny session-wide distinct-popup counter.

{
"popups": [
{
"id": "<uuid>",
"displayType": "modal",
"triggerType": "delay",
"triggerValue": 5,
"frequencyType": "session",
"closeButton": true,
"overlayClose": true,
"permanentDismissButton": false,
"statsEnabled": true,
"html": "<!-- pre-rendered stage HTML -->"
}
],
"displayMode": "only-first",
"maxPerSession": 3
}

html jest pre-renderowany server-side z stage-content.html.twig — klient po prostu wstawia go do DOM. Pozycjonowanie / styling / dane animacji każdej warstwy żyją w atrybutach data-* do odczytu przez silniki JS.

Dla każdego popupu w odpowiedzi resolve:

  • immediate → zamontuj od razu.
  • delaysetTimeout(triggerValue × 1000) potem montuj.
  • scroll → listener scrolla na window, odpala przy progu, usuwa się.

Gdy trigger się odpali, popup jest dopinany do document.body, animacja wejścia uruchamia się, silniki parallax / animacji / countdown / coupon się podpinają, a event impression jest wysyłany.

Podczas gdy popup jest zamontowany, klient wysyła eventy impression / CTA-click / close / permanent-dismiss do POST /p2lab-stage/track:

{
"popupId": "<uuid>",
"eventType": "impression",
"pageType": "product",
"requestUrl": "/product/spring-jacket"
}

Jeśli statsEnabled jest false na scenie, serwer silently no-opuje (defensive backstop — klient już pomija większość wywołań).

Przy odrzuceniu klient wywołuje też POST /p2lab-stage/dismiss, żeby zapisać suppression server-side (sesja PHP — przydatne gdy client storage jest niedostępny):

{ "popupId": "<uuid>", "frequencyType": "session", "frequencyValue": null }

Nieznane typy częstotliwości fallback na session, żeby misbehaving klient nie zapisał arbitralnych stringów.

Przy zmianie zgody (odwiedzający akceptuje cookies po server-dismiss popupa) klient wywołuje GET /p2lab-stage/dismissed, żeby pobrać listę server-side suppression i zmigrować do client storage.

Sceny slider nie używają pipeline’u resolve. Element CMS p2lab-stage-slider renderuje się inline przez normalny pipeline Shopping Experiences Shopware — partial Twig slidera jest częścią odpowiedzi HTML strony. Plugin JS slidera (P2LabStageSliderPlugin, zarejestrowany na [data-p2lab-stage-slider]) obsługuje autoplay, transitiony, swipe, kropki, strzałki.

Każdy route storefrontu w pluginie jest oznaczony _httpCache: false — resolve / track / dismiss / dismissed nie mogą być HTTP-cachowane, bo ich odpowiedzi różnią się per sesja, klient i stan częstotliwości. Sam template footera zostaje cachowalny, bo emituje tylko statyczny kontener kontekstu; faktyczne dane sceny są pobierane dynamicznie.