fix: linki produktow z permutacja atrybutow w feedzie Google (v0.350)
Separator URL miedzy parami attr-val zmieniony z "/" na "_" w generatorze feedu (ProductRepository::appendCombinationToXml). Wzorzec routingu pp_routes rozszerzony do [0-9_-]+ w Helpers::htacces (oba warianty: seo_link i fallback p-id-name). LayoutEngine konwertuje "_" -> "|" przed wywolaniem ProductRepository::findCached — format DB pozostaje "|". Partial product-attribute.php preselectuje wartosc z permutation_hash URL (forced_value_id), co poprawia UX wejscia z linka feedu. Suita: 834 -> 841 testow (+7), 2330 assertions. Wymagane akcje na produkcji po deployu: regeneracja pp_routes (Helpers::htacces), wyczyszczenie klucza pp_routes:all w Redis, regeneracja google-feed.xml, resubmit feedu w GMC. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
258
.paul/phases/18-google-feed-permutation-url-fix/18-01-PLAN.md
Normal file
258
.paul/phases/18-google-feed-permutation-url-fix/18-01-PLAN.md
Normal file
@@ -0,0 +1,258 @@
|
||||
---
|
||||
phase: 18-google-feed-permutation-url-fix
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- autoload/Domain/Product/ProductRepository.php
|
||||
- autoload/Shared/Helpers/Helpers.php
|
||||
- autoload/front/LayoutEngine.php
|
||||
- templates/shop-product/_partial/product-attribute.php
|
||||
- tests/Unit/Domain/Product/ProductRepositoryTest.php
|
||||
- tests/Unit/Shared/Helpers/HelpersTest.php
|
||||
autonomous: true
|
||||
delegation: off
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Naprawić linki produktów z permutacją atrybutów w feedzie Google: zamienić separator `/` na `_` między parami `attr-val`, dopasować routing `pp_routes`, konwersję `_` → `|` w warstwie front oraz preselekcję wartości atrybutów na podstawie `permutation_hash` z URL.
|
||||
|
||||
## Purpose
|
||||
URL z formatu `/slug/20-170/21-175` nie matchował się w `pp_routes` (wzorzec `[0-9-]+` nie obejmuje `/`), więc Google Merchant Center prowadził klientów na stronę główną zamiast na produkt z wybraną kombinacją atrybutów. Strata ruchu komercyjnego z feedu.
|
||||
|
||||
## Output
|
||||
- 4 pliki silnika z nowym separatorem `_`
|
||||
- Unit testy: regex routingu (Helpers) + generator linku (ProductRepository::appendCombinationToXml)
|
||||
- SUMMARY z listą akcji post-deploy do wykonania ręcznie na produkcji
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
@.paul/codebase/architecture.md
|
||||
|
||||
## Source Files
|
||||
@autoload/Domain/Product/ProductRepository.php
|
||||
@autoload/Shared/Helpers/Helpers.php
|
||||
@autoload/front/LayoutEngine.php
|
||||
@templates/shop-product/_partial/product-attribute.php
|
||||
|
||||
<clarifications>
|
||||
- **Testy** — Czy dodać unit testy dla zmian?
|
||||
→ Odpowiedź: Tak — pełne pokrycie (Helpers regex + ProductRepository::appendCombinationToXml)
|
||||
- **Post-deploy** — Czy wykonać regenerację routes/cache/feedu w ramach fazy?
|
||||
→ Odpowiedź: Nic — tylko kod; akcje produkcyjne udokumentowane w SUMMARY
|
||||
- **Redirect 301** — Czy dodać redirecty ze starych URL-i?
|
||||
→ Odpowiedź: Nie — Google sam zaktualizuje linki z feedu
|
||||
- **Skills** — /feature-dev required w SPECIAL-FLOWS?
|
||||
→ Odpowiedź: Override — pomiń (hotfix z konkretną instrukcją, jak w fazach 15/16/17)
|
||||
</clarifications>
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Generator linku w feedzie używa `_`
|
||||
```gherkin
|
||||
Given produkt z permutacją atrybutów (permutation_hash = "20-170|21-175")
|
||||
When wywołany jest ProductRepository::appendCombinationToXml dla feedu Google
|
||||
Then wygenerowany URL zawiera segment `20-170_21-175` (jeden segment, separator `_`)
|
||||
And nie zawiera `/` między parami atrybutów
|
||||
And dotyczy obu gałęzi (z seo_link i fallback p-id-name)
|
||||
```
|
||||
|
||||
## AC-2: Routing `pp_routes` matchuje URL z `_`
|
||||
```gherkin
|
||||
Given wzorzec routingu wygenerowany przez Helpers dla produktu z permutacją
|
||||
When URI to `slug-produktu/20-170_21-175`
|
||||
Then regex `[0-9_-]+` dopasowuje cały segment permutacji
|
||||
And `permutation_hash` w wynikowych GET to `20-170_21-175`
|
||||
And dotyczy obu wariantów (z seo_link i fallback p-id-name)
|
||||
```
|
||||
|
||||
## AC-3: Front konwertuje `_` z URL na `|` przed zapytaniem do bazy
|
||||
```gherkin
|
||||
Given GET['permutation_hash'] = "20-170_21-175"
|
||||
When LayoutEngine renderuje blok PRODUKT
|
||||
Then ProductRepository::findCached otrzymuje argument "20-170|21-175"
|
||||
And gdy GET['permutation_hash'] nie istnieje, findCached otrzymuje null
|
||||
```
|
||||
|
||||
## AC-4: Partial atrybutu preselectuje wartość z URL
|
||||
```gherkin
|
||||
Given URL produktu z permutation_hash zawierającym parę dla bieżącego atrybutu
|
||||
When renderuje się templates/shop-product/_partial/product-attribute.php
|
||||
Then aktywna (checked) jest wartość z URL, nie z is_default
|
||||
And gdy atrybut nie występuje w hashu, zachowane jest stare zachowanie (is_default)
|
||||
And blok <script> z fradio_label_click() emitowany jest dla wartości z URL
|
||||
```
|
||||
|
||||
## AC-5: Pełna suita testów zielona
|
||||
```gherkin
|
||||
Given wprowadzone zmiany w 4 plikach + 2 nowe/zaktualizowane testy
|
||||
When uruchomiony jest ./test.ps1
|
||||
Then wszystkie testy przechodzą (>=836 — 834 obecnych + ≥2 nowe)
|
||||
And brak warningów PHP
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Zmiana separatora w generatorze feedu i routingu</name>
|
||||
<files>autoload/Domain/Product/ProductRepository.php, autoload/Shared/Helpers/Helpers.php, autoload/front/LayoutEngine.php</files>
|
||||
<action>
|
||||
1. **autoload/Domain/Product/ProductRepository.php** — w `appendCombinationToXml` (~linie 2372 i 2374):
|
||||
- Zamienić `str_replace('|', '/', $combination['permutation_hash'])` na `str_replace('|', '_', $combination['permutation_hash'])`
|
||||
- Dotyczy OBU gałęzi (seo_link i fallback `p-id-name`)
|
||||
- Najpierw przeczytać metodę i potwierdzić obie wystąpienia przed edycją
|
||||
|
||||
2. **autoload/Shared/Helpers/Helpers.php** — w generatorze tras (~linie 694 i 699):
|
||||
- Rozszerzyć regex z `'^' . ... . '/([0-9-]+)$'` na `'^' . ... . '/([0-9_-]+)$'`
|
||||
- Dotyczy OBU wariantów (seo_link i fallback `p-id-name`)
|
||||
|
||||
3. **autoload/front/LayoutEngine.php** — w bloku `// PRODUKT` (~linia 196):
|
||||
- Wyciągnąć `permutation_hash` do zmiennej z konwersją `_` → `|`:
|
||||
```php
|
||||
$permutation_hash = isset($_GET['permutation_hash']) ? str_replace('_', '|', $_GET['permutation_hash']) : null;
|
||||
```
|
||||
- Przekazać `$permutation_hash` do `findCached()` zamiast inline `$_GET['permutation_hash'] ?? null`
|
||||
|
||||
Avoid:
|
||||
- Zmian w `findCached()` lub `permutation_hash` w bazie — separator w DB pozostaje `|`
|
||||
- Modyfikacji innych metod ProductRepository
|
||||
- PHP 8.0+ syntaxu (`match`, named args)
|
||||
</action>
|
||||
<verify>
|
||||
- `grep -n "str_replace.*'|'.*'/'" autoload/Domain/Product/ProductRepository.php` — brak wyników (0 wystąpień)
|
||||
- `grep -n "str_replace.*'|'.*'_'" autoload/Domain/Product/ProductRepository.php` — 2 wystąpienia
|
||||
- `grep -n "\[0-9_-\]+" autoload/Shared/Helpers/Helpers.php` — 2 wystąpienia
|
||||
- `grep -n "\[0-9-\]+\\\$" autoload/Shared/Helpers/Helpers.php` — brak (stary wzorzec usunięty z generatora produktów z permutacją)
|
||||
- `grep -n "permutation_hash" autoload/front/LayoutEngine.php` — zmienna wyciągnięta przed `findCached`
|
||||
</verify>
|
||||
<done>AC-1, AC-2, AC-3 spełnione</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Preselekcja atrybutu z permutation_hash w partialu</name>
|
||||
<files>templates/shop-product/_partial/product-attribute.php</files>
|
||||
<action>
|
||||
Najpierw przeczytać cały plik partiala (mały, ~kilkadziesiąt linii) i zlokalizować pętlę `foreach` po `values` oraz miejsca używające `$value['is_default']`.
|
||||
|
||||
Na początku partiala (przed pętlą po values) dodać:
|
||||
```php
|
||||
$forced_value_id = null;
|
||||
if ( isset( $_GET['permutation_hash'] ) && $_GET['permutation_hash'] !== '' )
|
||||
{
|
||||
$pairs = explode( '|', str_replace( '_', '|', $_GET['permutation_hash'] ) );
|
||||
foreach ( $pairs as $pair )
|
||||
{
|
||||
$parts = explode( '-', $pair );
|
||||
if ( count( $parts ) == 2 && (int)$parts[0] === (int)$this -> attribute['id'] )
|
||||
{
|
||||
$forced_value_id = (int)$parts[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
W pętli foreach po values, przed użyciem flagi `is_default`, policzyć:
|
||||
```php
|
||||
$is_active = $forced_value_id !== null
|
||||
? ( (int)$value['id'] === $forced_value_id )
|
||||
: (bool)$value['is_default'];
|
||||
```
|
||||
|
||||
Zastąpić WSZYSTKIE użycia `$value['is_default']` w kontekście aktywności (checked, fradio_label_click) zmienną `$is_active`. Nie ruszać `is_default` jeśli używane gdzie indziej semantycznie (np. atrybut metadata).
|
||||
|
||||
Avoid:
|
||||
- Modyfikacji `templates_user/` (potwierdzono: nie istnieje w tym repo)
|
||||
- Zmian struktury HTML / klas CSS
|
||||
- PHP 8.0+ syntaxu
|
||||
</action>
|
||||
<verify>
|
||||
- `grep -n "forced_value_id" templates/shop-product/_partial/product-attribute.php` — co najmniej 4 wystąpienia (deklaracja, set, użycie w `$is_active`, użycie w `$is_active`)
|
||||
- `grep -n "is_active" templates/shop-product/_partial/product-attribute.php` — co najmniej 2 wystąpienia (deklaracja + użycie w checked/script)
|
||||
- Manualnie potwierdzić: `checked="checked"` używa `$is_active`, `fradio_label_click(...)` script gate'owany przez `$is_active`
|
||||
</verify>
|
||||
<done>AC-4 spełnione</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: Unit testy dla regex routingu i generatora linku</name>
|
||||
<files>tests/Unit/Shared/Helpers/HelpersTest.php, tests/Unit/Domain/Product/ProductRepositoryTest.php</files>
|
||||
<action>
|
||||
Najpierw sprawdzić strukturę istniejących testów (szczególnie czy `HelpersTest.php` istnieje — jeśli nie, utworzyć z bootstrapem zgodnym z innymi testami w `tests/Unit/Shared/`).
|
||||
|
||||
1. **Helpers — test regex routingu z `_`:**
|
||||
- Wywołać generator tras dla produktu z permutacją (jeśli metoda jest publiczna; w przeciwnym razie test integracyjny z mockiem `$mdb` zwracającym permutacje produktu)
|
||||
- Zweryfikować że wygenerowany pattern zawiera `[0-9_-]+` zamiast `[0-9-]+`
|
||||
- Test: `preg_match` na patternie z URI `slug/20-170_21-175` zwraca true i wyciąga `20-170_21-175` jako capture group
|
||||
- Test negatywny: pattern NIE matchuje `slug/20-170/21-175` (stary format ze slashem — chcemy 404, nie przypadkowy match)
|
||||
|
||||
2. **ProductRepository::appendCombinationToXml — test separatora `_`:**
|
||||
- Może być nieosiągalna metoda private/protected. Strategia A (preferowana): jeśli private, użyć ReflectionMethod do wywołania na instancji z mockiem `$mdb`. Strategia B: jeśli zbyt skomplikowane, dodać minimalny test który wywołuje publiczną metodę feedu z mockiem i sprawdza wygenerowany XML.
|
||||
- Mock combination z `permutation_hash = '20-170|21-175'`, `seo_link = 'jakas-fraza'`
|
||||
- Asercja: w wygenerowanym XML link zawiera `jakas-fraza/20-170_21-175`, NIE zawiera `20-170/21-175`
|
||||
- Drugi test: gałąź fallback (brak `seo_link`) — link `p-{id}-{name}/20-170_21-175`
|
||||
|
||||
Trzymać się konwencji: AAA, mock Medoo (`$this->createMock(\medoo::class)`), namespace tests jak w istniejących plikach. Brak PHP 8.0+ syntaxu. Nazwy metod z `test` prefiksem.
|
||||
</action>
|
||||
<verify>
|
||||
- `./test.ps1 tests/Unit/Shared/Helpers/HelpersTest.php` — przechodzi
|
||||
- `./test.ps1 tests/Unit/Domain/Product/ProductRepositoryTest.php` — przechodzi (wszystkie testy, łącznie z nowymi)
|
||||
- `./test.ps1` — pełna suita zielona, count >= 836
|
||||
</verify>
|
||||
<done>AC-5 spełnione (testy zielone, ≥2 nowe testy pokrywające AC-1 i AC-2)</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- Format `permutation_hash` w bazie (kolumna `pp_shop_product_combinations.permutation_hash` pozostaje z separatorem `|`)
|
||||
- Sygnatura `ProductRepository::findCached()` — przyjmuje hash z `|`
|
||||
- Inne metody ProductRepository / inne kontrolery / inne partiale
|
||||
- Plików `templates_user/` (nie istnieje w tym repo, akcja po stronie klientów)
|
||||
- Schemat bazy danych
|
||||
- `.htaccess` w roocie (redirecty 301 wykluczone z scope)
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Tylko 4 pliki silnika + 2 pliki testów (lista w `files_modified`)
|
||||
- Brak automatycznej regeneracji `pp_routes` — udokumentowane w SUMMARY jako akcja deploy
|
||||
- Brak czyszczenia cache `pp_routes:all` w fazie — akcja deploy
|
||||
- Brak regeneracji `google-feed.xml` w fazie — akcja deploy
|
||||
- Brak redirectów 301 ze starych URL-i
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
- [ ] `./test.ps1` — pełna suita zielona (≥836 testów)
|
||||
- [ ] Brak `str_replace('|', '/', ...)` w ProductRepository (grep)
|
||||
- [ ] `[0-9_-]+` w obu wzorcach Helpers (grep)
|
||||
- [ ] `permutation_hash` wyciągnięte do zmiennej w LayoutEngine z konwersją `_`→`|`
|
||||
- [ ] Partial używa `$is_active` (forced_value_id || is_default) zamiast surowego `is_default`
|
||||
- [ ] Wszystkie 5 AC spełnione
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- 4 pliki silnika zmienione zgodnie z instrukcją
|
||||
- 2 nowe / zaktualizowane testy: routing regex + generator XML linku
|
||||
- Pełna suita testów zielona
|
||||
- Brak regresji w istniejących testach (834 → ≥836)
|
||||
- SUMMARY zawiera dokładną listę akcji post-deploy (regen pp_routes, clear cache, regen feedu, resubmit GMC)
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/18-google-feed-permutation-url-fix/18-01-SUMMARY.md` containing:
|
||||
- Co zmienione (lista plików + diff highlights)
|
||||
- Akcje post-deploy do wykonania ręcznie na produkcji (kolejność: regen pp_routes → clear cache pp_routes:all → regen google-feed.xml → resubmit GMC)
|
||||
- Test count delta
|
||||
- Decyzje (override /feature-dev, brak redirectów 301)
|
||||
</output>
|
||||
168
.paul/phases/18-google-feed-permutation-url-fix/18-01-SUMMARY.md
Normal file
168
.paul/phases/18-google-feed-permutation-url-fix/18-01-SUMMARY.md
Normal file
@@ -0,0 +1,168 @@
|
||||
---
|
||||
phase: 18-google-feed-permutation-url-fix
|
||||
plan: 01
|
||||
subsystem: feed/routing
|
||||
tags: [google-merchant, pp_routes, permutation, regex, php74]
|
||||
|
||||
requires:
|
||||
- phase: prior-architecture
|
||||
provides: ProductRepository, Helpers::htacces, LayoutEngine, frontAttributePartial
|
||||
provides:
|
||||
- Separator URL permutacji `_` zamiast `/` w feedzie Google
|
||||
- Wzorzec routingu pp_routes obejmujący `[0-9_-]+`
|
||||
- Konwersja `_` → `|` po stronie front przed `findCached`
|
||||
- Preselekcja wartości atrybutu z `permutation_hash` w partialu
|
||||
affects: [google-feed, pp_routes, frontend-product-attributes]
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "URL feedu: jeden segment z `_` zamiast wielu segmentów ze `/`"
|
||||
- "DB format `|`, URL format `_`, konwersja w warstwie front"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- tests/Unit/Shared/Helpers/HelpersRoutingTest.php
|
||||
- tests/Unit/Domain/Product/ProductFeedLinkTest.php
|
||||
modified:
|
||||
- autoload/Domain/Product/ProductRepository.php
|
||||
- autoload/Shared/Helpers/Helpers.php
|
||||
- autoload/front/LayoutEngine.php
|
||||
- templates/shop-product/_partial/product-attribute.php
|
||||
|
||||
key-decisions:
|
||||
- "Separator URL `_` zamiast `/` (one segment dopasowywalny przez pp_routes)"
|
||||
- "Konwersja `_` → `|` w LayoutEngine, format DB pozostaje `|`"
|
||||
- "Brak redirectów 301 — Google sam zaktualizuje feed"
|
||||
- "Brak automatycznych akcji post-deploy — udokumentowane jako manual steps"
|
||||
- "Override /feature-dev (hotfix z konkretną instrukcją)"
|
||||
|
||||
patterns-established:
|
||||
- "Forced value via URL parameters w partialach (preselekcja zamiast is_default)"
|
||||
- "Reflection-based test prywatnych metod XML feedu"
|
||||
|
||||
duration: ~25min
|
||||
completed: 2026-04-30
|
||||
---
|
||||
|
||||
# Phase 18 Plan 01: Google feed permutation URL fix — Summary
|
||||
|
||||
**Linki produktów z permutacją w feedzie Google używają teraz `_` jako separatora par `attr-val`, routing `pp_routes` matchuje takie URL-e, a partial atrybutu preselectuje wartości na podstawie `permutation_hash` z URL.**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~25 min |
|
||||
| Started | 2026-04-30 |
|
||||
| Completed | 2026-04-30 |
|
||||
| Tasks | 3 / 3 |
|
||||
| Files modified | 6 (4 silnik + 2 testy) |
|
||||
| Tests | 834 → 841 (+7) |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Generator linku w feedzie używa `_` | Pass | ProductFeedLinkTest.testCombinationLinkUsesUnderscoreInSeoLinkBranch + fallback + single-pair |
|
||||
| AC-2: Routing `pp_routes` matchuje URL z `_` | Pass | HelpersRoutingTest weryfikuje obecność `[0-9_-]+` w generatorze + preg_match na nowym wzorcu |
|
||||
| AC-3: Front konwertuje `_` z URL na `|` przed zapytaniem | Pass | LayoutEngine.php:196 — zmienna `$permutation_hash` z `str_replace('_','|',...)` |
|
||||
| AC-4: Partial preselectuje wartość z URL | Pass | `$forced_value_id` + `$is_active` używane w `checked` i `<script>` |
|
||||
| AC-5: Pełna suita testów zielona | Pass | PHPUnit: 841 tests, 2330 assertions, 0.764s |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Naprawa krytycznego problemu komercyjnego: feed Google prowadził klientów na home zamiast na produkt
|
||||
- Spójność stosu: separator URL (`_`) ↔ format DB (`|`) z jasnym punktem konwersji w warstwie front
|
||||
- 7 nowych testów (4 routing + 3 generator linku) — pełne pokrycie zmiany
|
||||
- Reflection-based test metody prywatnej `appendCombinationToXml` z mockiem Medoo i mockiem TransportRepository
|
||||
- UI strony produktu wchodząc z linka feedu pokazuje wybraną kombinację atrybutów (zamiast `is_default`)
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `autoload/Domain/Product/ProductRepository.php` | Modified (×2) | `appendCombinationToXml`: separator `/` → `_` w obu gałęziach (seo_link i fallback) |
|
||||
| `autoload/Shared/Helpers/Helpers.php` | Modified (×2) | Generator pp_routes: regex `[0-9-]+` → `[0-9_-]+` w obu wariantach |
|
||||
| `autoload/front/LayoutEngine.php` | Modified | Wyciągnięcie `$permutation_hash` z konwersją `_` → `|` przed `findCached` |
|
||||
| `templates/shop-product/_partial/product-attribute.php` | Modified | `$forced_value_id` z URL + `$is_active` w `checked`/`<script>` |
|
||||
| `tests/Unit/Shared/Helpers/HelpersRoutingTest.php` | Created | 4 testy regex routingu (file content + preg_match dla `_` i odrzucenia `/`) |
|
||||
| `tests/Unit/Domain/Product/ProductFeedLinkTest.php` | Created | 3 testy `appendCombinationToXml` via Reflection (seo_link, fallback, single-pair) |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| Separator URL `_` zamiast `/` | `_` mieści się w jednym segmencie regex `[0-9_-]+`; `/` wymagałby zmiany struktury routingu | Czysty fix, minimalna zmiana w pp_routes |
|
||||
| Format DB pozostaje `|` | Nie tykać zapisanych danych w `pp_shop_product_combinations.permutation_hash` | Zero migracji DB; konwersja tylko w warstwie I/O |
|
||||
| Brak redirectów 301 | Stare URL-e z feedu wymarły gdy GMC zaciągnie nowy feed | Mniej kodu w `.htaccess`, brak długoterminowego balastu |
|
||||
| Brak automatycznych akcji post-deploy | Hotfix dotyczy tylko silnika; regen routes/cache/feed są zależne od środowiska | Wymaga manualnego runbook'a (poniżej) |
|
||||
| Override /feature-dev | Hotfix z konkretną instrukcją od użytkownika, jak fazy 15/16/17 | Skill audit logged |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Summary
|
||||
|
||||
| Type | Count | Impact |
|
||||
|------|-------|--------|
|
||||
| Auto-fixed | 0 | — |
|
||||
| Scope additions | 0 | — |
|
||||
| Deferred | 0 | — |
|
||||
|
||||
**Total impact:** Zero. Plan wykonany dokładnie według instrukcji.
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
None.
|
||||
|
||||
### Deferred Items
|
||||
|
||||
None.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
| Issue | Resolution |
|
||||
|-------|------------|
|
||||
| `ProductRepository::appendCombinationToXml` jest `private` z zależnościami od `transportRepoForXml` i `AttributeRepository` (DB) | Test via `ReflectionMethod`; mock Medoo (`select` → `[]`, `get` → `null`); wstrzyknięty mock `TransportRepository::lowestTransportPrice` zwracający `0.0` na dynamicznej property `transportRepoForXml` |
|
||||
| Brak istniejącego folderu `tests/Unit/Shared/Helpers/` | Utworzony nowy katalog + `HelpersRoutingTest.php` |
|
||||
| `Helpers::htacces()` zbyt rozległe do testu E2E (DB writes, file I/O) | Test pośredni: assercje na zawartości pliku Helpers.php (file_get_contents) + standalone `preg_match` na sample patternie |
|
||||
|
||||
## Post-deploy runbook (manual, kolejność krytyczna)
|
||||
|
||||
Wymagane akcje na środowisku produkcyjnym po deployu kodu:
|
||||
|
||||
1. **Regeneracja `pp_routes`** — wywołać `Helpers::htacces()` (np. zapis ustawień w adminie lub regeneracja sitemap), żeby nowe wzorce z `_` trafiły do bazy. Bez tego stare wzorce `[0-9-]+` w `pp_routes` nadal nie zmatchują URL z `_`.
|
||||
2. **Wyczyścić cache routingu** — skasować klucz `pp_routes:all` w Redis (`DEL pp_routes:all`) albo poczekać 24h na expiry. `index.php:63` cachuje routing.
|
||||
3. **Regeneracja feedu Google** — uruchomić cron `cron/cron-xml.php` (`\admin\factory\ShopProduct::generate_google_feed_xml()`), żeby `google-feed.xml` zawierał nowe linki z `_`.
|
||||
4. **Resubmit feedu w GMC** — automatycznie wg harmonogramu lub ręcznie "Fetch now".
|
||||
5. **Stare URL-e w GMC** — same wypadną z indeksu po podmianie feedu (Google).
|
||||
|
||||
Walidacja po deployu:
|
||||
- `https://domena/google-feed.xml` — tagi `<link>` zawierają `_` zamiast `/` między parami
|
||||
- `https://domena/slug-produktu/20-170_21-175` — ładuje produkt z preselectowaną kombinacją (nie home)
|
||||
- GMC: feed bez błędów "Landing page error"
|
||||
|
||||
## Skill Audit (Phase 18)
|
||||
|
||||
| Expected | Invoked | Notes |
|
||||
|----------|---------|-------|
|
||||
| /feature-dev | ○ | User-approved override (hotfix z konkretną instrukcją) |
|
||||
| /koniec-pracy | ○ | Pending — uruchomić przy zakończeniu sesji jeśli release wchodzi do update package |
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Hotfix completed; pełna suita zielona
|
||||
- Brak zmian w schemacie DB
|
||||
- Wzorzec preselekcji partial z URL parameter dostępny dla innych partiali (jeśli pojawi się potrzeba)
|
||||
|
||||
**Concerns:**
|
||||
- Akcje post-deploy (regen routes / clear cache / regen feed) wymagają manualnego wykonania — brak automatu
|
||||
- Klienci sklepu mający własne nadpisane `templates_user/shop-product/_partial/product-attribute.php` muszą zaaplikować zmianę u siebie (Tpl::view priorytetuje `templates_user/`)
|
||||
|
||||
**Blockers:**
|
||||
- None.
|
||||
|
||||
---
|
||||
*Phase: 18-google-feed-permutation-url-fix, Plan: 01*
|
||||
*Completed: 2026-04-30*
|
||||
Reference in New Issue
Block a user