|
| 1 | +# Umsetzungskonzept: Referer-Handling im dc-general (Contao 5.7) |
| 2 | + |
| 3 | +> Status: **alle Schritte 1–5 erledigt.** Static Analysis (Psalm + phpcs PSR12) und |
| 4 | +> End-to-End-Klicktest (Playwright) grün. |
| 5 | +> |
| 6 | +> - [x] 1 – Backend-Test des 5.7-Ist-Verhaltens (Anhang A) |
| 7 | +> - [x] 2 – `ViewHelpers::getBackUrl()` eingeführt, `redirectHome/redirectCleanHome` darauf umgestellt |
| 8 | +> - [x] 3 – Call-Sites umgestellt: EditMask (saveNclose/saveNback), AbstractPropertyOverrideEditAllHandler, BackButtonListener, SelectHandler, ShowHandler + Show-Template |
| 9 | +> - [x] 4 – `StoreRefererListener` + Service entfernt; `_dcg_referer_update` aus metamodels/core routing.yml entfernt (verifiziert: Service weg, Route-Defaults bereinigt) |
| 10 | +> - [x] 5 – Psalm (`--no-cache`) + phpcs PSR12 sauber; Playwright-Klicktest grün (Anhang B) |
| 11 | +> |
| 12 | +> Entscheidung: `GetReferrerEvent` **ersatzlos** aus dem DCG-Navigationspfad genommen |
| 13 | +> (Event bleibt in events-contao-bindings bestehen, wird von DCG nur nicht mehr genutzt). |
| 14 | +
|
| 15 | +## 1. Ausgangslage / Ursache |
| 16 | + |
| 17 | +Contao 5.7 hat `System::getReferer()` intern umgestellt: Es liest **nicht mehr die |
| 18 | +Session** (`session['referer'][refererId]`), sondern baut den Pfad über den neuen |
| 19 | +Service `contao.data_container.dca_url_analyzer` (`getTrail()`) aus DCA-Metadaten, |
| 20 | +echten DB-Records, `ptable` und Standard-Sorting-Modi auf. |
| 21 | + |
| 22 | +**Konsequenz:** |
| 23 | + |
| 24 | +- `StoreRefererListener` schreibt eine Session, die **niemand mehr liest** → |
| 25 | + funktionsloses Altlast-Objekt. Entfernen ist funktional risikolos. |
| 26 | +- `DcaUrlAnalyzer` ist auf `DC_Table`-Konventionen gebaut → für dc-generals eigene |
| 27 | + Data-Provider und dynamische MetaModels-Tabellen (`mm_*`) **nicht verlässlich**. |
| 28 | + Deshalb erzeugt DCG seine Back-URLs künftig selbst. |
| 29 | + |
| 30 | +## 2. Fundament existiert bereits |
| 31 | + |
| 32 | +`ViewHelpers::redirectHome()/redirectCleanHome()` → `determineNewStyleRedirect()` |
| 33 | +baut die "zurück zur Liste"-URL bereits **deterministisch aus dem aktuellen Request** |
| 34 | +(`_route` + `_route_params` + Query, ohne `act`) und hat einen Legacy-Fallback |
| 35 | +(`contao?do=…&table=…[&pid=…]`). Das ist im Kern der gewünschte "DCG-eigene Trail" — |
| 36 | +nur an einen `never`-Redirect gekoppelt und ohne URL-String-Rückgabe für Buttons/Links. |
| 37 | + |
| 38 | +## 3. Kernidee: URL-Builder zentralisieren |
| 39 | + |
| 40 | +Neue, wiederverwendbare Methode in `ViewHelpers`, die die URL **zurückgibt** statt zu |
| 41 | +redirecten: |
| 42 | + |
| 43 | +```php |
| 44 | +public static function getBackUrl( |
| 45 | + EnvironmentInterface $environment, |
| 46 | + array $cleanNames = [], |
| 47 | + ?string $targetProvider = null // für saveNback = Parent-Provider |
| 48 | +): string |
| 49 | +``` |
| 50 | + |
| 51 | +Kapselt **beide** Zweige aus der heutigen `determineNewStyleRedirect`/ |
| 52 | +`determineLegacyRedirect`-Logik: |
| 53 | + |
| 54 | +- **New-Style** (eigene MM-Route, `_route !== 'contao_backend'`): |
| 55 | + `router->generate(routeName, params)` mit bereinigten Parametern. |
| 56 | +- **Legacy** (`contao_backend`): `contao?do=…&table=…[&pid=…]`. |
| 57 | + |
| 58 | +Parameter-Bereinigung fürs Listen-Ziel: `act` **und** `id` entfernen, `cleanNames` |
| 59 | +entfernen, `pid` behalten (= Kind-Liste). Für `saveNback`/`$targetProvider` eine Ebene |
| 60 | +hochgehen (Ziel-`table` = Parent-Provider, `pid` entsprechend reduzieren). |
| 61 | + |
| 62 | +`redirectHome()/redirectCleanHome()` werden dünne Wrapper: |
| 63 | + |
| 64 | +```php |
| 65 | +self::dispatchRedirect($environment, new RedirectEvent(self::getBackUrl($environment, $cleanNames))); |
| 66 | +``` |
| 67 | + |
| 68 | +## 4. Die 5 Call-Sites — konkrete Umstellung |
| 69 | + |
| 70 | +| # | Ort | Heute | Neu | |
| 71 | +|---|-----|-------|-----| |
| 72 | +| 1 | `EditMask::doPersist` `saveNclose` | `GetReferrerEvent` → `RedirectEvent` | `RedirectEvent(getBackUrl($env))` | |
| 73 | +| 2 | `EditMask::doPersist` `saveNback` | `GetReferrerEvent(false, $parentProvider)` | `RedirectEvent(getBackUrl($env, [], $parentProvider))` | |
| 74 | +| 3 | `AbstractPropertyOverrideEditAllHandler:90` | `GetReferrerEvent(false, $definition->getName())` → Redirect | `RedirectEvent(getBackUrl($env))` | |
| 75 | +| 4 | `BackButtonListener::getReferrerUrl` (`@api`, Listen-Back-Button) | `GetReferrerEvent(true, parent/self)` | `$event->setHref(getBackUrl($env))` | |
| 76 | +| 5 | `SelectHandler::getReferrerUrl` (private, Button-Href) | `GetReferrerEvent(...)` | `getBackUrl($env)` | |
| 77 | + |
| 78 | +Zusätzlich **Template**: `dcbe_general_show.html5:25` nutzt `$this->getReferer(true)` |
| 79 | +(Contao-`BackendTemplate`-Methode → `System::getReferer()`). → In `ShowHandler` neue |
| 80 | +Template-Variable `backHref = ViewHelpers::getBackUrl($environment)` setzen und im |
| 81 | +Template `$this->backHref` verwenden. |
| 82 | + |
| 83 | +## 5. Entfernen / Aufräumen |
| 84 | + |
| 85 | +- `src/EventListener/StoreRefererListener.php` **löschen**. |
| 86 | +- Service-Registrierung in `src/Resources/config/event_listeners.yml` |
| 87 | + (Block `StoreRefererListener`) **entfernen**. |
| 88 | +- In **metamodels/core** `.../Resources/config/routing.yml`: die wirkungslosen |
| 89 | + `_dcg_referer_update: true`-Defaults entfernen (4 Vorkommen). |
| 90 | + *(Anderes Repo/Paket — separater Commit/PR.)* |
| 91 | + |
| 92 | +## 6. Bewusst nicht anfassen (BC) |
| 93 | + |
| 94 | +- `GetReferrerEvent` + `SystemSubscriber::handleGetReferer` liegen in |
| 95 | + **events-contao-bindings** und funktionieren weiter (jetzt via DcaUrlAnalyzer). |
| 96 | + Bleiben öffentliche API — dc-general nutzt sie nur intern nicht mehr für die eigene |
| 97 | + Navigation. |
| 98 | +- **Offen:** `GetReferrerEvent` als optionalen Override-Hook in `getBackUrl` |
| 99 | + voranstellen — oder ersatzlos aus dem DCG-Navigationspfad nehmen? *(noch zu entscheiden)* |
| 100 | +- `BackButtonListener` bleibt `@api`-Klasse mit gleicher Signatur, nur interne |
| 101 | + URL-Quelle ändert sich. |
| 102 | + |
| 103 | +## 7. Offene Punkte / zu testen |
| 104 | + |
| 105 | +1. **5.7-Verhalten ist ungetestet** → Backend-Durchlauf im |
| 106 | + `metamodels-devstack-5x-backend-1`-Container: Verhält sich `System::getReferer()` |
| 107 | + unter DCG falsch/leer? Referenz-URLs zum Abgleich sammeln. **(Schritt 1, läuft)** |
| 108 | +2. **`id`-Bereinigung**: heutiges `determineNewStyleRedirect` entfernt nur `act`, |
| 109 | + behält `id`; Legacy-Zweig droppt `id`. `getBackUrl` muss `id` konsistent entfernen |
| 110 | + — Nichtregression für bestehende `redirectHome`-Nutzer (Delete/Paste/Select) prüfen. |
| 111 | +3. **saveNback-Ebenenlogik**: Parent-Provider → Ziel-`table`/`pid`, auch bei |
| 112 | + mehrstufigen Parent/Child-Beziehungen. |
| 113 | +4. **`popup`-/`picker`-Modus** und **Ampersand-Encoding** im URL-Builder abbilden. |
| 114 | + |
| 115 | +## 8. Reihenfolge |
| 116 | + |
| 117 | +1. Backend-Test des Ist-5.7-Verhaltens (7.1) → dokumentieren. |
| 118 | +2. `ViewHelpers::getBackUrl()` einführen + `redirectHome/redirectCleanHome` darauf |
| 119 | + umstellen (additiv, testbar). |
| 120 | +3. Call-Sites 1–5 + Show-Template umstellen. |
| 121 | +4. `StoreRefererListener` + Service + Routing-Defaults entfernen. |
| 122 | +5. Psalm (`--no-cache`, backend-Container) + Backend-Klicktest: Edit→Speichern-und- |
| 123 | + zurück, Listen-Back-Button, Show-Back, Select-Modus, EditAll. |
| 124 | + |
| 125 | +## Anhang A: Backend-Test-Ergebnisse (5.7-Ist-Verhalten) |
| 126 | + |
| 127 | +**Umgebung:** Contao Managed Edition 5.7.9 (dev), Container |
| 128 | +`metamodels-devstack-5x-backend-1`. MM-Datenansicht ist bereits eine New-Style-Route: |
| 129 | +`/contao/metamodel/mm_employees` (kein klassisches `contao?do=…`). |
| 130 | + |
| 131 | +**Probe (auth-frei, Kernel gebootet, `DcaUrlAnalyzer` direkt) für `mm_employees`:** |
| 132 | + |
| 133 | +| Aufruf | Ergebnis | Bewertung | |
| 134 | +|--------|----------|-----------| |
| 135 | +| `getEditUrl('mm_employees', 1)` | `NULL` | Analyzer findet **kein** Backend-Modul für die Tabelle | |
| 136 | +| `getViewUrl('mm_employees', 1)` | `NULL` | dito | |
| 137 | +| `getTrail(edit-context)` | 1 Item → `{"label":"","url":"/contao?do=metamodels"}` | **falsches Ziel**: zeigt auf das MM-*Konfig*-Modul, nicht auf die `mm_employees`-Liste | |
| 138 | + |
| 139 | +**Ursache (verifiziert):** |
| 140 | + |
| 141 | +- Kein `$GLOBALS['BE_MOD']`-Eintrag führt `mm_employees` in seiner `tables`-Liste. |
| 142 | +- Einziges `metamodel*`-Modul: `metamodels/metamodels` mit `tables=[tl_metamodel_notelist]` |
| 143 | + (= Modell-Konfiguration). Die **per-Modell-Datenansichten** (`mm_*`) sind New-Style- |
| 144 | + Routen und **keine** klassischen `BE_MOD`-Module mit `tables`-Array. |
| 145 | +- `DcaUrlAnalyzer` ist genau auf diese `BE_MOD['…']['tables']`-Konvention gebaut → |
| 146 | + kann MM-Datenansichten nicht auflösen und fällt auf `do=metamodels` (Konfig) zurück. |
| 147 | + |
| 148 | +**Fazit:** `System::getReferer()` / `DcaUrlAnalyzer` liefern für MetaModels-Datenansichten |
| 149 | +**keine korrekten Back-URLs** (leer bzw. auf das falsche, übergeordnete Konfig-Modul). |
| 150 | +Damit ist der DCG-eigene Trail bestätigt notwendig. |
| 151 | + |
| 152 | +**Korrektes Ziel** (Referenz für `getBackUrl`): Aus einer Edit-/Show-Ansicht von |
| 153 | +`mm_employees` muss die Back-URL auf die Liste `/contao/metamodel/mm_employees` zeigen |
| 154 | +(bzw. bei Kind-Listen auf die entsprechende Parent-Route). |
| 155 | + |
| 156 | +**Route-Struktur (verifiziert per Router-Match):** |
| 157 | + |
| 158 | +Edit-URL (Adresszeile, real): |
| 159 | +``` |
| 160 | +/contao/metamodel/mm_employees?act=edit&id=mm_employees::1&rt=<token> |
| 161 | +``` |
| 162 | + |
| 163 | +`router->match('/contao/metamodel/mm_employees')`: |
| 164 | +``` |
| 165 | +_route = metamodels.metamodel |
| 166 | +_controller = MetaModels\CoreBundle\Controller\Backend\MetaModelController |
| 167 | +_scope = backend |
| 168 | +_dcg_referer_update = 1 |
| 169 | +_token_check = 1 |
| 170 | +tableName = mm_employees (Modellname als ROUTE-Param!) |
| 171 | +``` |
| 172 | + |
| 173 | +Unterschiede zu Contao-Core (wichtig für den Builder): |
| 174 | + |
| 175 | +- Eigene Route `metamodels.metamodel` statt `contao_backend`; Modellname steckt im |
| 176 | + **Route-Param `tableName`**, nicht in `?table=`. |
| 177 | +- ID ist dc-generals **serialisierte ModelId** `id=mm_employees::1`, nicht `?id=<int>`. |
| 178 | +- Aktion in der Query: `act=edit` (+ `rt`-Token). |
| 179 | + |
| 180 | +**Back-URL-Bildung konkret:** |
| 181 | +`router->generate('metamodels.metamodel', ['tableName' => 'mm_employees'])` |
| 182 | += `/contao/metamodel/mm_employees` |
| 183 | +→ Route + `_route_params` (`tableName`) behalten; Query **`act`, `id`, `rt`** (+ `cleanNames`) entfernen. |
| 184 | + |
| 185 | +**Korrektur zu Punkt 7.2:** Core-`determineNewStyleRedirect` entfernt heute nur `act` |
| 186 | +und ließe `id`/`rt` stehen. `getBackUrl` muss `id` **und** `rt` mit strippen. |
| 187 | + |
| 188 | +**Erweiterung zu Abschnitt 5 (Cleanup):** `_dcg_referer_update` hängt **auch an der |
| 189 | +`metamodels.metamodel`-Route** (nicht nur an den `add_all`-Routen). Cleanup-Scope in |
| 190 | +metamodels/core entsprechend größer. |
| 191 | + |
| 192 | +**Noch offen:** Kind-/Parent-Listen-Route (saveNback, mehrstufig) — Routen-/`pid`-Struktur |
| 193 | +bei verschachtelten MM noch am Live-Backend zu bestätigen. `mm_employees` ist flach: |
| 194 | +`saveNback` = `saveNclose` (beide → Liste). |
| 195 | + |
| 196 | +## Anhang B: End-to-End-Verifikation (Playwright, eingeloggtes Backend) |
| 197 | + |
| 198 | +Getestet gegen `http://localhost:8025`, Modell `mm_employees` (flach). Ergebnis nach |
| 199 | +dem Umbau: |
| 200 | + |
| 201 | +| Ansicht / Aktion | Back-/Redirect-Ziel | OK | |
| 202 | +|------------------|---------------------|----| |
| 203 | +| EDIT „Zurück" (`header_back dcg`) | `/contao/metamodel/mm_employees` | ✅ | |
| 204 | +| SHOW „Zurück" (`header_back dcg`) | `/contao/metamodel/mm_employees` | ✅ | |
| 205 | +| „Speichern und schließen" (saveNclose) | Redirect → `/contao/metamodel/mm_employees` | ✅ | |
| 206 | +| „Speichern und zurück" (saveNback) | Redirect → `/contao/metamodel/mm_employees` | ✅ | |
| 207 | +| Select-Modus „Beenden" | `/contao/metamodel/mm_employees` | ✅ | |
| 208 | + |
| 209 | +Alle Ziele sauber, ohne stale `id`/`rt`. |
| 210 | + |
| 211 | +**Fund + Fix während des Tests:** Der Select-Modus-„Beenden"-Button behielt zunächst |
| 212 | +`?select=models`. `SelectHandler::getReferrerUrl()` gibt jetzt `getBackUrl($env, ['select'])` |
| 213 | +mit — analog zum früheren `redirectCleanHome(['select'])`. |
| 214 | + |
| 215 | +Static Analysis: Psalm (`--no-cache`) „No errors", phpcs PSR12 ohne Beanstandung auf |
| 216 | +allen geänderten Dateien. |
0 commit comments