presseportale/docs/user-admin/user-zusammenhaenge.md

14 KiB

User-Admin: Zusammenhänge und relevante Daten

Stand: 2026-05-21 (aktualisiert nach Phase 7)

Diese Notiz beschreibt den User als fachlichen Mittelpunkt für die weitere Konzeption des Admin-User-Bereichs. Grundlage sind die aktuellen Models und Migrationen im Laravel-Projekt.

Was sich seit dem ursprünglichen Stand (2026-05-05) geändert hat:

  • Pressemitteilungen haben zusätzliche Felder: subtitle, scheduled_at, embargo_at, boilerplate_override, no_export (Phase 7).
  • Neue Tabelle press_release_attachments (Modell PressReleaseAttachment). UI ist temporär deaktiviert (Security-Review), Tabelle und Service bleiben aber erhalten.
  • Neuer Service-Layer für PMs: PressReleaseService (Status-Übergänge, Scheduling-Auflösung), PressReleaseHtmlSanitizer (HTMLPurifier-Wrapper), PressReleaseAttachmentStorage.
  • Neuer Console-Command php artisan press-releases:publish-scheduled (Scheduler-Intervall 5 Min) veröffentlicht geplante PMs automatisch.
  • Pivot press_release_contact bleibt n:m, der Customer-Flow speichert aber nur einen Kontakt pro PM und das Feld ist nullable.

Zentraler Ausgangspunkt

Ein User ist nicht nur ein Login-Konto, sondern bündelt im System mehrere fachliche Bereiche:

  • Zugang und Status: Name, E-Mail, Verifikation, Passwort, 2FA, Aktiv/Inaktiv, Super-Admin-Flag, Rollen/Berechtigungen.
  • Portal- und Legacy-Zuordnung: portal, registration_type, language, legacy_portal, legacy_id.
  • CRM-Zuordnung: Firmen, Firmenrollen, Kontakte.
  • Content-Zuordnung: Pressemitteilungen, Bilder, Statusverlauf.
  • Abrechnung und Verträge: Rechnungsadresse, Zahlungsoptionen, Zahlungen, Rechnungen, Legacy-Rechnungen.
  • API und Sicherheit: Sanctum-Tokens, API-Nutzung, Magic-Links.
  • Kommunikation und Arbeitskomfort: Newsletter-Abos, Filter-Presets.

Datenmodell im Überblick

