Signal handler obdrží route parametr místo POST těla při kolizi názvů (AJAX)

SamuelThorn
Člen | 45
+
0
-

Předem upozorňuju, že následujcí psal Claude. Ale chtěl jsem o tom minimálně informovat a zjistit, jestli je to opravdu chyba, nebo zamýšlené chování.

Narazil jsem na chování, které mě zaskočilo, a chci ověřit, zda jde o záměr nebo bug.

Situace
Mám presenter s routou <eventKey>[/<year>] a signal handler:

  public function handleSyncBookmarks(string $eventKey = '', ...): void

JS volá endpoint přes fetch s hlavičkou X-Requested-With: XMLHttpRequest a v POST těle posílá eventKey=ff-2026. URL požadavku je /akce/ff/2026/?do=syncBookmarks – tedy route parametr eventKey je ff (bez roku).

Problém
Handler dostane $eventKey = 'ff' (z routy), nikoli 'ff-2026' z POST těla.

Příčina
V Presenter::initGlobalParameters() (řádek ~1057):

  $params = $request->getParameters(); // route params: ['eventKey' => 'ff', ...]
  // ...
  } elseif ($this->isAjax()) {
      $params += $request->getPost();  // += nepřepíše existující klíče!
  }

Operátor += nepřepisuje existující klíče, takže route parametr eventKey='ff' vyhraje nad POST parametrem eventKey='ff-2026'.

Otázka
Je toto záměrné chování? Intuitivně bych čekal, že POST tělo má u AJAXového signálu přednost před route parametry, nebo alespoň že při kolizi názvů dojde k přepsání (jako u array_merge).

Workaround
Odstranil jsem $eventKey z parametrů handleru a klíč odvodím server-side přímo z presenteru ($this->event->getEventKey()). Funguje, ale pro vývojáře to není zřejmé – pojmenovat parametr signálu stejně jako route parametr je přirozené a chyba se projeví tiše, jen špatnými daty.

Editoval SamuelThorn (30. 6. 19:39)

m.brecher
Generous Backer | 926
+
+1
-

@SamuelThorn

Je toto záměrné chování? Intuitivně bych čekal, že POST tělo má u AJAXového signálu přednost před route parametry, nebo alespoň že při kolizi názvů dojde k přepsání (jako u array_merge).

Signály Nette jsou vynikající na jednoduché použití přímo v presenteru, bez psaní komponenty. Tento pohodlný přístup má svoje hranice – malé riziko přepsání GET parametrů akce presenteru GET parametrem signálu. Nette sice odděluje parametry signálu prefixem komponenty od parametrů akce presenteru, ale to platí pouze pokud je signál v komponentě! Pokud je signál rovnou v presenteru, tak ke kolizi dojít může.

Claude AI provedl důkladnou analýzu a došel k závěru, že pokud napíšeš v Latte link na signál s explicitním GET parametrem, pak Nette použije hodnotu parametru ze signálu a ignoruje hodnotu parametru z akce. Přednost má explicitní hodnota před implicitní, což je správně. Výsledné chování ale není uspokojivé, protože si přepíšeš parametr akce, proto si musíš sám ohlídat, aby parametry akce a signálu nebyly stejné!!!

U signálů Nette nerozlišuje zda jsou parametry doručeny jako GET nebo POST a jak Jsi sám objevil, POST parametry nepřepisují již existující GET parametry, to bych řekl, že je pragmatický kompromis, ale výsledek není uspokojivý – POST parametr se zahodí. Tyto problémy nebudou existovat, pokud budeš mít signál v samostatné komponentě mimo presenter a do presenteru vložíš komponentu.

Jedno řešení by bylo na úrovni Nette – doplnit prefixování parametrů signálů presenteru, tak aby Nette dokázalo odlišit parametry akce od parametrů signálu. Asi by se mělo jít touto cestou, je to systémové a elegantní.

Druhá možnost je napsat příslušný odstavec do dokumentace, vysvětlit jak dochází ke kolizím parametrů signálu který je přímo v presenteru. Doporučit nepoužívat stejná jména parametrů v akci a v signálu, nebo použít signál v samostatné komponentě.

Editoval m.brecher (3. 7. 2:07)

SamuelThorn
Člen | 45
+
0
-

Děkuju za odpověď. Je to asi hodně okrajová záležitost, ale dokázala potrápit, i Clauda. Hledal to asi hodinu.

m.brecher
Generous Backer | 926
+
0
-

@SamuelThorn

Mám hotovou opravu, není komplikovaná, mělo by se to opravit, protože právě tento typ podivných chování stojí vývojáře spoustu času navíc.

Editoval m.brecher (4. 7. 0:57)