presseportale/dev/optimize/system-optimierung-analyse-codex.md
Kevin Adametz c7bd940f28 Optimierung Phase 0: Korrektheit & Security
Konsolidierter Entwicklungsplan aus den zwei Analyse-Dokumenten plus
Umsetzung der Phase-0-Massnahmen (Korrektheit/Security, je mit Tests):

0.1 BasicAuth: timing-sicherer Vergleich (hash_equals, beide Felder ohne
    Short-Circuit); Enabled-Check zur Laufzeit via config('auth.basic.*')
    statt env() im Bootstrap-Closure (greift jetzt unter config:cache);
    Config-Block + .env.example. Fund: auth.basic.* war nie als Config
    definiert, Gate war faktisch unkonfigurierbar.
0.2 Legacy-Invoice-"latest"-Abfrage zentralisiert (LegacyInvoice::
    scopeLatestFirst + User::latestLegacyInvoice); beide Consumer N+1-frei
    umgestellt; unterschiedliches null-Verhalten bewusst erhalten.
0.3 Payment-Option-Eligibility vereinheitlicht (period-aware, strikt):
    UserPaymentOption::scopeActiveNow/grandfatheredNow + User::
    hasActivePaymentOption; hasActiveBooking/hasUnlimitedPressReleaseQuota
    und ApiAccessEligibilityService darauf umgestellt; $hasGrandfathered-
    Mislabel behoben.
0.4 Theme->Portal-Vokabular: totes, projektfremdes ThemeMiddleware
    geloescht; Mapping in Portal::fromTheme() zentralisiert; fremden
    b2in-Default im ThemeServiceProvider auf Portal-Default korrigiert.
0.5 InvoiceNumberGenerator: Erst-Insert race-sicher via insertOrIgnore
    (Unique-Index auf circle existiert bereits). CreditWalletService war
    bereits abgesichert (Unique-Index + createOrFirst).

Tests: 4 neue Dateien, 5 bestehende auf gueltiges grandfathered_until
angepasst. Volle Suite gruen (793 passed, 1 skipped).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 08:09:02 +00:00

12 KiB

System-Optimierung Analyse

Stand: 18.06.2026

Ziel

Analyse des aktuellen Entwicklungsstands mit Fokus auf Controller, Views, Livewire/Volt-Komponenten und wiederverwendbare Code-Strukturen. Schwerpunkt: Code-Dopplungen, tote/alte Strukturen, Query-Wiederholungen und Refactoring-Potenzial.

Kurzfazit

Das System hat bereits gute fachliche Bausteine wie Services, Policies, Model-Scopes, Eloquent Resources und einzelne Livewire-Concerns. Das groesste Optimierungspotenzial liegt nicht in den klassischen Controllern, sondern in:

  • sehr grosser Route-/Page-Logik in routes/web.php
  • sehr grossen Volt-Dateien mit wiederholter Formular-, Scheduling- und Query-Logik
  • alten Admin-Wrapper-Views neben den aktiven Volt-Routen
  • einzelnen Demo-/Prototyp-Komponenten, die noch erreichbar sind
  • mehrfach eingebetteten API-Zugriffsqueries

Priorisierte Findings

1. Erreichbarer Prototyp: admin/users/table

Dateien

  • routes/admin.php
  • resources/views/livewire/admin/users/table.blade.php

routes/admin.php routet noch admin/users/table auf die Volt-Komponente admin.users.table. Diese Komponente wirkt wie ein frueher Flux-Prototyp:

  • filtert auf users.status, obwohl die Tabelle laut Schema kein status-Feld hat
  • sortiert Demo-Spalten wie date, status, amount
  • liest $user->role, obwohl Rollen ueber Spatie-Relationen laufen
  • enthaelt englische Demo-Menupunkte wie New post, Draft, Published, Archived

Risiko

Die Route ist erreichbar und kann zu SQL-Fehlern oder fachlich falscher UI fuehren.

Empfehlung