erDiagram
    users ||--o| profiles : "hat"
    users ||--o| billing_addresses : "hat"
    users ||--o{ companies : "owner_user_id"
    users }o--o{ companies : "company_user.role"
    users }o--o{ contacts : "contact_user"
    companies ||--o{ contacts : "hat"
    users ||--o{ press_releases : "autor"
    companies ||--o{ press_releases : "zugeordnet"
    contacts }o--o{ press_releases : "press_release_contact"
    press_releases ||--o{ press_release_images : "hat"
    press_releases ||--o{ press_release_status_logs : "hat"
    users ||--o{ press_release_status_logs : "changed_by_user_id"
    users ||--o{ user_payment_options : "hat"
    user_payment_options }o--o{ companies : "user_payment_option_company"
    user_payment_options ||--o{ user_payments : "hat"
    user_payments ||--o{ invoices : "erzeugt"
    users ||--o{ invoices : "hat"
    users ||--o{ legacy_invoices : "hat"
    users ||--o{ newsletter_subscriptions : "hat"
    users ||--o{ magic_links : "hat"
    users ||--o{ user_filter_presets : "hat"
    users ||--o{ api_usage_logs : "hat"

Direkte Relationen am User

profile

  • Kardinalität: 1:0..1.
  • Tabelle: profiles, Foreign Key user_id ist eindeutig.
  • Inhalt: persönliche Profildaten, Anrede, Titel, Vor-/Nachname, Telefon, Adresse, Land, Geburtsdatum, Backlink, Statistik-/Footer-Code-Flags, Validierungs-/Vertragsdaten, Steuerdaten.
  • Relevanz im Admin: Datenqualität, Legacy-Profil, persönliche Stammdaten, Vertrag/Validierung.

billingAddress

  • Kardinalität: 1:0..1.
  • Tabelle: billing_addresses, Foreign Key user_id ist eindeutig.
  • Inhalt: Rechnungsname, Adresse, PLZ, Ort, Land.
  • Relevanz im Admin: Rechnungsfähigkeit, fehlende Abrechnungsdaten, Kundendatenpflege.

ownedCompanies

  • Kardinalität: 1:n.
  • Tabelle: companies, Foreign Key owner_user_id.
  • Bedeutung: Der User ist fachlicher Eigentümer einer Firma, auch ohne Pivot-Eintrag in company_user.
  • Relevanz im Admin: wichtig für Rechte im Customer-Profil und für klare Verantwortlichkeit.

companies

  • Kardinalität: n:m.
  • Pivot: company_user mit company_id, user_id, role.
  • Rollen: member, responsible, owner.
  • Bedeutung: Ein User kann mehrere Firmen haben; eine Firma kann mehreren Usern zugeordnet sein.
  • Relevanz im Admin: Hauptstruktur für Kundenkonto, Berechtigungen in der Firmenpflege und spätere User-Detailansicht.

contacts

  • Kardinalität: n:m.
  • Pivot: contact_user mit contact_id, user_id.
  • Zusätzlich gehört jeder Kontakt zwingend zu genau einer Firma über contacts.company_id.
  • Bedeutung: Kontakte können direkt Usern zugeordnet sein, fachlich sind sie aber an Firmen aufgehängt.
  • Relevanz im Admin: Ansprechpartner und Zuordnungsqualität.

pressReleases

  • Kardinalität: 1:n.
  • Tabelle: press_releases, Foreign Key user_id.
  • Zusätzlich: optionale Firmenzuordnung über company_id und Pflichtkategorie über category_id.
  • Wichtig: Datenbankseitig ist company_id nullable, fachlich sollte eine Customer-PM einer Firma zugeordnet sein. Der Customer-Flow erzwingt aktuell eine eigene Firma und leitet daraus das Portal ab.
  • Relevanz im Admin: Content-Historie, Status, Veröffentlichungen, Qualität, Freigabeprozess.

newsletterSubscriptions

  • Kardinalität: 1:n.
  • Tabelle: newsletter_subscriptions, Foreign Key user_id.
  • Inhalt: Portal, Name, E-Mail, IP, Bestätigung, Subscribe/Unsubscribe, Legacy-Zuordnung.
  • Relevanz im Admin: Kommunikationsstatus und Opt-in/Opt-out-Historie.

billing / payments

  • userPaymentOptions: 1:n über user_payment_options.user_id.
  • invoices: 1:n über invoices.user_id.
  • legacyInvoices: 1:n über legacy_invoices.user_id.
  • Indirekt: user_payment_options hat n:m zu Firmen über user_payment_option_company; user_payments hängt an user_payment_options; invoices können zusätzlich an user_payments und invoice_billing_addresses hängen.
  • Relevanz im Admin: Vertrags-/Abo-Status, aktive API-Freischaltung, Rechnungen, Legacy-Archiv.

tokens

  • Kommt über Laravel Sanctum HasApiTokens.
  • Tabelle: personal_access_tokens mit polymorphem tokenable_type / tokenable_id.
  • Im Customer-Bereich werden Tokens für API-Zugriffe verwaltet.
  • Relevanz im Admin: API-Zugang, genutzte Berechtigungen, letzter Zugriff.

magicLinks

  • Kardinalität: 1:n.
  • Tabelle: magic_links, Foreign Key user_id.
  • Inhalt: Token-Hash, Zweck, Payload, Ablauf, Verbrauch, IPs.
  • Relevanz im Admin: Auth-/Support-Kontext, Magic-Login-Historie.

filterPresets

  • Kardinalität: 1:n.
  • Tabelle: user_filter_presets, Foreign Key user_id.
  • Inhalt: Seite, Name, Default-Flag, letzte Nutzung, Filter-JSON.
  • Relevanz im Admin: eher Arbeitskomfort/Personalisierung, nicht Kern-Kundendaten.

apiUsageLogs

  • Kardinalität: faktisch 1:n über api_usage_logs.user_id, aber aktuell keine Relation im User-Model.
  • Inhalt: Token, Methode, Pfad, Route, Status, IP, User-Agent, Dauer, Zeitpunkt.
  • Relevanz im Admin: API-Aktivität, Support und Missbrauchsanalyse.

roles und permissions

  • Kommen über Spatie HasRoles.
  • Tabellen u. a. roles, permissions, model_has_roles, model_has_permissions, role_has_permissions.
  • Relevanz im Admin: Trennung zwischen Admin, Editor, Customer, Super-Admin und feineren Berechtigungen wie press-releases:publish oder users:manage.

Firmen, Kontakte und Pressemitteilungen

Die fachliche CRM-/Content-Struktur läuft im Kern so:

  • User hat mehrere Firmen über company_user.
  • User kann zusätzlich direkter Eigentümer einer Firma über companies.owner_user_id sein.
  • Firma hat mehrere Kontakte über contacts.company_id.
  • Firma hat mehrere Pressemitteilungen über press_releases.company_id.
  • Pressemitteilung hat genau einen Autor/User über press_releases.user_id.
  • Pressemitteilung kann mehrere Kontakte über press_release_contact referenzieren.
  • Kontakt kann mehreren Usern direkt zugeordnet sein über contact_user, bleibt aber immer an genau eine Firma gebunden.

Daraus ergibt sich für den User-Admin eine wichtige Sicht:

  • User-Zentrum: Wer ist der Account?
  • Firmen-Zentrum: Welche Firmen gehören dazu, in welcher Rolle?
  • Kontakt-Zentrum: Welche Ansprechpartner hängen an diesen Firmen und/oder am User?
  • Content-Zentrum: Welche Pressemitteilungen wurden vom User erstellt und welcher Firma sind sie zugeordnet?
  • Abrechnungs-Zentrum: Welche Zahloptionen, Rechnungen und Legacy-Rechnungen hängen am User und ggf. an Firmen?

Pressemitteilungen im Detail

Eine Pressemitteilung (press_releases) enthält:

  • Identifikation: id, uuid, slug, legacy_portal, legacy_id.
  • Zuordnung: portal, language, user_id, company_id, category_id.
  • Inhalt: title, subtitle, text, backlink_url, keywords, boilerplate_override.
  • Status/Publikation: status, published_at, scheduled_at, embargo_at, no_export, hits, teaser_begin, teaser_end.
  • Medien: Bilder über press_release_images, Anhänge über press_release_attachments (UI deaktiviert).
  • Kontakte: Pivot press_release_contact.
  • Verlauf: Statuslogs über press_release_status_logs, inklusive changed_by_user_id, Statuswechsel, Grund und Quelle.

Service-Layer rund um PMs:

  • App\Services\PressRelease\PressReleaseService — kapselt die Übergänge submitForReview, publish, reject, archive, deleteFromAdmin sowie das Auflösen von scheduled_at/embargo_at für die echte Veröffentlichung.
  • App\Services\PressRelease\PressReleaseHtmlSanitizer — Wrapper um mews/purifier mit definierter Tag-/Attribut-Whitelist.
  • App\Services\PressRelease\PressReleaseAttachmentStorage — kapselt Datei-Operationen auf dem public-Disk (UI aktuell deaktiviert).
  • App\Console\Commands\PublishScheduledPressReleases — wird per Scheduler (alle 5 Min) ausgeführt und veröffentlicht freigegebene PMs zum geplanten Zeitpunkt, respektiert Embargo.

Fachliche Konsequenz:

  • Für Kunden sollte company_id praktisch Pflicht sein, obwohl die DB es nullable erlaubt.
  • Für Admins kann company_id = null als Migrations-/Altfall auftauchen und sollte im User-Admin sichtbar markiert werden.
  • portal sollte mit der Firma konsistent sein. Im Customer-Flow wird das Portal aus der Firma abgeleitet; im Admin-Bereich sollte eine bewusste Korrektur möglich sein.

User-relevante Tabellen

Kernkonto:

  • users
  • profiles
  • billing_addresses
  • sessions
  • password_reset_tokens
  • personal_access_tokens
  • magic_links
  • Spatie Permission-Tabellen

CRM:

  • companies
  • company_user
  • contacts
  • contact_user

Content:

  • press_releases
  • press_release_images
  • press_release_attachments (UI temporaer deaktiviert)
  • press_release_contact
  • press_release_status_logs
  • categories
  • category_translations

Billing:

  • billing_addresses
  • user_payment_options
  • user_payment_option_company
  • user_payments
  • payment_options
  • payment_option_translations
  • invoices
  • invoice_billing_addresses
  • legacy_invoices

Kommunikation/API/Arbeitskomfort:

  • newsletter_subscriptions
  • api_usage_logs
  • user_filter_presets

Relevante Inhalte für den Admin-User-Bereich

Für eine optimierte Admin-User-Ansicht sollten diese Blöcke geplant werden:

  • Account: Status, Portal, Sprache, Registrierungstyp, Rollen, Berechtigungen, Super-Admin, letzte Aktivität, letzte IP, E-Mail-Verifikation, 2FA-Status.
  • Datenqualität: Profil vorhanden, Rechnungsadresse vorhanden, Firma vorhanden, Kontakte vorhanden, veröffentlichte PMs vorhanden, Legacy-Zuordnung vorhanden.
  • Firmen: Firmenliste mit Rolle, Eigentümerstatus, Portal, Aktivstatus, Logo, Footer-Code-Flag, Kontakt-/PM-Anzahl.
  • Kontakte: Kontakte je Firma plus direkte User-Kontakte aus contact_user.
  • Pressemitteilungen: Gesamtzahl, Statusverteilung, letzte PMs, veröffentlichte PMs, PMs ohne Firma, PMs mit Portalabweichung.
  • Abrechnung: Rechnungsadresse, aktive Zahlungsoptionen, verknüpfte Firmen zu Zahlungsoptionen, Zahlungen, Rechnungen, Legacy-Rechnungen.
  • API: Token-Anzahl, Berechtigungen, letzte Nutzung, API-Usage-Logs.
  • Support/Admin-Aktionen: User bearbeiten, Rollen/Berechtigungen ändern, Impersonation, Magic-Link-Kontext, ggf. Account deaktivieren.

Offene Punkte für die weitere Konzeption

  • User hat keine direkte apiUsageLogs()-Relation, obwohl api_usage_logs.user_id existiert. Für Admin-Detailansichten wäre eine Relation sinnvoll.
  • press_releases.company_id ist nullable. Fachlich sollte geklärt werden, ob das nur Legacy-/Admin-Fälle erlaubt oder langfristig verboten werden soll.
  • contacts gehören immer zu einer Firma, können aber zusätzlich direkt Usern zugeordnet werden. Für die Oberfläche muss klar sein, ob "User-Kontakte" nur direkte Pivot-Kontakte meint oder alle Kontakte seiner Firmen.
  • Eine Firma kann über owner_user_id und zusätzlich über company_user.role = owner eine Eigentümerinformation haben. Das sollte im Admin eindeutig dargestellt und ggf. harmonisiert werden.
  • Billing hängt primär am User, Zahlungsoptionen können aber über Pivot auch Firmen betreffen. Für den Admin sollte klar werden, ob Rechnungen immer User-Rechnungen sind oder später firmenscharf betrachtet werden sollen.
  • Portal-Scope ist auf Firmen, Kontakte, Pressemitteilungen und Newsletter aktiv. Admin-Auswertungen nutzen teils withoutGlobalScopes(). In der User-Admin-Konzeption muss entschieden werden, wann portalübergreifend gesucht und angezeigt wird.

Quellen im Code

Modelle:

  • app/Models/User.php
  • app/Models/Company.php
  • app/Models/Contact.php
  • app/Models/PressRelease.php
  • app/Models/PressReleaseImage.php
  • app/Models/PressReleaseAttachment.php
  • app/Models/PressReleaseStatusLog.php
  • app/Models/Profile.php
  • app/Models/BillingAddress.php
  • app/Models/Invoice.php
  • app/Models/LegacyInvoice.php
  • app/Models/UserPaymentOption.php
  • app/Models/UserPayment.php
  • app/Models/NewsletterSubscription.php
  • app/Models/MagicLink.php
  • app/Models/UserFilterPreset.php
  • app/Models/ApiUsageLog.php

Services / Commands:

  • app/Services/PressRelease/PressReleaseService.php
  • app/Services/PressRelease/PressReleaseHtmlSanitizer.php
  • app/Services/PressRelease/PressReleaseAttachmentStorage.php
  • app/Services/Image/ImageService.php
  • app/Console/Commands/PublishScheduledPressReleases.php
  • routes/console.php (Scheduler-Eintraege)

Migrationen:

  • database/migrations/*