Files
adsPRO/.paul/phases/06-xml-feed-import/06-01-SUMMARY.md
2026-04-30 01:04:06 +02:00

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
xml-feed
gmc
products
schema-rename
cron
supplemental-feed
phase provides
02-supplemental-feed-cl1 SupplementalFeed::generate_for_client (TSV out -> GMC)
phase provides
04-products-aggregate products_aggregate scope, p.title/p.name display fallback
clients.xml_feed_url + xml_feed_last_sync_at
products schema split
title/description (zrodlo) vs title_gmc/description_gmc (edycja)
products.price (DECIMAL z feedu)
\services\XmlFeedImporter (XMLReader streaming + batched manual upsert)
cron_universal hook na import feedu
products UI
AI suggestions modal
supplemental feed generation
added patterns
XMLReader streaming
manual SELECT-then-UPDATE/INSERT upsert
Idempotentne migracje rename via PREPARE/EXECUTE z guardem INFORMATION_SCHEMA
Source/edit field separation (X / X_gmc) z SupplementalFeed czytajacym _gmc
created modified
migrations/029_products_rename_columns_and_xml_feed.sql
autoload/services/class.XmlFeedImporter.php
autoload/factory/class.Products.php
autoload/controls/class.Products.php
autoload/controls/class.Cron.php
autoload/controls/class.Clients.php
autoload/services/class.SupplementalFeed.php
templates/clients/main_view.php
api.php
title/description = ZRODLO (feed XML lub pierwszy fetch GA), title_gmc/description_gmc = EDYTOWALNE (output do GMC supplemental)
INDEX (client_id, offer_id) bez UNIQUE - istniejace duplikaty legacy uniemozliwiaja UNIQUE
Manual upsert (SELECT IN + UPDATE/INSERT) zamiast ON DUPLICATE KEY UPDATE - zgodne z brakiem UNIQUE
Aliasy AS name w SELECTach zachowane - kontrakt JS/DataTables nieruszony
Source vs Edit field naming: X dla zrodla, X_gmc dla edycji wysylanej do Merchant Center
Importery zewnetrznych feedow: XMLReader + batche w transakcjach + tempfile, set_time_limit(600), memory_limit 512M, gc co batch
~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+description to zrodlowe pola wypelniane przez feed XML lub pierwszy fetch GA; title_gmc+description_gmc to 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 (typ url, walidacja serwerowa) bez nowych zaleznosci frontendowych.
  • Cron_universal automatycznie pobiera feed po sync produktow GA dla kazdego klienta z ustawionym xml_feed_url; raport xml_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 na products z p.name/p.title, oraz set_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, aliasy AS name/AS title dodane gdzie publiczny kontrakt API (offer_id/get_default_titles endpointy); set_product_data zapisuje teraz do title_gmc
  • Files: api.php
  • Verification: lint PHP czysty; grep p\.name\|p\.title\b zwraca 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 products mialy wiele wierszy z tym samym (client_id, offer_id)
  • Fix: Zamiana na non-unique INDEX idx_products_client_offer + przepisanie XmlFeedImporter::flush_batch z ON DUPLICATE KEY UPDATE na 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.php przechodzi; 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 w description. Jesli historyczne edycje powinny migrowac do description_gmc, wymagany jest dodatkowy UPDATE 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 products rozdziela source/edit - przyszle integracje feedow (np. inne formaty, multi-feed) maja czytelny target
  • \services\XmlFeedImporter jako referencyjny wzorzec importera feedow (XMLReader + batched transactions)
  • Cron pipeline ma jednolity hook point dla wzbogacania danych produktow

Concerns:

  • Legacy duplikaty (client_id, offer_id) w products nie 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 description istnialo 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