07-01: Performance — N+1 subqueries, information_schema static, DB indexes 07-02: Stability — SSL verification (4 clients), cron throttle→DB, migration 000014b 07-03: UX — orderpro_to_allegro disable, orders list items 14-17 07-04: Tests — AllegroTokenManager + AllegroOrderImportService unit tests 07-05: InPost ShipmentProviderInterface (replaces allegro_wza workaround) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
9.0 KiB
9.0 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous
| phase | plan | type | wave | depends_on | files_modified | autonomous | ||
|---|---|---|---|---|---|---|---|---|
| 07-pre-expansion-fixes | 01 | execute | 1 |
|
true |
Purpose
Lista zamówień jest głównym widokiem aplikacji. Przy 50 wierszach na stronę, correlated subqueries oznaczają 200+ dodatkowych zapytań per page load. canResolveMappedMedia() uderza w information_schema przy każdym żądaniu do OrdersRepository — notoriously slow na MySQL. Oba problemy narastają z liczbą zamówień.
Output
OrdersRepository::buildListSql()— 4 subqueries zastąpione aggregating LEFT JOINOrdersRepository::canResolveMappedMedia()— wynik cachowany wstaticproperty- Nowa migracja z brakującymi indeksami dla
orderstable
Source Files
@src/Modules/Orders/OrdersRepository.php
<acceptance_criteria>
AC-1: Brak correlated subqueries w liście zamówień
Given OrdersRepository::buildListSql() zawiera 4 correlated subqueries (items_count, items_qty, shipments_count, documents_count)
When metoda buduje SQL dla listy zamówień
Then SQL nie zawiera "(SELECT COUNT(*) FROM order_items" ani podobnych correlated subqueries
AND zwracane kolumny items_count, items_qty, shipments_count, documents_count nadal istnieją
AND logika transformOrderRow() w pełni działa (te pola nadal trafiają do wyniku)
AC-2: information_schema nie jest odpytywany per-request
Given canResolveMappedMedia() używa instance property $this->supportsMappedMedia jako cache
When OrdersRepository jest instanciowany dwukrotnie w tej samej sesji PHP (np. dwa wywołania repository)
Then information_schema.COLUMNS jest zapytany co najwyżej raz na cykl PHP (static property)
AC-3: Brakujące indeksy dodane migracją
Given tabela orders nie ma indeksów na kolumnach source, external_status_id, ordered_at
When tworzona jest nowa migracja i wykonana przez migrator
Then tabela orders ma indeksy na: source, external_status_id, ordered_at
AND migracja jest idempotentna (IF NOT EXISTS lub ADD INDEX IF NOT EXISTS lub ALTER IGNORE)
</acceptance_criteria>
Task 1: Zamień 4 correlated subqueries na aggregating LEFT JOINs w buildListSql() src/Modules/Orders/OrdersRepository.php W metodzie `buildListSql()` (linie ~135-171) zamień: ```sql (SELECT COUNT(*) FROM order_items oi WHERE oi.order_id = o.id) AS items_count, (SELECT COALESCE(SUM(oi.quantity), 0) FROM order_items oi WHERE oi.order_id = o.id) AS items_qty, (SELECT COUNT(*) FROM order_shipments sh WHERE sh.order_id = o.id) AS shipments_count, (SELECT COUNT(*) FROM order_documents od WHERE od.order_id = o.id) AS documents_count ``` Na: ```sql COALESCE(oi_agg.items_count, 0) AS items_count, COALESCE(oi_agg.items_qty, 0) AS items_qty, COALESCE(sh_agg.shipments_count, 0) AS shipments_count, COALESCE(od_agg.documents_count, 0) AS documents_count ``` I dodaj do klauzuli FROM (po istniejących LEFT JOINach, przed WHERE): ```sql LEFT JOIN ( SELECT order_id, COUNT(*) AS items_count, COALESCE(SUM(quantity), 0) AS items_qty FROM order_items GROUP BY order_id ) oi_agg ON oi_agg.order_id = o.id LEFT JOIN ( SELECT order_id, COUNT(*) AS shipments_count FROM order_shipments GROUP BY order_id ) sh_agg ON sh_agg.order_id = o.id LEFT JOIN ( SELECT order_id, COUNT(*) AS documents_count FROM order_documents GROUP BY order_id ) od_agg ON od_agg.order_id = o.id ``` UWAGA: buildListSql() generuje SQL jako string — upewnij się że nowe JOINy są wstawione przed `$whereSql` (który jest konkatenowany na końcu z klauzulą WHERE).Sprawdź jak wygląda $whereSql — czy zawiera "WHERE" czy tylko "AND ..."?
Jeśli WHERE jest w $whereSql, nowe JOINy idą bezpośrednio przed nim.
NIE zmieniaj metody transformOrderRow() — klucze tablicy pozostają te same.
php -l src/Modules/Orders/OrdersRepository.php
grep -c "SELECT COUNT\|SELECT COALESCE\|subquery" src/Modules/Orders/OrdersRepository.php
# Powinno zwrócić 0 dla wzorców correlated subquery w buildListSql
AC-1 satisfied: buildListSql() używa LEFT JOIN zamiast correlated subqueries
Task 2: Zamień instance property na static w canResolveMappedMedia()
src/Modules/Orders/OrdersRepository.php
Znajdź deklarację instance property (okolice klasy):
```php
private ?bool $supportsMappedMedia = null;
```
Zmień na:
```php
private static ?bool $supportsMappedMedia = null;
```
W metodzie `canResolveMappedMedia()` zmień referencje z `$this->supportsMappedMedia` na
`self::$supportsMappedMedia` (we wszystkich miejscach metody — przypisanie i odczyt).
To jedyna zmiana. Nie modyfikuj logiki zapytania ani żadnej innej metody.
php -l src/Modules/Orders/OrdersRepository.php
grep -n "supportsMappedMedia" src/Modules/Orders/OrdersRepository.php
# Powinno pokazać: private static ?bool i self::$supportsMappedMedia
AC-2 satisfied: canResolveMappedMedia() używa static property
Task 3: Migracja z brakującymi indeksami dla tabeli orders
database/migrations/20260313_000048_add_orders_performance_indexes.sql
Stwórz plik `database/migrations/20260313_000048_add_orders_performance_indexes.sql`.
Przed pisaniem migracji, sprawdź jakie indeksy już istnieją:
```bash
grep -n "INDEX\|KEY " database/migrations/20260302_000018_create_orders_tables_and_schedule.sql
grep -rn "ADD INDEX\|ADD KEY" database/migrations/ | grep -i "order"
```
Migracja powinna dodać brakujące indeksy:
```sql
-- Indeksy dla typowych filtrów i sortowań na liście zamówień
ALTER TABLE orders
ADD INDEX IF NOT EXISTS orders_source_idx (source),
ADD INDEX IF NOT EXISTS orders_external_status_idx (external_status_id),
ADD INDEX IF NOT EXISTS orders_ordered_at_idx (ordered_at),
ADD INDEX IF NOT EXISTS orders_source_status_idx (source, external_status_id);
-- Indeks dla allegro status mapping lookup (JOIN w buildListSql)
ALTER TABLE allegro_order_status_mappings
ADD INDEX IF NOT EXISTS allegro_status_code_idx (allegro_status_code)
-- tylko jeśli ten indeks nie istnieje (sprawdź migration 000038)
```
WAŻNE: Sprawdź aktualny schemat przed dodaniem indeksu — nie twórz duplikatów.
Użyj `IF NOT EXISTS` gdzie MySQL to obsługuje (MySQL 8.0+), albo sformułuj jako
osobne polecenia z IGNORE: `ALTER TABLE orders ADD IGNORE INDEX ...`
Sprawdź też `database/migrations/20260308_000038_ensure_order_status_mappings_table.sql`
aby wiedzieć jakie indeksy allegro_order_status_mappings już ma.
php -l database/migrations/20260313_000048_add_orders_performance_indexes.sql 2>/dev/null || echo "SQL file - no php lint needed"
# Sprawdź czy plik istnieje i nie jest pusty
cat database/migrations/20260313_000048_add_orders_performance_indexes.sql
AC-3 satisfied: migracja z indeksami na source, external_status_id, ordered_at istnieje
DO NOT CHANGE
- Logika filtrowania ($whereSql) — tylko zmiana mechanizmu agregacji, nie filtrów
- Metoda transformOrderRow() — zwracane klucze muszą pozostać identyczne
- Inne metody OrdersRepository poza buildListSql() i canResolveMappedMedia()
- Istniejące pliki migracji — tylko NOWY plik 000048
SCOPE LIMITS
- Tylko optymalizacja zapytania list; nie zmieniamy zapytań detail/find
- Nie dodajemy nowych kolumn ani nie zmieniamy schematu tabel — tylko indeksy
- Nie implementujemy cache'owania na poziomie Redis/Memcached — tylko static property
<success_criteria>
- 4 correlated subqueries usunięte z buildListSql()
- canResolveMappedMedia() z static property
- Migracja 000048 z indeksami na source, external_status_id, ordered_at
- Zero błędów składniowych PHP </success_criteria>