Route und Komponente entfernen, falls sie nicht mehr gebraucht werden. Falls eine separate User-Tabelle geplant ist, sollte sie auf Basis der produktiven admin.users-Komponente neu aufgebaut werden.

Prioritaet: Hoch

2. Alte Admin-Wrapper-Views neben direkten Volt-Routen

Dateien

  • resources/views/admin/*
  • routes/admin.php
  • app/Http/Controllers/Admin/DashboardController.php

Die aktiven Admin-Seiten laufen groesstenteils direkt ueber Volt::route(...). Gleichzeitig existieren alte Wrapper-Views unter resources/views/admin/*, die Livewire-Komponenten manuell mounten, z. B.:

  • resources/views/admin/companies/show.blade.php
  • resources/views/admin/press-releases/show.blade.php
  • resources/views/admin/roles/edit.blade.php

Ein Suchlauf hat nur einen produktiven view('admin.*')-Aufruf ergeben:

  • DashboardController rendert admin.dashboard

Viele Wrapper sind dadurch wahrscheinlich tot. Einige verwenden zudem alte Routennamen oder alte Route-Parameter, z. B. request()->route('pressRelease'), waehrend die aktuelle Volt-Route {id} nutzt.

Risiko

Verwirrung bei Wartung, falsche Wiederverwendung, alte Parameter-Konventionen, unklare Quelle der Wahrheit.

Empfehlung

  • admin.dashboard behalten, solange der DashboardController sie nutzt
  • ungenutzte Admin-Wrapper entfernen oder in einen klaren Legacy-/Dokumentationsbereich verschieben
  • resources/views/admin/*.md separat bewerten, da diese eher Dokumentation/Status darstellen

Prioritaet: Hoch

3. routes/web.php enthaelt zu viel Controller- und Query-Logik

Datei

  • routes/web.php

routes/web.php hat aktuell ueber 1100 Zeilen und enthaelt komplette Datenaufbereitung fuer:

  • Home-Daten ($webHomeData)
  • Kategorie-/Branchenseite ($categoryPage)
  • Pressemitteilungs-Detailseite ($releaseDetailPage)
  • Veroeffentlichen-Seite ($veroeffentlichenPage)
  • Suche ($suchePage)
  • Newsrooms-Index ($newsroomsIndex)
  • Newsroom-Detail ($newsroomDetail)
  • Preise ($preisePage)
  • Legacy-Redirects

Die Datei wiederholt mehrfach:

  • Domain-/Portal-Erkennung
  • portal/status/language/published_at-Filter
  • Eager-Load-Sets fuer company, category.translations, images
  • Most-read Queries
  • Newsroom Count Queries
  • Statistikfenster wie 7/14/30 Tage

Risiko

Schwer testbar, schwer zu optimieren, hohe Aenderungskosten, Query-Regeln koennen auseinanderlaufen.

Empfehlung

In mehreren kleinen Schritten extrahieren:

  1. WebPortalContext oder aehnlicher Service fuer DomainKey, Portal, PortalValues, Language und Theme-Konfiguration.
  2. PublishedReleaseQuery oder lokale Scopes am Model fuer veroeffentlichte Pressemitteilungen.
  3. Page-Data-Klassen wie HomePageData, CategoryPageData, NewsroomPageData.
  4. Danach Routen auf schlanke Controller oder Invokable Controller umstellen.

Prioritaet: Hoch

4. Wiederholte Published-Release-Bedingungen

Dateien

  • routes/web.php
  • app/Models/PressRelease.php

Die gleiche fachliche Bedingung fuer oeffentlich sichtbare Pressemitteilungen taucht mehrfach auf:

  • Portal muss aktuelles Portal oder both sein
  • Status ist published
  • Sprache passt
  • published_at ist gesetzt
  • published_at <= now()

Teilweise wird withoutGlobalScope(PortalScope::class) verwendet, teilweise nicht. Das ist fachlich nachvollziehbar, aber die Logik sollte zentraler formuliert werden.

Empfehlung

Moegliche Scopes:

public function scopePublished(Builder $query): Builder
public function scopeForPortalValues(Builder $query, array $portalValues): Builder
public function scopeForLanguage(Builder $query, string $language): Builder
public function scopeVisibleInArchive(Builder $query, array $portalValues, string $language): Builder

Alternativ ein Query-Objekt, wenn die Kombination mit withoutGlobalScope(PortalScope::class) bewusst kontrolliert werden soll.

Prioritaet: Hoch

5. Grosse PM-Editoren mit duplizierter Formular- und Scheduling-Logik

Dateien

  • resources/views/livewire/admin/press-releases/create.blade.php
  • resources/views/livewire/admin/press-releases/edit.blade.php
  • resources/views/livewire/customer/press-releases/create.blade.php
  • resources/views/livewire/customer/press-releases/edit.blade.php
  • app/Livewire/Concerns/InteractsWithPressReleaseTags.php

Die vier PM-Editoren sind sehr gross und wiederholen viele Muster:

  • Properties fuer Portal, Sprache, Firma, Kategorie, Kontakt, Titel, Text, Keywords
  • updatedPublishMode
  • updatedScheduledDate
  • updatedScheduledTime
  • syncScheduledAt
  • resolveScheduledAt
  • validateScheduledAtWhenReady
  • grosse formRules
  • Teile der Speicher- und Submit-Logik

Positiv: Fuer Customer-Editoren existiert bereits InteractsWithPressReleaseTags. Admin-Editoren duplizieren die Tag-Logik aber noch lokal.

Empfehlung

In kleinen, risikoarmen Schritten:

  1. Admin-PM-Editoren ebenfalls InteractsWithPressReleaseTags nutzen lassen.
  2. InteractsWithPressReleaseScheduling einfuehren fuer Scheduling-Hooks und Validierung.
  3. Gemeinsame Rule-Fragmente in einen Service oder Trait auslagern, z. B. PressReleaseFormRules.
  4. Wiederkehrende Formularabschnitte als Blade-Komponenten oder partials extrahieren.

Prioritaet: Hoch

6. API-Zugriffsqueries mehrfach eingebettet

Dateien

  • app/Http/Controllers/Api/V1/PressReleaseController.php
  • app/Http/Controllers/Api/V1/PressReleaseImageController.php
  • app/Http/Controllers/Api/V1/CompanyController.php
  • app/Models/PressRelease.php
  • app/Models/Company.php

Mehrere Controller enthalten fast gleiche Ownership-/Access-Queries:

  • findOwnedPressRelease in PressReleaseController
  • findOwnedPressRelease in PressReleaseImageController
  • Company-Zugriff ueber owner_user_id oder users-Pivot in CompanyController
  • dieselbe Company-Zugriffslogik nochmals in PressReleaseController

Es gibt bereits PressRelease::accessibleBy(User $user), allerdings nutzt die API aktuell eine engere user_id-Logik. Das kann fachlich korrekt sein, sollte aber explizit zentralisiert werden.

Empfehlung

  • Company::accessibleBy(User $user) als Scope einfuehren
  • API-spezifischen PressRelease-Scope definieren, z. B. ownedByApiUser(User $user), falls API-Zugriff bewusst nur Autor-Ownership meint
  • alternativ Policies konsequenter nutzen

Prioritaet: Mittel bis hoch

7. Wiederholte API-Resource-Eager-Loads

Datei

  • app/Http/Controllers/Api/V1/PressReleaseController.php

Die gleiche Relation-Liste wird mehrfach geladen:

  • company:id,name,portal,legacy_portal,legacy_id
  • category.translations
  • images

Empfehlung

Eine private Methode oder ein Query-Scope:

private function resourceRelations(): array

oder:

PressRelease::query()->withApiResourceRelations()

Prioritaet: Mittel

8. Grosse Volt-Dateien als langfristiges Wartungsrisiko

Beispiele

  • resources/views/livewire/admin/press-releases/edit.blade.php mit ca. 1394 Zeilen
  • resources/views/livewire/customer/press-releases/create.blade.php mit ca. 1371 Zeilen
  • resources/views/livewire/customer/press-releases/edit.blade.php mit ca. 1331 Zeilen
  • resources/views/livewire/admin/users/edit.blade.php mit ca. 1241 Zeilen
  • resources/views/livewire/admin/press-releases/index.blade.php mit ca. 1093 Zeilen

Volt Single-File-Komponenten sind hier legitim, aber bei dieser Groesse vermischen sich Zustand, Query, Validierung, Aktionen und Markup stark.

Empfehlung

Nicht pauschal auf klassische Livewire-Klassen migrieren, sondern gezielt extrahieren:

  • Concerns fuer wiederkehrende Interaktionen
  • Service-Klassen fuer fachliche Aktionen
  • kleine Blade-Komponenten fuer wiederkehrende UI-Abschnitte
  • Query-Methoden/Scopes fuer Tabellenfilter

Prioritaet: Mittel

Positive Muster, die beibehalten werden sollten

  • PressRelease::accessibleBy(User $user) ist ein guter Single-Source-Ansatz fuer Kundenzugriff.
  • PortalScope kapselt Portalfilterung zentral.
  • HasUniqueSlug reduziert Slug-Duplikate.
  • API nutzt Form Requests und Eloquent Resources.
  • Dashboard nutzt bereits AdminPerformanceCache.
  • Es gibt viele Feature-Tests fuer Admin, API, Customer und Web.
  • Erste Concerns wie InteractsWithPressReleaseTags zeigen die richtige Richtung.

Vorgeschlagene Refactoring-Pakete

Paket A: Aufraeumen ohne Fachlogik-Aenderung

  1. admin/users/table Route entfernen.
  2. resources/views/livewire/admin/users/table.blade.php entfernen.
  3. Ungenutzte resources/views/admin/* Wrapper entfernen oder eindeutig als Legacy markieren.
  4. Tests fuer fehlende/entfernte Route anpassen oder ergaenzen.

Nutzen: Weniger Altlasten, weniger Risiko durch erreichbare Demo-Komponente.

Paket B: Gemeinsame PM-Editor-Concerns

  1. Admin-PM-Editoren auf InteractsWithPressReleaseTags umstellen.
  2. InteractsWithPressReleaseScheduling extrahieren.
  3. Gemeinsame Rule-Fragmente zentralisieren.
  4. Bestehende PM-Editor-Tests gezielt laufen lassen.

Nutzen: Weniger Dopplung in vier grossen Dateien, geringere Fehlerwahrscheinlichkeit bei Scheduling.

Paket C: Web-Routen entflechten

  1. Portal-/Domain-Kontext extrahieren.
  2. Published-Release-Query zentralisieren.
  3. Home/Kategorie/Newsroom-Daten in Services verschieben.
  4. Routen schrittweise auf kleine Controller umstellen.

Nutzen: Bessere Testbarkeit, klarere Verantwortlichkeiten, zentraleres Performance-Tuning.

Paket D: API-Zugriff zentralisieren

  1. Company::accessibleBy(User $user) einfuehren.
  2. API-PressRelease-Zugriff zentral benennen.
  3. Eager-Load-Sets fuer API Resources zentralisieren.
  4. API-Feature-Tests laufen lassen.

Nutzen: Weniger Query-Duplikate, klarere Zugriffspolitik.

Pruefung / Verifikation

Die Analyse basiert auf statischer Codepruefung, Laravel-Boost-Projektinformationen, versionierter Dokumentationssuche und Schema-Inspektion. Eine Artisan-Routentabelle konnte nicht ausgefuehrt werden, weil Sail gemeldet hat:

Docker or Podman is not running.

Hinweis zum aktuellen Worktree

Zum Analysezeitpunkt gab es bereits eine nicht von mir angelegte Aenderung:

  • resources/views/components/layouts/app/sidebar.blade.php

Diese Datei wurde fuer die Analyse nicht veraendert.