presseportale/dev/migration 2026/01-LEGACY-ANALYSIS.md
Kevin Adametz 5b8bdf4182
Some checks are pending
linter / quality (push) Waiting to run
tests / ci (push) Waiting to run
12-05-2026 Frontend dev
2026-05-12 18:32:33 +02:00

282 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 01 Legacy-Analyse
Detailliertes Inventar des Symfony-1.4-Backends in `/_businessportal24.com/`. Das `presseecho`-Projekt ist code-identisch.
---
## 1. Projektstruktur (Top-Level)
```
_businessportal24.com/
├── apps/
│ ├── backend/ ← Admin-App (Routing, Config, Templates). Modules liegen in Plugins.
│ └── frontend/ ← Public/Frontend-App (wird NICHT migriert neue Frontends existieren)
├── config/ ← Globale Config (Datenbanken, Projekt, i18n)
├── lib/ ← Global genutzte Klassen (Models liegen aber in Plugin)
├── plugins/ ← Fachliche Logik der Hauptteil!
│ ├── PressePortalPlugin ★ Kern-Plugin (Entities, Modules, Forms)
│ ├── sfDoctrineGuardPlugin ← Auth
│ ├── sfDoctrineGuardPaymentPlugin ★ Payment, Invoice, Coupon
│ ├── sfDoctrineApplyPlugin ← Registrierung
│ ├── sfFormExtraPlugin ← Form-Widgets
│ ├── sfImageTransformPlugin ← Image-Thumbnailing
│ ├── sfImageTransformExtraPlugin
│ ├── sfCaptchaGDPlugin ← Captcha
│ ├── sfCsvPlugin ← CSV-Export
│ ├── isicsWidgetFormTinyMCEPlugin← WYSIWYG
│ ├── tnHttpErrorPlugin ← HTTP-Error-Handling
│ └── tnStrategicTesterResponsePlugin
├── symfony-1.4/ ← Framework (Vendor)
├── data/ ← Fixtures
├── web/ ← DocRoot, Public Assets
└── cron_script_{local,server}.sh
```
---
## 2. Admin-Module (`PressePortalPlugin/modules/`)
### 2.1 Verwaltung / CRUD
| Modul | Entität | Admin-URL | Migrations-Target |
|---|---|---|---|
| `adminCenter` | Dashboard / Navigationshub | `/` | Livewire `admin.dashboard` (existiert) |
| `adminSfGuardAuth` | Login/Logout | `/login`, `/logout` | Fortify (existiert) |
| `adminSfGuardUser` (über Routing) | User | `/sf_guard_user` | `admin.users.*` (existiert) |
| `adminSfGuardGroup` | Rollen | `/sf_guard_group` | Spatie Roles → `admin.roles.*` (existiert) |
| `adminSfGuardPermission` | Permissions | `/sf_guard_permission` | Spatie Permissions |
| `adminSalutation` | Anreden | `/salutation` | → Config (`config/salutations.php`) |
| `adminCountry` | Länder | `/country` | → Config (`config/countries.php`) |
| `adminCategory` | Kategorien (i18n) | `/category` | `admin.categories.*` (existiert) |
| `adminCompany` | Firmen/Agenturen | `/company` | `admin.companies.*` (existiert) |
| `adminContact` | Ansprechpartner | `/contact` | `admin.contacts.*` (existiert) |
| `adminPressrelease` | Pressemitteilungen | `/pressrelease` | `admin.press-releases.*` (existiert) |
| `adminPressreleaseimage` | Bilder zu PMs | `/pressreleaseimage` | Teil von PressRelease |
| `adminPromotionlink` | Werbeplätze/Links | `/promotionlink` | **Scope-Entscheidung nötig** |
| `adminFootercode` | Custom Footer-HTML | `/footercode` | **Scope-Entscheidung nötig** |
| `adminBlacklist` | Wort-Blacklist | `/blacklist` | `admin.blacklist.*` (neu) |
| `adminNewslettersubscription` | Newsletter-Subscriber | `/newslettersubscription` | `admin.newsletter.subscribers` (existiert) |
| `sfpCouponAdmin` | Gutscheine | `/coupon` | `admin.coupons.*` (existiert) |
| `sfpInvoiceAdmin` | Rechnungen | `/invoice` | `admin.invoices.*` (existiert) |
| `sfpInvoiceBillingAddressAdmin` | Rechn.-Adressen | `/invoice_billing_address` | Teil von Invoice |
| `sfpUserBillingAddressAdmin` | User-Rechn.-Adressen | `/user_billing_address` | Teil von User |
| `sfpUserPaymentOptionAdmin` | User-Abos/Payment | `/user_payment_option` | `admin.payments.*` (existiert) |
### 2.2 Service-/System-Module
| Modul | Zweck | Migrations-Target |
|---|---|---|
| `sfpCron` | HTTP-Trigger für Cron-Scripts | Laravel Scheduler (`routes/console.php`) |
| `sfpStore` | Abo-/Bestell-Flow | Stripe Checkout + Domain-Service |
| `sfpInvoicePdf` | PDF-Generation | `barryvdh/laravel-dompdf` + Blade-Template |
| `sfpUserBilling` | User-seitige Rechn.-Übersicht | Frontend-/Kundencenter-Views |
| `import` | CSV-/Daten-Import | **Neu als `php artisan` Command(s)** |
| `ajax` | Autocompletion für Admin-Forms | Livewire `wire:model`-Suche |
| `tnHttpError` | Error-Views | Laravel's Standard-Error-Pages |
### 2.3 Frontend-Module (werden NICHT migriert)
| Modul | Zweck |
|---|---|
| `company`, `customercenter`, `page`, `pressrelease`, `pressreleaseimage`, `category`, `profile`, `newsletter`, `presscontact`, `sfApply`, `sfGuardAuth`, `sfGuardUser` | Altes Symfony-Frontend. Wird ersetzt durch bestehendes `resources/views/web/*` und das neue Backend-API |
---
## 3. Datenmodell (aus `PressePortalPlugin/config/doctrine/schema.yml`)
### 3.1 Entitäten (Kern)
| Doctrine-Entität | Zeilen in Schema | Fachlicher Zweck |
|---|---|---|
| `Salutation` | 418 | Anrede (Herr/Frau, i18n) |
| `Country` | 2332 | Länder |
| `sfGuardUser` | 3884 | User |
| `sfGuardUserProfile` | 92191 | User-Profil (Name, Adresse, Tel, E-Mail, API-Key, Tax-ID …) |
| `Company` | 197278 | Firma (inkl. Logo, Login, Footer-Code-Disable) |
| `Agency` | 280297 | Column-Aggregation-Inheritance von Company (ctype='agency') |
| `CompanyUser` | 300318 | User ↔ Company Pivot |
| `ApiUser` | 321339 | API-Only User-Markierung |
| `ResponsibleCompanyUser` | 342360 | Verantwortliche User pro Company |
| `Contact` | 365411 | Ansprechpartner einer Firma |
| `NewsletterSubscription` | 418469 | Newsletter-Anmeldungen |
| `Category` | 476494 | Kategorien (i18n, sluggable) |
| `PressRelease` | 500575 | Pressemitteilungen (mit Teaser-Range, Status, SEO) |
| `PressReleaseContact` | 583601 | PM ↔ Contact Pivot |
| `PressReleaseImage` | 608644 | Bilder zu PMs |
| `PromotionLink` | 650684 | Werbelinks |
| `PromotionLinkCategory` | 687705 | PromotionLink ↔ Category Pivot |
| `FooterCode` | 707733 | Custom Footer-HTML pro Kategorie/Sprache |
| `CategoryFooterCode` | 735753 | Pivot |
| `Blacklist` | 755768 | Worte für Spam-Check |
| `UserBillingAddress` | 771807 | Rechn.-Adresse am User |
| `InvoiceBillingAddress` | 809841 | Rechn.-Adresse auf Rechnung (Snapshot) |
| `UserPaymentOptionCompany` | 854874 | Abo ↔ Firma |
### 3.2 Entitäten aus `sfDoctrineGuardPaymentPlugin`
| Entität | Zweck |
|---|---|
| `PaymentOption` | Produkt-Definition (Preis, Typ: recurring/onetime/single) |
| `PaymentOptionReference` | Self-referencing, Parent→Child (z. B. Aufpreispakete) |
| `PaymentOptionExcludeGroup` | Gruppen, die Option NICHT buchen dürfen |
| `PaymentOptionAccessGroup` | Gruppen, die durch Option Zugriff erhalten |
| `UserPaymentOption` | Gebuchtes Abo (User ↔ Produkt), Status, Laufzeit |
| `UserPaymentOptionReference` | Self-referencing |
| `UserPayment` | Zahlungsdatensatz |
| `Invoice` | Rechnung (mit Mahnungen, Fälligkeit) |
| `UserBillingAddress` | (hier nochmal definiert) |
| `InvoiceBillingAddress` | (hier nochmal definiert) |
| `Coupon` | Gutschein |
### 3.3 sfGuard-Tabellen
`sfGuardUser`, `sfGuardGroup`, `sfGuardPermission`, `sfGuardUserGroup`, `sfGuardGroupPermission`, `sfGuardUserPermission`, `sfGuardRememberKey` → Mapping auf Laravel `users` + Spatie `roles`/`permissions`/`model_has_*`.
### 3.4 Besondere Doctrine-Features
| Feature | Verwendung | Laravel-Äquivalent |
|---|---|---|
| `actAs: Timestampable` | auf fast allen Entitäten | `created_at`/`updated_at` (Standard) |
| `actAs: I18n` | `Salutation`, `Category`, `PaymentOption` | Translation-Tabellen oder `spatie/laravel-translatable` |
| `actAs: Sluggable` | `Company`, `Category`, `PressRelease`, `PressReleaseImage` | `spatie/laravel-sluggable` |
| `inheritance: column_aggregation` | `Agency extends Company` | Entweder gemeinsames Model mit `type`-Scope, oder Parent+Child-Model mit globalem Scope |
| `relations refClass` | viele M:N | Pivot-Tabellen, `belongsToMany` |
---
## 4. Routing (`apps/backend/config/routing.yml`)
Auszug **15 `sfDoctrineRouteCollection`** (auto-generated REST für alle CRUD-Entitäten) + folgende Custom-Routes:
| Route | URL | Ziel |
|---|---|---|
| `homepage` | `/` | `adminCenter::index` |
| `change` | `/mychange` | `adminCenter::change` |
| `sf_guard_signin` | `/login` | Login |
| `sf_guard_signout` | `/logout` | Logout |
| `sf_guard_password` | `/request_password` | Password-Reset |
| `usercp` | `/usercp` | User-CP |
| `masterdata` | `/masterdata` | Stammdaten |
| `portalcp` | `/portalcp` | Portal-CP |
| `promotioncp` | `/promotioncp` | Promotion |
| `pressrelease_company_autocomplete` | `/pressrelease/companyautocomplete` | AJAX |
| `company_user_autocomplete` | `/company/userautocomplete` | AJAX |
| `press_release_autocomplete_url` | `/ajax/press_release_autocomplete` | AJAX |
| `sf_image` | `/thumbnails/sf_image/:format/:filepath.:sf_format` | Thumbnails |
| `slug_image` | `/thumbnails/:type/:format/:path/:slug-:id.:sf_format` | Thumbnails (Slug) |
→ Abbildung auf `routes/admin.php` (bereits zu 80 % vorhanden) + ein paar Livewire-Aktionen.
---
## 5. Admin-Menü (`config/adminMenu/hnav.yml`)
```
Home
├─ Master data
│ ├─ Countries
│ ├─ Salutations
│ └─ Blacklists
├─ User CP
│ ├─ Users
│ ├─ Groups
│ ├─ Permissions
│ └─ Newsletter subscription
├─ Billing
│ ├─ Payment options
│ ├─ Coupons
│ └─ Invoices
├─ Portal CP
│ ├─ Press releases
│ ├─ Press release images
│ ├─ Categories
│ ├─ Companies
│ └─ Contacts
└─ Promotions
├─ Promotion links
└─ Footer code
```
→ Diese Struktur ist im neuen Laravel-Projekt bereits in der `sidebar.blade.php`-Navigation abgebildet.
---
## 6. Cron-Scripts (`cron_script_server.sh`)
```bash
php symfony sfp:user-payment-create --application=frontend
php symfony sfp:invoice-create --application=frontend --culture=de
php symfony sfp:invoice-reminder --application=frontend --culture=de
# php symfony sfp:user-paymentoption-expire --application=frontend (deaktiviert)
```
Dazu `sfpCron::index` (HTTP-getriggert, sehr unsicher: Shared-Secret als URL-Parameter).
**Ziel-Mapping:**
| Legacy-Task | Neu |
|---|---|
| `sfp:user-payment-create` | Job `CreateRecurringPayments` (daily) |
| `sfp:invoice-create` | Job `GenerateInvoices` (daily) |
| `sfp:invoice-reminder` | Job `SendInvoiceReminders` (daily) |
| `sfp:user-paymentoption-expire` | Job `ExpirePaymentOptions` (daily) |
Alles über Laravel Scheduler → `routes/console.php`.
---
## 7. API (aus `lib/ApiActions.class.php` + Frontend-Modulen)
- **Auth**: Statischer `api_key` im User-Profil (String, SHA1-base64).
- **CSRF**: Für API-Requests in `prepareForm()` deaktiviert.
- **Endpoints** (exakte Liste siehe [`07-API-MIGRATION.md`](./07-API-MIGRATION.md)):
- `POST /pressrelease/create` neue PM anlegen
- `POST /pressreleaseimage/upload` Bild hochladen
- `GET /pressrelease/list` PMs des Users
- `GET /company/list`
- `POST /newsletter/subscribe`
-
---
## 8. Mailer (`lib/bpMailer.class.php`)
- Templating via `get_partial()` in Symfony.
- Versand via SwiftMailer.
→ Ersetzt durch:
- Laravel `Mailable` + Blade-Templates
- Queues (`mail` queue)
---
## 9. Frontend-Assets / Thumbnails
- URL-Format: `/thumbnails/pressreleaseimage/thumb_200/pr/<slug>-<id>.jpg`
- Generator: `sfImageTransformPlugin`
- Pfade referenzieren Slugs → bei Slug-Änderung Broken-Links
→ Ersatz: `intervention/image` + `/storage/images/...` + `public/thumbnails/...`. Für alte URLs **302-Redirect-Middleware**.
---
## 10. Vorarbeiten im Repository (nicht verlieren!)
1. **`_businessportal24.com/dev/`** Bereits extrahiertes Core-Package mit DTOs, Enums, Value Objects, Domain-Events. Inhalte prüfen + dort wo sinnvoll in Laravel-App übernehmen.
2. **`_businessportal24.com/dev/migration/`** Vorhandene Migrations-Doku (CHECKLIST, IMPLEMENTATION etc.) als Referenz nutzen, aber **nicht mehr pflegen**.
3. **`dev/_old/Models/`** & **`dev/_old/migrations/`** Früher automatisch erzeugte Eloquent-Modelle/-Migrations aus dem alten Schema (2024). Wert: Nützliches Mapping der Spalten-/Typ-Namen. **NICHT** 1:1 übernehmen Schema wird modernisiert.
---
## 11. „Kritische" Stellen (Achtung bei Migration)
| Datei / Ort | Problem |
|---|---|
| `PressePortalPlugin/lib/helper/PaypalCredentialsHelper.php` (oder vergleichbar) | Hart-kodierte PayPal-Credentials → entfällt (nur Stripe) |
| `sfpCron/actions/actions.class.php` | Shared Secret `G8ZvEbnP8fEPfnWX4L` in URL; `exec()`-Call → entfällt |
| `sfGuardUser.password`-Feld | `sha1($salt.$password)` → Laravel bcrypt, Legacy-Hasher nötig |
| `Company.ctype` enum | Inheritance-Feld; bei Migration entscheiden, ob `type enum('agency','company')` oder Single-Table |
| `sfGuardUserProfile.api_key` | Wird von API-Clients genutzt → Umwandlung nach Sanctum + Keep-Alive |
| `PressRelease.text` | `string(40000)` in MySQL 8 auf `TEXT` oder `MEDIUMTEXT` mappen |
| Thumbnails mit `/slug-id.ext` | Bei Slug-Änderung 404; neues Schema sollte `id` primär nutzen, Slug redirectbar |
| Mehrsprachigkeit `I18n` behavior | Aktuell `de` + `en` Daten in `*_translation`-Tabellen siehe [`04-DATA-MODEL.md`](./04-DATA-MODEL.md) |