11 KiB
11 KiB
🎉 Laravel Event-Portal - Vollständige Implementierung
Dieses Projekt ist ein vollständig arbeitsfertiges Event-Portal für Dresden mit Integration von externen Veranstaltungsquellen.
📋 Projektstruktur
Veranstaltungen-APP/
├── app/
│ ├── Models/
│ │ ├── Source.php # Quelle (z.B. Stadt Dresden)
│ │ ├── Event.php # Veranstaltung
│ │ └── EventOccurrence.php # Einzelne Termine
│ ├── Http/Controllers/
│ │ └── EventController.php # REST API Controller
│ ├── Jobs/
│ │ └── ImportEventsJob.php # Queue Job für Import
│ ├── Commands/
│ │ └── ImportEventsCommand.php # Artisan Command für manuellen Import
│ ├── Services/
│ │ └── EventImportService.php # Business Logic Service
│
├── database/
│ └── migrations/
│ ├── 2026_04_09_000001_create_sources_table.php
│ ├── 2026_04_09_000002_create_events_table.php
│ └── 2026_04_09_000003_create_event_occurrences_table.php
│
├── routes/
│ └── api.php # REST API Routen
│
├── docs/
│ ├── SETUP.md # Diese Datei
│ ├── EXAMPLE_QUERIES.php # Eloquent Query-Beispiele
│ ├── API_RESPONSES.md # API Response-Formate
│ ├── IMPORT_SCRAPER_INTEGRATION.md # Import-Dokumentation
│ └── KERNEL_SCHEDULER_EXAMPLE.php # Scheduler-Konfiguration
⚙️ Installation & Setup
1. Frisches Laravel-Projekt erstellen
# Laravel 11 LTS (oder aktuelle LTS)
composer create-project laravel/laravel Veranstaltungen-APP
cd Veranstaltungen-APP
2. Diese Dateien in das Projekt kopieren
# Kopiere alle PHP/Migration-Dateien aus diesem Package
# in die entsprechenden Verzeichnisse
# Beispiel:
cp app/Models/*.php ./app/Models/
cp app/Http/Controllers/*.php ./app/Http/Controllers/
cp app/Jobs/*.php ./app/Jobs/
cp app/Commands/*.php ./app/Commands/
cp app/Services/*.php ./app/Services/
cp database/migrations/*.php ./database/migrations/
cp routes/api.php ./routes/
3. Umgebungsvariablen konfigurieren
# .env erstellen
cp .env.example .env
# Schüssel generieren
php artisan key:generate
Bearbeite .env:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=veranstaltungen_app
DB_USERNAME=root
DB_PASSWORD=
QUEUE_CONNECTION=database
MAIL_FROM_ADDRESS=noreply@veranstaltungen-app.de
4. Datenbank & Migrations
# Datenbank erstellen (MariaDB)
mysql -u root -p -e "CREATE DATABASE veranstaltungen_app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
# Migrations ausführen
php artisan migrate
# Optionale Fixtures/Seeds laden
php artisan db:seed --class=SourceSeeder
5. Queue für Imports vorbereiten
# Queue-Tabelle erstellen
php artisan queue:table
php artisan migrate
# Queue Worker starten (Development)
php artisan queue:work --verbose
🚀 Erste Schritte
Event-Quellen erstellen
# Interaktiv via Artisan Tinker
php artisan tinker
>>> $source = \App\Models\Source::create([
... 'name' => 'Stadt Dresden',
... 'description' => 'Offizielle Veranstaltungen der Stadt',
... 'url' => 'https://stadt-dresden.de',
... 'status' => 'active',
... ]);
Events importieren
# Manueller Import (blockierend)
php artisan events:import --sync
# Oder asynchron in Queue
php artisan events:import
# Queue Worker muss laufen für Verarbeitung:
php artisan queue:work
API testen
# Events auflisten
curl "http://localhost:8000/api/events?from=2026-04-15&to=2026-05-31&limit=10"
# Ein Event anzeigen
curl "http://localhost:8000/api/events/1"
# Verfügbare Kategorien
curl "http://localhost:8000/api/events/categories/list"
# Verfügbare Orte
curl "http://localhost:8000/api/events/locations/list"
📚 Dokumentation
Für Event-Queries siehe:
Für API-Endpoints siehe:
Für Import/Scraper-Integration siehe:
👉 IMPORT_SCRAPER_INTEGRATION.md
Für Scheduler-Setup siehe:
👉 KERNEL_SCHEDULER_EXAMPLE.php
🔑 API-Endpoints
| Methode | Endpoint | Beschreibung |
|---|---|---|
| GET | /api/events |
Events mit Filtern auflisten |
| GET | /api/events/{id} |
Einzelnes Event anzeigen |
| GET | /api/events/categories/list |
Verfügbare Kategorien |
| GET | /api/events/locations/list |
Verfügbare Orte |
Filter-Parameter
GET /api/events
?from=2026-04-15 # Ab Datum (YYYY-MM-DD), Standard: heute
&to=2026-05-31 # Bis Datum (YYYY-MM-DD), Standard: +3 Monate
&category=Kultur # Nach Kategorie filtern
&location=Dresden # Nach Ort filtern
&limit=20 # Ergebnisse pro Seite (1-100, Standard: 20)
🎯 Datenmodell
Events (Veranstaltungen)
id- Eindeutige IDsource_id- Referenz zur Quelleexternal_id- ID der Quelletitle- Name der Veranstaltungdescription- Beschreibunglocation- Ort/Stadtcategory- Kategorie (z.B. Kultur, Sport)slug- URL-freundlicher Nameimage_url,website_url,contact_email,contact_phonestatus- draft | published | archivedcreated_at,updated_at
Event Occurrences (Termine)
id- Eindeutige IDevent_id- Referenz zum Eventstart_datetime- Startzeitend_datetime- Endzeitis_all_day- Ganztägig?location_details- Raum/Gebäudecapacity- Kapazitätavailable_tickets- Verfügbare Ticketsprice- Preis (optional)status- scheduled | cancelled | completed
Sources (Quellen)
id- Eindeutige IDname- Name der Quelledescription- Beschreibungurl- Website URLstatus- active | inactivelast_import_at- Letzter Import-Zeitpunkt
🔄 Import-Workflow
1. External Source (z.B. Stadt Dresden API)
↓
2. ImportEventsCommand / EventImportService
↓
3. ImportEventsJob (läuft in Queue)
↓
4. fetchExternalEvents() - Ruft externe Daten ab
↓
5. upsertEvent() - Erstellt oder aktualisiert Event+Occurrences
↓
6. Database (MySQL/MariaDB)
↓
7. API (für Frontend verfügbar)
Upsert-Logik
- Events werden anhand
[source_id, external_id]abgeglichen - Existierende Events = Update
- Neue Events = Insert
- Verhindert Duplikate durch Unique Index
⏰ Geplante Imports (Scheduler)
In app/Console/Kernel.php (siehe Beispiel):
- 03:00 Uhr - Täglich alle Quellen importieren
- Stündlich - Stadt-Dresden-Quelle (häufige Updates)
- Alle 6 Stunden - Andere Quellen
- 04:00 Uhr - Markiere abgelaufene Termine
- Sonntag 05:00 - Räume archivierte Events auf
🛠️ Commands
# Event-Import
php artisan events:import [--source=ID|Name] [--sync]
# Queue einrichten
php artisan queue:table
php artisan migrate
# Queue Worker starten (Development)
php artisan queue:work [--verbose] [--tries=3] [--timeout=120]
# Failed Jobs anzeigen
php artisan queue:failed
php artisan queue:retry ID
php artisan queue:forget ID
# Alle Jobs leeren
php artisan queue:flush
# Cache leeren
php artisan cache:clear
# Logs leeren
php artisan log:prune
# Datenbank frisch seeden
php artisan migrate:refresh --seed
📊 Datenbank-Indizes
Die Migrationen erstellen folgende Indizes für Performance:
sources:
status(Filter nach aktiv/inaktiv)created_at(Sortierer)
events:
source_id(Foreign Key)slug(Unique, für SEO-URLs)[location, status](Composite Index für Location-Filter)[category, status](Composite Index für Kategorie-Filter)created_at(Neueste zuerst)[source_id, external_id](Unique, verhindert Duplikate)
event_occurrences:
event_id(Foreign Key)start_datetime(Filter nach Datum)[start_datetime, status](Composite Index für "nächste Events")[event_id, status](Filter nach Event & Status)
🔐 Security-Best-Practices
✅ Implementiert:
- SQL-Injections vermieden (Eloquent ORM)
- CSRF-Schutz (Laravel Standard)
- Rate Limiting für APIs
- Input Validation in Controllers
- Soft Deletes für Datenintegrität
⚠️ Zu implementieren:
- API-Authentifizierung (Laravel Passport/Sanctum)
- Request Throttling
- CORS-Konfiguration
- Content Security Policy
🐛 Troubleshooting
Migrations schlagen fehl
# Checke MariaDB Version
mysql --version
# Migrations zurückrollen
php artisan migrate:reset
# Neu starten
php artisan migrate
Queue-Jobs werden nicht verarbeitet
# Worker-Prozess läuft?
ps aux | grep "queue:work"
# Queue starten (Development)
php artisan queue:work --verbose
API gibt 404 zurück
# Checke Routes
php artisan route:list
# Starte Server
php artisan serve
Zu viel Memory-Verbrauch
# Optimize Autoloader
composer install --optimize-autoloader --no-dev
# Disable Query Logging in Production
# In .env: APP_DEBUG=false
📈 Production Deployment
Vorbereitung
# .env für Production
APP_ENV=production
APP_DEBUG=false
QUEUE_CONNECTION=redis # oder beanstalkd
LOG_CHANNEL=stack
Cron-Job einrichten (Scheduler)
# /etc/cron.d/laravel-scheduler
* * * * * cd /path/to/app && php artisan schedule:run >> /dev/null 2>&1
Oder mit systemd:
# supervisor für Queue Workers
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /path/to/app/artisan queue:work redis --sleep=3 --tries=3 --timeout=90
autostart=true
autorestart=true
numprocs=4
redirect_stderr=true
stdout_logfile=/path/to/app/storage/logs/worker.log
🤝 Weitere Integration
Beispiel: Import aus Stadt-Dresden-API
Bearbeite app/Jobs/ImportEventsJob.php:
protected function fetchExternalEvents()
{
$response = Http::withHeaders([
'Accept' => 'application/json',
])->get('https://api.stadt-dresden.de/events', [
'limit' => 1000,
]);
return $response->json('data');
}
Beispiel: Web-Scraping
composer require symfony/dom-crawler symfony/http-client
Dann in Import-Service:
use Symfony\Component\DomCrawler\Crawler;
$response = Http::get('https://example.com/events');
$crawler = new Crawler($response->body());
// ... scrape & extract events
📞 Support & Weitere Ressourcen
Version: 1.0
Laravel: 11 LTS
PHP: 8.2+
Database: MariaDB 10.4+
Erstellt: 9. April 2026