11 KiB
11 KiB
phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, duration, started, completed
| phase | plan | subsystem | tags | requires | provides | affects | tech-stack | key-files | key-decisions | patterns-established | duration | started | completed | |||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 06-xml-feed-import | 01 | products |
|
|
|
|
|
|
|
|
~50min | 2026-04-30T00:00:00Z | 2026-04-30T01:00:00Z |
Phase 6 Plan 01: XML Feed Import Summary
Klient moze podac URL feedu XML w panelu edycji; cron_universal pobiera go strumieniowo (XMLReader, batche 200 w transakcjach), wzbogaca tabele products o title/description/price/custom_label_1, a schemat products zostal rozdzielony na pola zrodlowe (title/description) i edytowalne dla supplemental feed (title_gmc/description_gmc).
Performance
| Metric | Value |
|---|---|
| Duration | ~50 min |
| Started | 2026-04-30T00:00:00Z |
| Completed | 2026-04-30T01:00:00Z |
| Tasks | 4/4 auto + 1 checkpoint approved |
| Files modified | 9 |
Acceptance Criteria Results
| Criterion | Status | Notes |
|---|---|---|
| AC-1: Schemat bazy danych | Pass | Migracja 029 idempotentna; kolumny title/title_gmc/description_gmc/price + xml_feed_url/xml_feed_last_sync_at; INDEX zamiast UNIQUE z powodu legacy duplikatow |
| AC-2: Edycja klienta — pole feed XML | Pass | Pole "XML Feed URL" w dialogu edycji + walidacja FILTER_VALIDATE_URL + prefill z data.xml_feed_url |
| AC-3: Cron pobiera feed XML i wzbogaca products | Pass | XmlFeedImporter::import_for_client wywolywany w cron_universal; manual upsert (SELECT IN + UPDATE/INSERT); transakcje per batch; ON ERROR pojedynczego itemu - skip+log |
| AC-4: Refaktor odwolan do name/title | Pass | Wszystkie p.name -> p.title; p.title -> p.title_gmc; aliasy AS name zachowane; is_product_core_field ma title_gmc/description_gmc; lint PHP czysty |
Accomplishments
- Schemat products przemodelowany na czysty source/edit split:
title+descriptionto zrodlowe pola wypelniane przez feed XML lub pierwszy fetch GA;title_gmc+description_gmcto pola edytowane przez UI/AI i wysylane do GMC przez SupplementalFeed. - Odporny importer feedu XML — XMLReader streaming bez ladowania calego pliku do pamieci, batche po 200 pozycji w transakcjach, manual upsert kompatybilny z legacy duplikatami
(client_id, offer_id), set_time_limit(600), memory_limit 512M, gc_collect_cycles co batch. - UI edycji klienta uzupelnione o pole
XML Feed URL(typurl, walidacja serwerowa) bez nowych zaleznosci frontendowych. - Cron_universal automatycznie pobiera feed po sync produktow GA dla kazdego klienta z ustawionym
xml_feed_url; raportxml_feed: { fetched, updated, inserted, skipped, peak_memory_mb, duration_ms }dolaczony do response cron-a.
Files Created/Modified
| File | Change | Purpose |
|---|---|---|
migrations/029_products_rename_columns_and_xml_feed.sql |
Created | Idempotentny rename name->title, title->title_gmc + description_gmc + price + xml_feed_url/xml_feed_last_sync_at + INDEX (client_id, offer_id) |
autoload/services/class.XmlFeedImporter.php |
Created | XMLReader streaming parser + manual batched upsert (SELECT IN -> UPDATE/INSERT) + cURL streaming download |
autoload/factory/class.Products.php |
Modified | SQL rename p.name->p.title, p.title->p.title_gmc; is_product_core_field z title_gmc/description_gmc; aliasy AS name zachowane |
autoload/controls/class.Products.php |
Modified | get_product_data dla edycji ('title_gmc', 'description_gmc'); set_product_data zapisuje do _gmc; mapowanie $row['title']/$row['title_gmc'] |
autoload/controls/class.Cron.php |
Modified | Hook XmlFeedImporter w cron_universal; SELECTy/INSERTy products zaktualizowane; raport xml_feed w response |
autoload/controls/class.Clients.php |
Modified | save() przyjmuje xml_feed_url + walidacja FILTER_VALIDATE_URL |
autoload/services/class.SupplementalFeed.php |
Modified | TSV czyta z title_gmc AS title, description_gmc AS description |
templates/clients/main_view.php |
Modified | Pole "XML Feed URL" w dialogu edycji + prefill JS |
api.php |
Modified | Wszystkie SQL na products zaktualizowane (p.name->p.title, p.title->p.title_gmc); aliasy AS name/AS title zachowane jako kontrakt API; set_product_data('title', ...) -> 'title_gmc' (post-fix po uwadze uzytkownika) |
Decisions Made
| Decision | Rationale | Impact |
|---|---|---|
| title/description = zrodlo, title_gmc/description_gmc = edycja | Korekta semantyki w trakcie checkpointa - pierwotna interpretacja byla odwrotna. Edytor pisze do _gmc, supplemental feed czyta z _gmc i pcha do GMC | Cala logika edycyjna i sync GMC zostaly zaktualizowane konsekwentnie; XmlFeedImporter pisze do title/description |
| INDEX zamiast UNIQUE na (client_id, offer_id) | Istniejace dane mialy duplikaty (np. '2-1625' x N), UNIQUE rzucal 1062 IntegrityViolation | Manual upsert w PHP zamiast ON DUPLICATE KEY UPDATE; legacy duplikaty sa wszystkie aktualizowane (lista id z SELECT IN) |
| Manual SELECT IN + UPDATE/INSERT per batch | Konsekwencja braku UNIQUE; daje pelna kontrole nad obsluga duplikatow | Jeden SELECT + N UPDATE/INSERT per batch w transakcji - wciaz wydajne dla 5000+ pozycji |
Aliasy ... AS name w SELECTach zachowane |
Kontrakt z DataTables/JS w templates/products | Brak zmian frontendu; minimalny blast radius |
Deviations from Plan
Summary
| Type | Count | Impact |
|---|---|---|
| Auto-fixed | 3 | Konieczne korekty - migracja UNIQUE -> INDEX, semantyka pol, brakujacy api.php |
| Scope additions | 0 | Brak |
| Deferred | 1 | Backfill description -> description_gmc nieobligatoryjny |
Total impact: Korekty kierunkowe (semantyka pol + UNIQUE -> INDEX), bez scope creep.
Auto-fixed Issues
3. [Coverage] api.php nie byl uwzgledniony w pierwotnym refaktorze
- Found during: post-checkpoint pytanie uzytkownika ("Czy w api.php rowniez poprawiles nazwy kolumn?")
- Issue: Plan boundaries i grep nie obejmowaly
api.php- 4 zapytania SQL naproductszp.name/p.title, orazset_product_data($product['id'], 'title', ...)w endpointcie product_title_set - Fix: Wszystkie zapytania zaktualizowane analogicznie do reszty kodu:
p.name->p.title,p.title->p.title_gmc, aliasyAS name/AS titledodane gdzie publiczny kontrakt API (offer_id/get_default_titles endpointy);set_product_datazapisuje teraz dotitle_gmc - Files:
api.php - Verification: lint PHP czysty; grep
p\.name\|p\.title\bzwraca tylko legalne aliasy
1. [Schema] UNIQUE INDEX (client_id, offer_id) blokowal migracje przez legacy duplikaty
- Found during: human-verify checkpoint (php install.php zwrocil SQLSTATE 23000 / 1062 'Duplicate entry 2-1625')
- Issue: Istniejace dane w
productsmialy wiele wierszy z tym samym(client_id, offer_id) - Fix: Zamiana na non-unique INDEX
idx_products_client_offer+ przepisanie XmlFeedImporter::flush_batch zON DUPLICATE KEY UPDATEna manual SELECT-then-UPDATE/INSERT (aktualizuje WSZYSTKIE legacy duplikaty per offer_id) - Files:
migrations/029_products_rename_columns_and_xml_feed.sql,autoload/services/class.XmlFeedImporter.php - Verification:
php install.phpprzechodzi; importer testowo dziala na klientach z xml_feed_url
2. [Semantics] Inwersja zrodlo vs edycja w polach title/description
- Found during: uwaga uzytkownika podczas Task 4 ("title/description = zrodlo, title_gmc/description_gmc = edycja")
- Issue: Pierwotny plan zaklada title=glowny display, title_gmc=zrodlo z feedu - co odwrocilo by przeplyw
- Fix: Flip: XmlFeedImporter pisze do title/description; is_product_core_field = title_gmc/description_gmc; SupplementalFeed czyta z _gmc; controls/Products.php: edycja zapisuje do _gmc; AI prompt context czyta z _gmc
- Files:
autoload/factory/class.Products.php,autoload/controls/class.Products.php,autoload/services/class.SupplementalFeed.php,autoload/services/class.XmlFeedImporter.php - Verification: lint PHP czysty wszedzie; flow source -> edit -> GMC zgodny z modelem mentalnym
Deferred Items
- Backfill istniejacych edycji do _gmc: stare wartosci
description(mieszane: GMC source + uzytkowe edycje) pozostaja wdescription. Jesli historyczne edycje powinny migrowac dodescription_gmc, wymagany jest dodatkowyUPDATE products SET description_gmc = description WHERE description_gmc IS NULL. Pozostawione decyzji uzytkownika - brak autoryzacji w planie.
Issues Encountered
| Issue | Resolution |
|---|---|
php install.php -> SQLSTATE 23000 (1062) na uk_products_client_offer |
Zamiana UNIQUE -> non-unique INDEX + manual upsert w PHP |
| Niejednoznacznosc semantyki title vs title_gmc po pierwszym renamie | Korekta po uwadze uzytkownika - flip: title=zrodlo, _gmc=edycja, sciagniete na cale stos |
Next Phase Readiness
Ready:
- Schemat
productsrozdziela source/edit - przyszle integracje feedow (np. inne formaty, multi-feed) maja czytelny target \services\XmlFeedImporterjako referencyjny wzorzec importera feedow (XMLReader + batched transactions)- Cron pipeline ma jednolity hook point dla wzbogacania danych produktow
Concerns:
- Legacy duplikaty
(client_id, offer_id)wproductsnie sa rozwiazywane przez ten plan - importer aktualizuje wszystkie wiersze z danym offer_id, ale uklad bazy pozostaje "smieciowy". Przyszle zadanie deduplikacji powinno przeniesc historie/agregaty na pojedynczy id przed wymuszeniem UNIQUE. - Pole
descriptionistnialo wczesniej i moglo zawierac edycje uzytkownika - po nowej semantyce te dane reprezentuja "zrodlo", co moze byc mylace dla starych klientow. Backfill -> description_gmc jest deferred.
Blockers: None.
Phase: 06-xml-feed-import, Plan: 01 Completed: 2026-04-30