Moderne TYPO3 Anwendungen müssen oft in Echtzeit mit externen Diensten kommunizieren. Ob Sie nun Headless-Setups erstellen, Daten mit APIs von Drittanbietern synchronisieren oder automatisierte Workflows auslösen, Webhooks sind unerlässlich, um Systeme miteinander zu verbinden.
Dieser Leitfaden bietet Ihnen eine bessere Lösung: die Verwendung des modernen PSR-14 Event Dispatchers von TYPO3. Anstatt veralteten Code oder benutzerdefinierte Hacks zu verwenden, lernen Sie, wie Sie Echtzeit-Webhook-Trigger einrichten, die saubere JSON-Daten an jeden externen Dienst senden - genau dann, wenn ein Datensatz in TYPO3 aktualisiert wird.
Was sind TYPO3 Webhooks?
TYPO3 Webhooks sind eine Möglichkeit für Ihre TYPO3 Website, Daten in Echtzeit an ein anderes System zu senden, wenn sich etwas ändert, z. B. wenn eine Seite aktualisiert wird, ein neuer Artikel veröffentlicht wird oder ein Datensatz gelöscht wird.
Das TYPO3 Content Management System (CMS) unterstützt Webhooks, die es Ihrer Website ermöglichen, automatisch Daten an ein anderes System zu senden, wenn etwas passiert - ohne jeglichen manuellen Aufwand.
Stellen Sie sich einen Webhook wie einen digitalen Boten vor. Wenn ein bestimmtes Ereignis auf Ihrer TYPO3-Website eintritt, z. B. wenn ein Benutzer ein Formular ausfüllt, kann ein Webhook ausgelöst werden, um diese Information an ein anderes System zu senden. Dieses System könnte sein:
- Ein CRM (wie HubSpot oder Salesforce),
- ein E-Mail-Dienst
- Oder sogar eine Chat-App wie Slack oder Microsoft Teams.
Der Webhook sendet eine Nachricht (Payload genannt) in Echtzeit an eine bestimmte URL auf dem externen System. So kann TYPO3 Aufgaben automatisieren, Workflows auslösen und mit anderen Tools integrieren, ohne ständig nach Updates suchen oder Daten manuell pushen zu müssen.
Voraussetzungen zum Auslösen von Custom Webhooks
- TYPO3 v10.4+ (PSR-14 Unterstützung)
- Grundlegende PHP-Kenntnisse
- Verständnis für die Entwicklung von TYPO3 Extensions
- Vertrautheit mit JSON und HTTP-Anfragen
Der PSR-14-Ereignis-Dispatcher von TYPO3 im Überblick
Der PSR-14 Event Dispatcher ist das moderne Event-System von TYPO3, das in Version 10 die alten Hooks ersetzt hat. Es bietet eine standardisierte Möglichkeit, auf Ereignisse im TYPO3 Kern und in den Erweiterungen zu warten und darauf zu reagieren.
Warum PSR-14 im Vergleich zu Legacy Hooks?
- Typsicherheit: Ereignisse sind PHP-Objekte mit definierten Eigenschaften
- Bessere Leistung: Effizienter als String-basierte Hooks
- Moderne Architektur: Entspricht den PSR-14-Standards
- Zukunftssicher: Der empfohlene Ansatz für TYPO3 v10+
Einrichten Ihrer Erweiterung
Lassen Sie uns zunächst die grundlegende Struktur für unsere Webhook-Erweiterung erstellen.
Verzeichnisstruktur
ext_webhook/
├── Classes/
│ ├── Event/
│ │ └── WebhookEvent.php
│ ├── EventListener/
│ │ └── WebhookEventListener.php
│ └── Service/
│ └── WebhookService.php
├── Configuration/
│ └── Services.yaml
└── ext_emconf.php
<?php
$EM_CONF['ext_webhook'] = [
'title' => 'TYPO3 Webhook Integration',
'description' => 'Send webhooks on record updates using PSR-14',
'category' => 'misc',
'version' => '1.0.0',
'state' => 'stable',
'author' => 'Your Name',
'author_email' => 'your.email@example.com',
'constraints' => [
'depends' => [
'typo3' => '10.4.0-11.5.99',
],
],
];
Erstellen des Webhook-Ereignisses
Lassen Sie uns ein benutzerdefiniertes Ereignis erstellen, das unsere Webhook-Daten übertragen wird.
Klassen/Ereignis/WebhookEvent.php
<?php
declare(strict_types=1);
namespace Vendor\ExtWebhook\Event;
final class WebhookEvent
{
private string $table;
private int $uid;
private array $recordData;
private string $action;
private string $webhookUrl;
public function __construct(
string $table,
int $uid,
array $recordData,
string $action,
string $webhookUrl
) {
$this->table = $table;
$this->uid = $uid;
$this->recordData = $recordData;
$this->action = $action;
$this->webhookUrl = $webhookUrl;
}
public function getTable(): string
{
return $this->table;
}
public function getUid(): int
{
return $this->uid;
}
public function getRecordData(): array
{
return $this->recordData;
}
public function getAction(): string
{
return $this->action;
}
public function getWebhookUrl(): string
{
return $this->webhookUrl;
}
public function getPayload(): array
{
return [
'event' => 'record_' . $this->action,
'table' => $this->table,
'uid' => $this->uid,
'data' => $this->recordData,
'timestamp' => time(),
];
}
}
Erstellung des Webhook-Dienstes
Lassen Sie uns nun einen Dienst erstellen, der die eigentliche Webhook-Übertragung übernimmt.
Klassen/Service/WebhookService.php
<?php
declare(strict_types=1);
namespace Vendor\ExtWebhook\Service;
use TYPO3\CMS\Core\Http\RequestFactory;
use TYPO3\CMS\Core\Log\LogManager;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use Psr\Log\LoggerInterface;
use Vendor\ExtWebhook\Event\WebhookEvent;
class WebhookService
{
private RequestFactory $requestFactory;
private LoggerInterface $logger;
public function __construct(RequestFactory $requestFactory, LogManager $logManager)
{
$this->requestFactory = $requestFactory;
$this->logger = $logManager->getLogger(__CLASS__);
}
public function sendWebhook(WebhookEvent $event): bool
{
$url = $event->getWebhookUrl();
$payload = $event->getPayload();
try {
$response = $this->requestFactory->request(
$url,
'POST',
[
'headers' => [
'Content-Type' => 'application/json',
'User-Agent' => 'TYPO3-Webhook/1.0',
],
'json' => $payload,
'timeout' => 30,
]
);
$statusCode = $response->getStatusCode();
if ($statusCode >= 200 && $statusCode < 300) {
$this->logger->info('Webhook sent successfully', [
'url' => $url,
'table' => $event->getTable(),
'uid' => $event->getUid(),
'status' => $statusCode,
]);
return true;
} else {
$this->logger->warning('Webhook failed with HTTP error', [
'url' => $url,
'status' => $statusCode,
'response' => $response->getBody()->getContents(),
]);
return false;
}
} catch (\Exception $e) {
$this->logger->error('Webhook delivery failed', [
'url' => $url,
'error' => $e->getMessage(),
'payload' => $payload,
]);
return false;
}
}
public function sendWebhookWithRetry(WebhookEvent $event, int $maxRetries = 3): bool
{
$attempt = 1;
while ($attempt <= $maxRetries) {
if ($this->sendWebhook($event)) {
return true;
}
if ($attempt < $maxRetries) {
$delay = pow(2, $attempt); // Exponential backoff
sleep($delay);
$this->logger->info("Retrying webhook delivery (attempt $attempt/$maxRetries)");
}
$attempt++;
}
$this->logger->error('Webhook delivery failed after all retries', [
'url' => $event->getWebhookUrl(),
'attempts' => $maxRetries,
]);
return false;
}
}
Erstellen des Ereignis-Listeners
Der Event-Listener wird auf die in TYPO3 eingebauten Events reagieren und unsere Webhooks auslösen.
Klassen/EventListener/WebhookEventListener.php
<?php
declare(strict_types=1);
namespace Vendor\ExtWebhook\EventListener;
use TYPO3\CMS\Core\DataHandling\Event\AfterRecordUpdatedEvent;
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use Vendor\ExtWebhook\Event\WebhookEvent;
use Vendor\ExtWebhook\Service\WebhookService;
use Psr\EventDispatcher\EventDispatcherInterface;
final class WebhookEventListener
{
private WebhookService $webhookService;
private EventDispatcherInterface $eventDispatcher;
private array $configuration;
public function __construct(
WebhookService $webhookService,
EventDispatcherInterface $eventDispatcher
) {
$this->webhookService = $webhookService;
$this->eventDispatcher = $eventDispatcher;
// Load extension configuration
$this->configuration = GeneralUtility::makeInstance(ExtensionConfiguration::class)
->get('ext_webhook') ?? [];
}
public function onAfterRecordUpdated(AfterRecordUpdatedEvent $event): void
{
$table = $event->getTable();
$uid = $event->getUid();
$recordData = $event->getRecord();
// Check if webhooks are enabled for this table
if (!$this->shouldSendWebhook($table)) {
return;
}
// Get webhook URL from configuration
$webhookUrl = $this->getWebhookUrl($table);
if (empty($webhookUrl)) {
return;
}
// Create and dispatch webhook event
$webhookEvent = new WebhookEvent(
$table,
$uid,
$recordData,
'updated',
$webhookUrl
);
// Send webhook asynchronously (or synchronously based on configuration)
if ($this->configuration['async_webhooks'] ?? false) {
// In a real implementation, you'd queue this for background processing
// For now, we'll send it synchronously
$this->webhookService->sendWebhookWithRetry($webhookEvent);
} else {
$this->webhookService->sendWebhookWithRetry($webhookEvent);
}
}
private function shouldSendWebhook(string $table): bool
{
$enabledTables = GeneralUtility::trimExplode(
',',
$this->configuration['enabled_tables'] ?? 'pages,tt_content',
true
);
return in_array($table, $enabledTables, true);
}
private function getWebhookUrl(string $table): string
{
// You can customize this logic to support multiple URLs per table
return $this->configuration['webhook_url'] ?? '';
}
}
Registrierung von Diensten und Ereignis-Listenern
Konfigurieren Sie die Abhängigkeitsinjektion und die Ereignis-Listener.
Konfiguration/Dienste.yaml
services:
_defaults:
autowire: true
autoconfigure: true
public: false
Vendor\ExtWebhook\:
resource: '../Classes/*'
Vendor\ExtWebhook\EventListener\WebhookEventListener:
tags:
- name: event.listener
identifier: 'webhook-after-record-updated'
method: 'onAfterRecordUpdated'
event: TYPO3\CMS\Core\DataHandling\Event\AfterRecordUpdatedEvent
# cat=webhook/enable/10; type=string; label=Webhook URL: The URL to send webhooks to
webhook_url = https://example.com/webhook
# cat=webhook/tables/20; type=string; label=Enabled Tables: Comma-separated list of tables to monitor (e.g., pages,tt_content,news)
enabled_tables = pages,tt_content
# cat=webhook/performance/30; type=boolean; label=Async Webhooks: Send webhooks asynchronously (requires queue setup)
async_webhooks = 0
Testen Ihrer Webhook-Implementierung
1. Installieren und konfigurieren
- Installieren Sie Ihre Erweiterung
- Konfigurieren Sie die Webhook-URL in der Erweiterungskonfiguration
- Richten Sie die Tabellen ein, die Sie überwachen wollen
2. Testen Sie die Webhook-Zustellung
Erstellen Sie einen einfachen Test-Endpunkt, um Webhooks zu empfangen:
// webhook-test.php
<?php
$json = file_get_contents('php://input');
$data = json_decode($json, true);
file_put_contents('webhook.log', date('Y-m-d H:i:s') . ": " . $json . "\n", FILE_APPEND);
http_response_code(200);
echo "OK";
3. Überprüfen in TYPO3
- Bearbeiten Sie eine Seite oder ein Inhaltselement
- Überprüfen Sie Ihre Webhook-Endpunkt-Protokolle
- Überprüfen Sie die Struktur der JSON-Nutzdaten
Erweiterte Funktionen
Mehrere Webhook-URLs
Erweitern Sie die Konfiguration, um verschiedene URLs pro Tabelle zu unterstützen:
private function getWebhookUrl(string $table): string
{
$urls = [
'pages' => $this->configuration['webhook_url_pages'] ?? '',
'tt_content' => $this->configuration['webhook_url_content'] ?? '',
'default' => $this->configuration['webhook_url'] ?? '',
];
return $urls[$table] ?? $urls['default'];
}
Add security by signing your webhooks:
private function signPayload(array $payload, string $secret): string
{
return hash_hmac('sha256', json_encode($payload), $secret);
}
Queue-Integration
Für Websites mit hohem Besucheraufkommen bietet sich die Integration mit dem Warteschlangensystem von TYPO3 an:
use TYPO3\CMS\Core\Messaging\AbstractMessage;
use TYPO3\CMS\Core\Messaging\FlashMessage;
use TYPO3\CMS\Core\Messaging\FlashMessageService;
// In your event listener
$this->queueService->add('webhook_queue', $webhookEvent);
Fehlersuche bei allgemeinen Problemen
Webhooks werden nicht ausgelöst
- Überprüfen Sie die Ereignisregistrierung: Überprüfen der Services.yaml-Konfiguration
- Tabellen-Überwachung: Sicherstellen, dass die Tabelle in enabled_tables enthalten ist
- URL-Konfiguration: Überprüfen Sie, ob die Webhook-URL zugänglich ist
Performance-Probleme
- Aktivieren Sie die asynchrone Verarbeitung: Setzen Sie async_webhooks = 1
- Warteschlangen implementieren: Verwenden Sie das Warteschlangensystem von TYPO3 für hohes Volumen
- Timeouts hinzufügen: Konfigurieren Sie geeignete HTTP-Timeouts
Tipps zur Fehlersuche
// Add debug logging
$this->logger->debug('Webhook triggered', [
'table' => $table,
'uid' => $uid,
'url' => $webhookUrl,
]);
Bewährte Praktiken
- Fehlerbehandlung: Implementieren Sie immer eine Wiederholungslogik und eine angemessene Fehlerbehandlung
- Sicherheit: HTTPS verwenden und Webhook-Signaturen berücksichtigen
- Leistung: Verwenden Sie asynchrone Verarbeitung für Websites mit hohem Datenaufkommen
- Überwachung: Protokollieren Sie Webhook-Lieferungen zur Fehlersuche
- Konfiguration: Webhook-URLs pro Umgebung konfigurierbar machen
Fazit
Der PSR-14 Event Dispatcher von TYPO3 bietet eine leistungsfähige, moderne Möglichkeit, Webhook-Funktionalität zu implementieren. Mit diesem Leitfaden haben Sie gelernt, wie man:
- Benutzerdefinierte Events und Listener erstellen
- Ein robustes Webhook-Zustellungssystem aufzubauen
- Fehler und Wiederholungsversuche zuverlässig zu behandeln
- Ihre Implementierung zu konfigurieren und zu testen
Dieser Ansatz ersetzt veraltete Hooks durch eine typsichere, performante Lösung, die perfekt für Headless TYPO3-Setups und Integrationen von Drittanbietern geeignet ist.
Nächste Schritte
- Andere PSR-14-Ereignisse für verschiedene Auslöser erforschen
- Warteschlangenbasierte asynchrone Verarbeitung implementieren
- Webhook-Signaturüberprüfung hinzufügen
- Erstellen eines Backend-Moduls für die Webhook-Verwaltung
Der PSR-14 Event Dispatcher eröffnet unendlich viele Möglichkeiten, die Funktionalität von TYPO3 zu erweitern. Experimentieren Sie mit verschiedenen Ereignissen und bauen Sie die Integrationen, die Ihre Projekte benötigen!
Post a Comment