Naprawione eventy purchase, begin_checkout, view_item, add_to_cart do formatu GA4 (item_id/item_name zamiast id/name, currency PLN, google_business_vertical, poprawne typy danych). Dodany nowy event view_cart na stronie koszyka. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
10 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous
| phase | plan | type | wave | depends_on | files_modified | autonomous | ||||
|---|---|---|---|---|---|---|---|---|---|---|
| 11-datalayer-ga4-fix | 01 | execute | 1 |
|
true |
Purpose
Bez tych poprawek remarketing dynamiczny Google Ads i konwersje GA4 nie dzialaja poprawnie — ceny produktow sa zerowe, klucze itemow niezgodne z GA4, brakuje walut i eventow.
Output
Poprawione 4 szablony PHP z prawidlowymi eventami dataLayer GA4.
## Project Context @.paul/PROJECT.md @.paul/ROADMAP.md @.paul/STATE.mdSource Files
@templates/shop-order/order-details.php (purchase event, lines 164-192) @templates/shop-basket/summary-view.php (begin_checkout event, lines 72-80, 175-187) @templates/shop-product/product.php (view_item lines 273-288, add_to_cart lines 607-625) @templates/shop-basket/basket.php (brak view_cart — do dodania)
Technical Reference
@poprawki_datalayer_projectpro.md (specyfikacja zmian z audytu)
Data Model
Order products (pp_shop_order_products) mają kolumny: product_id, name, price_brutto, price_brutto_promo, quantity. Basket products — surowa tablica z sesji, product data fetchowana przez ProductRepository::findCached().
No specialized flows configured — standard execute plan.<acceptance_criteria>
AC-1: Purchase event — format GA4 z prawidlowa cena
Given strona potwierdzenia zamowienia /zamowienie/*
When dataLayer.push(purchase) jest wywolany
Then items maja klucze item_id (string), item_name, price (number > 0), quantity (number), google_business_vertical: "retail"
And ecommerce ma currency: "PLN"
And nie ma zduplikowanego klucza value ani hardcoded wartosci
AC-2: Begin_checkout event — format GA4
Given strona /koszyk-podsumowanie z produktami w koszyku
When dataLayer.push(begin_checkout) jest wywolany
Then items maja klucze item_id (string), item_name (zamiast id, name), price (number), quantity (number), google_business_vertical: "retail"
AC-3: View_item event — kompletne dane
Given strona produktu
When dataLayer.push(view_item) jest wywolany
Then ecommerce zawiera currency: "PLN" i value (number)
And items maja price jako number (nie string), google_business_vertical: "retail"
AC-4: Add_to_cart event — poprawne typy
Given klikniecie "dodaj do koszyka" na stronie produktu
When dataLayer.push(add_to_cart) jest wywolany
Then items maja google_business_vertical: "retail"
And quantity jest number (nie string)
AC-5: View_cart event — nowy event na stronie koszyka
Given strona /koszyk z produktami w koszyku
When strona sie zaladuje
Then dataLayer.push({event: "view_cart"}) jest wywolany z currency, value i items w formacie GA4
</acceptance_criteria>
Task 1: Naprawic istniejace eventy dataLayer (purchase, begin_checkout, view_item, add_to_cart) templates/shop-order/order-details.php, templates/shop-basket/summary-view.php, templates/shop-product/product.php **order-details.php (purchase event, linie 167-187):** 1. Usunac hardcoded `value: 25.42` (linia 172) — zostawic tylko dynamiczny `value` z linii 174 2. Zamienic `'id': ` na `item_id: ""` 3. Zamienic `'name': ''` na `item_name: ""` 4. Zamienic logike price na: `price: 0 && (float)$product['price_brutto_promo'] < (float)$product['price_brutto']) ? \Shared\Helpers\Helpers::normalize_decimal($product['price_brutto_promo']) : \Shared\Helpers\Helpers::normalize_decimal($product['price_brutto']);?>` 5. Dodac `google_business_vertical: "retail"` do kazdego itemu 6. Zamienic single quotes na double quotes w kluczach itemow (konsystencja) 7. Dodac `'quantity': ` (rzutowanie na int)**summary-view.php (begin_checkout items, linie 72-80):**
1. Zamienic `'"id": "' . $product['id']` na `'"item_id": "' . $product['id']`
2. Zamienic `'"name": "' . $product['language']['name']` na `'"item_name": "' . str_replace('"', '', $product['language']['name'])`
3. Dodac `'"google_business_vertical": "retail"'` do kazdego itemu
**product.php (view_item, linie 273-287):**
1. Dodac `currency: "PLN",` do obiektu ecommerce (przed items)
2. Dodac `value: <cena>,` do obiektu ecommerce (po currency)
3. Zmienic `price: '<cena>'` na `price: <cena>` (usunac cudzyslow — number zamiast string)
4. Dodac `google_business_vertical: "retail"` do itemu
**product.php (add_to_cart, linie 607-624):**
1. Dodac `google_business_vertical: "retail"` do itemu
- quantity jest juz prawidlowo number (zmienna JS `quantity` pochodzi z parseInt/parseFloat lub .val() — sprawdzic i ewentualnie dodac parseInt)
**Wazne:** Nie zmieniac struktury warunkow `if ($this->settings['google_tag_manager_id'])` — zostawic identycznie.
**Wazne:** Uzywac normalize_decimal() dla cen (zapewnia format z kropka, nie przecinkiem).
1. Przegladnac wygenerowany HTML kazdego eventu — sprawdzic format kluczy, typy, obecnosc currency i google_business_vertical
2. Sprawdzic brak bledow skladni JS (cudzyslow, przecinki)
3. Testy PHPUnit nie powinny byc dotknięte (zmiany tylko w szablonach)
AC-1, AC-2, AC-3, AC-4 satisfied: Wszystkie eventy uzywaja item_id/item_name, price jako number, currency PLN, google_business_vertical
Task 2: Dodac event view_cart na stronie koszyka
templates/shop-basket/basket.php
Dodac dataLayer.push dla view_cart w sekcji `<script>` na poczatku bloku `$(function() {` w basket.php (linia 209).
Implementacja:
1. Dodac blok PHP+JS wewnatrz istniejacego `<script>` (po linii 50, w nowym `<script>` z warunkiem GTM):
```
<? if ( $this -> settings['google_tag_manager_id'] ?? false ): ?>
<? if ( is_array( $this -> basket ) and count( $this -> basket ) ): ?>
<script type="text/javascript">
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "view_cart",
ecommerce: {
currency: "PLN",
value: [obliczona suma],
items: [
// iteracja po $this->basket z fetchem produktu przez ProductRepository
]
}
});
</script>
<? endif; ?>
<? endif; ?>
```
2. Iterowac po `$this->basket`, dla kazdego elementu pobrac product data (ProductRepository::findCached) i zbudowac item z item_id, item_name, price, quantity, google_business_vertical.
3. Obliczyc value jako sume (price * quantity) wszystkich produktow.
**Uwaga:** basket.php ma dostep do `$this->basket` (raw basket array). Kazdy element ma klucze: 'product-id', 'quantity', 'parent_id', 'attributes'.
Product data nalezy pobrac przez: `(new \Domain\Product\ProductRepository($GLOBALS['mdb']))->findCached((int)$position['product-id'], $lang_id)` — identycznie jak robi basket-details.php.
Uzyc `$GLOBALS['mdb']` i `(new \Domain\Languages\LanguagesRepository($GLOBALS['mdb']))->defaultLanguage()` dla lang_id (lub sprawdzic czy $this->lang_id jest dostepny — jesli nie, pobrac z sesji).
**Wazne:** Dodac nowy `<script>` blok PRZED istniejacym blokiem `<script>` (przed linia 36), nie wewnatrz istniejacego — zeby uniknac konfliktow z jQuery ready i AJAX reload.
**Wazne:** Warunek `settings['google_tag_manager_id']` — uzyc `$settings` (global) lub `$this->settings` — sprawdzic ktore jest dostepne w basket.php (linia 1: `global $settings` sugeruje ze $settings jest dostepny).
1. Otworzyc /koszyk z produktami — sprawdzic w konsoli przegladarki dataLayer na obecnosc view_cart
2. Sprawdzic czy items maja poprawne pola: item_id (string), item_name, price (number), quantity (number), google_business_vertical
3. Sprawdzic czy value = suma cen * ilosci
4. Sprawdzic czy event NIE odapla sie ponownie po AJAX reload koszyka (bo jest w osobnym script poza basket-details)
AC-5 satisfied: Event view_cart jest pushowany na stronie /koszyk z pelnym zestawem danych GA4
DO NOT CHANGE
- autoload/Domain/* (warstwa domenowa — bez zmian)
- autoload/front/Controllers/* (kontrolery — bez zmian)
- templates/shop-basket/basket-details.php (AJAX-replaceable — nie dodawac tam skryptow)
- Logika sesji google-analytics-purchase (purchase dedup)
- Warunki
if ($this->settings['google_tag_manager_id'])— zachowac identycznie
SCOPE LIMITS
- Tylko eventy dataLayer — nie dodawac/zmieniac Facebook Pixel, gtag, ani innych trackerow
- Nie zmieniac struktury HTML szablonow
- Nie dodawac user_data do purchase (opcjonalne w specyfikacji, wymaga osobnej analizy RODO)
- Nie usuwac/przenosic kodu GADS conversion (nie znaleziono w kodzie — prawdopodobnie w GTM)
- Nie dodawac nowych eventow poza view_cart (np. remove_from_cart — poza zakresem)
<success_criteria>
- All tasks completed
- All verification checks pass
- No errors or warnings introduced
- DataLayer eventy zgodne z formatem GA4 (item_id, item_name, currency, google_business_vertical)
- Remarketing dynamiczny Google Ads ma prawidlowe ceny produktow </success_criteria>