Skip to content

Events & Flow Builder

Die Erweiterung versendet genau ein eigenes Event: eine Meldung über einen abgeschlossenen Auftrag, auf die Sie im Flow Builder oder in PHP reagieren. Außerdem registriert sie zwei geplante Aufgaben und zwei Subscriber.

Flow-Builder-Trigger

Nice\Translate\Job\Event\TranslationJobFinishedEvent ist ein registriertes Business Event, deshalb erscheint es in der Trigger-Liste des Flow Builders.

AspektWert
Technischer Namenice_translate.job.finished
Bezeichnung in der AdministrationÜbersetzungsauftrag abgeschlossen
Trigger-PfadKI-ÜbersetzungAuftragAbgeschlossen
KlasseNice\Translate\Job\Event\TranslationJobFinishedEvent
BasisklasseSymfony\Contracts\EventDispatcher\Event

Es implementiert drei Aware-Interfaces:

InterfaceAuswirkung
Shopware\Core\Framework\Event\FlowEventAwareDas Event kann überhaupt ein Flow-Trigger sein
Shopware\Core\Content\Flow\Dispatching\Aware\ScalarValuesAwareDie Erweiterung speichert die sechs Payload-Werte mit der Flow-Sequenz, deshalb hat auch eine gespeicherte, verzögerte Flow-Ausführung sie noch
Shopware\Core\Framework\Event\MailAwareDie Mail-Aktion von Shopware adressiert den Ersteller des Auftrags, und Sie konfigurieren keinen Empfänger

getSalesChannelId() liefert immer null. Übersetzungsaufträge sind Arbeit auf Administrationsebene und gehören zu keinem Verkaufskanal.

Der Flow-Builder-Editor mit dem Trigger für den abgeschlossenen Übersetzungsauftrag der KI-Übersetzung

Payload

getAvailableData() stellt genau diese sechs Skalare in dieser Reihenfolge bereit. getValues() liefert dieselben sechs Schlüssel, und dadurch stehen sie auch einer verzögerten Flow-Ausführung und einer E-Mail-Vorlage zur Verfügung.

EigenschaftTypBedeutung
jobIdstringDie Hex-UUID der Zeile in nice_translate_job
titlestringDer Auftragstitel, wie ihn die Auftragsliste zeigt
statusstringDer Endstatus, den der Auftrag erreicht hat
processedItemsintDie Datensätze, die die Erweiterung übersetzt hat
failedItemsintDie Datensätze, die fehlgeschlagen sind
skippedItemsintDie Datensätze, die die Schutzprüfungen übersprungen haben

Die Erweiterung schließt einen Auftrag ab, sobald processedItems + failedItems + skippedItems die geplante Gesamtzahl erreicht. Die drei Zähler decken zusammen deshalb den gesamten Lauf ab. Ein Datensatz gilt als übersprungen, wenn eine Schutzprüfung sein Überschreiben abgelehnt hat. Siehe Schutzmechanismen.

Wann es auslöst

Das Event trägt einen von drei Status:

StatusWird erreicht, wenn
completedDie Erweiterung jeden Datensatz verarbeitet hat und keiner fehlgeschlagen ist
completed_with_errorsDer Auftrag mit mindestens einem fehlgeschlagenen Datensatz endete
failedDie Vorbereitung eines wartenden Auftrags fehlschlug, bevor ein Batch lief

Der Finalizer der Batches erzeugt nur die ersten beiden Status. failed entsteht ausschließlich über JobProgressUpdater::failQueued(), der seinen eigenen Snapshot in dieselbe Outbox schreibt.

Für einen cancelled-Auftrag löst das Event nicht aus. Die Automatisierung beim Speichern löst es ebenfalls nicht aus, denn sie übersetzt ohne einen Auftrag. Siehe Automatisierung.

Der Finalizer schreibt den Abschluss-Snapshot in die Outbox nice_translate_finished_event, in derselben Transaktion, die den Auftrag abschließt, und er versendet das Event nie selbst. JobFinishedEventRelay nimmt den Snapshot nach diesem Commit auf. Die Zustellung erfolgt deshalb mindestens einmal: Ein Versand, der eine Exception wirft, behält seinen Lease, und die geplante Wiederherstellung versucht ihn nach Ablauf des Stale-Fensters erneut. Wenn Ihr Listener Seiteneffekte hat, deduplizieren Sie über jobId.

E-Mail-Empfänger

Das Relay leitet den Empfänger vom Ersteller des Auftrags ab, also von dem Shopware-Benutzer, auf den nice_translate_job.created_by_id verweist. Diese Adresse reist mit dem Event, und deshalb erreicht eine Mail-Aktion im Flow den Ersteller ohne weitere Konfiguration.

Achtung

Die geplante Aufgabe legt ihre Aufträge in einem Systemkontext an, und diese Aufträge haben keinen Ersteller. Die Empfängerliste bleibt für sie deshalb leer. Konfigurieren Sie in Ihrer Mail-Aktion einen festen Empfänger, wenn Sie eine Benachrichtigung über die geplanten Läufe möchten.

Einen Flow erstellen

Diese Schritte bauen einen Flow, der die Aufträge meldet, die mit Fehlern abgeschlossen wurden:

  1. Öffnen Sie in der Administration den Flow Builder und legen Sie einen neuen Flow an. Geben Sie ihm einen Namen wie Übersetzungsauftrag abgeschlossen.
  2. Wählen Sie als Trigger KI-ÜbersetzungAuftragAbgeschlossen aus. Die Liste führt ihn als Übersetzungsauftrag abgeschlossen.
  3. Fügen Sie dem Flow eine Mail-Aktion hinzu. Das Event ist MailAware, deshalb adressiert die Aktion den Ersteller des Auftrags. Sie akzeptiert auch alle Administratoren oder eine feste Adresse.
  4. Verzweigen Sie in der E-Mail-Vorlage über das Payload, damit ein sauberer Lauf keine E-Mail versendet:
twig
{% if failedItems > 0 %}
    Übersetzungsauftrag "{{ title }}" wurde mit Status {{ status }} beendet.
    {{ processedItems }} verarbeitet, {{ failedItems }} fehlgeschlagen, {{ skippedItems }} übersprungen.
    Fehlerliste in der Auftragsdetailansicht öffnen: Auftrags-ID {{ jobId }}.
{% endif %}

Der Trigger löst für jeden Auftrag aus, der einen Endstatus erreicht, und er hat keinen eigenen Filter. Die Bedingung gehört deshalb in die Vorlage oder in einen PHP-Listener.

Geplante Aufgaben

Die Erweiterung registriert zwei geplante Aufgaben mit dem Tag shopware.scheduled.task. Beide brauchen einen laufenden bin/console scheduled-task:run-Worker.

AufgabenklasseAufgabennameStandardintervall
AutoTranslateTasknice_translate.auto_translate3600 s (1 Stunde)
BatchDispatchTasknice_translate.dispatch_batches60 s (1 Minute)

nice_translate.auto_translate

Die Aufgabe läuft stündlich und übersetzt höchstens einmal pro scheduledIntervalHours. AutoTranslateTaskHandler::run() führt bei jedem Durchlauf diese Schritte der Reihe nach aus:

  1. Verlauf aufräumen. Der Handler löscht die Zeilen in nice_translate_history, die älter als historyRetentionDays sind. Das geschieht bei jedem Durchlauf, mit eingeschalteter oder ausgeschalteter geplanter Übersetzung. Bei einem konfigurierten Wert unter 1 gilt eine Aufbewahrung von 30 Tagen.
  2. Abbrechen, wenn scheduledEnabled aus ist. Nichts unterhalb dieses Punktes läuft dann.
  3. Abbrechen, wenn seit scheduledLastRun das konfigurierte Intervall nicht verstrichen ist. Bei einem konfigurierten Wert unter 1 gilt für scheduledIntervalHours der Wert 24.
  4. Den Lauf vermerken und abbrechen, wenn scheduledEntities oder autoTranslateLanguageIds leer ist.
  5. Abbrechen, ohne den Lauf zu vermerken, wenn noch ein nice_translate_job den Status queued oder running hat. Der Lauf bleibt fällig, und der nächste Durchlauf prüft dasselbe erneut.
  6. Die Zielsprachen nach ihrem aufgelösten Anbieter gruppieren und je Anbietergruppe einen Auftrag anlegen. Ein Auftrag je Anbieter hält Validierung, Abrechnung und Auftragsbezeichnungen eindeutig. Die Aufträge nutzen den Scope missing, den Modus missing, skipUnchanged und protectManualEdits, und ihr Titel beginnt mit Scheduled (<provider label>): .
  7. scheduledLastRun als ISO-8601-Zeitstempel schreiben, nachdem der Handler mindestens einen Auftrag angelegt hat. Ein fälliger Lauf ohne angelegten Auftrag lässt den Zeitstempel unverändert, deshalb versucht es der nächste Durchlauf erneut.

Die Entitätenliste dieser Aufgabe ist scheduledEntities, eine eigene Einstellung neben den autoTranslateEntities, die die Automatisierung beim Speichern liest. Automatisierung und die Einstellungsreferenz dokumentieren beide Einstellungen.

nice_translate.dispatch_batches

BatchDispatchTaskHandler::run() ruft JobBatchDispatcher::recover() auf, danach JobFinishedEventRelay::recover(). Jeder Aufruf hat sein eigenes try/catch, das den Fehler protokolliert, deshalb blockiert ein fehlschlagender Wiederherstellungslauf den anderen Lauf nicht.

Diese Aufgabe macht die Ausfallsicherheitsschicht wirksam. Sie stellt die committeten Aufträge erneut in die Queue, deren Startnachricht den Transport nie erreicht hat, versendet die Batches mit unvollständigem Versand erneut, wiederholt die abgelaufenen Ansprüche auf Abschluss-Events und räumt die abgelaufenen Quittungen auf. Sie ist Infrastruktur und hat keine Einstellungen.

Subscriber

src/Job/Subscriber/ enthält zwei Subscriber, und beide sind als kernel.event_subscriber registriert.

KlasseReagiert auf
BusinessEventCollectorSubscriberBusinessEventCollectorEvent::NAME, Priorität 1000
EntityWrittenSubscriberproduct.written, category.written, product_manufacturer.written, cms_page.written

BusinessEventCollectorSubscriber definiert TranslationJobFinishedEvent und nimmt es in die Sammlung auf. Dadurch erscheint der Trigger im Flow Builder.

EntityWrittenSubscriber setzt die Automatisierung beim Speichern um. Bevor er etwas versendet, durchläuft er in dieser Reihenfolge eine Kette von Prüfungen:

  1. autoTranslateEnabled muss true sein.
  2. Der Kontext darf nicht den State nice_translate.writing tragen. Das ist der Schleifenschutz. Er hält die eigenen Schreibvorgänge der Erweiterung von einem zweiten Auslösen ab.
  3. Der Schreibvorgang muss in der Standardsprache des Systems erfolgt sein.
  4. Der Schreibvorgang muss in der Live-Version erfolgt sein.
  5. autoTranslateEntities muss die Entität enthalten.
  6. autoTranslateLanguageIds darf nicht leer sein.
  7. Der Subscriber verwirft die Ergebnisse von Löschvorgängen und dedupliziert die verbleibenden IDs.

Danach löst er je Zielsprache einen Anbieter auf, baut eine auftragslose JobConfig mit dem Scope selection, dem Modus missing, skipUnchanged und protectManualEdits, teilt die IDs anhand der konfigurierten Batch-Größe auf und registriert ein dauerhaftes Manifest vor dem Versand. Wenn er einen Anbieter nicht auflösen kann, protokolliert er das und überspringt diese Sprache. Einen fehlgeschlagenen Versand protokolliert er ebenfalls und überlässt ihn danach der Wiederherstellungsaufgabe.

TranslationJobFinishedEvent ist die einzige eigene Event-Klasse der Erweiterung, und sie versendet nichts anderes. Wenn Sie einen Hook brauchen, den es nicht gibt, ist das Service-Tag aus Eigene Anbieter meist die richtige Stelle.

In PHP darauf reagieren

Das Relay versendet mit einem ausdrücklichen Event-Namen:

php
$this->eventDispatcher->dispatch($event, TranslationJobFinishedEvent::EVENT_NAME);

Registrieren Sie Ihren Subscriber auf diesen Namen. Ein Subscriber, den Sie auf die Klasse registrieren, erhält den Versand nicht:

php
<?php declare(strict_types=1);

namespace Acme\Translate\Subscriber;

use Nice\Translate\Job\Event\TranslationJobFinishedEvent;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

final class TranslationJobSubscriber implements EventSubscriberInterface
{
    public function __construct(
        private readonly LoggerInterface $logger,
    ) {
    }

    public static function getSubscribedEvents(): array
    {
        return [
            TranslationJobFinishedEvent::EVENT_NAME => 'onJobFinished',
        ];
    }

    public function onJobFinished(TranslationJobFinishedEvent $event): void
    {
        if ($event->getFailedItems() === 0) {
            return;
        }

        $this->logger->warning('Translation job finished with errors.', [
            'jobId' => $event->getJobId(),
            'title' => $event->getTitle(),
            'status' => $event->getStatus(),
            'processed' => $event->getProcessedItems(),
            'failed' => $event->getFailedItems(),
            'skipped' => $event->getSkippedItems(),
        ]);
    }
}
xml
<service id="Acme\Translate\Subscriber\TranslationJobSubscriber">
    <argument type="service" id="logger"/>
    <tag name="kernel.event_subscriber"/>
</service>

Zwei Eigenschaften des Versands sind für jeden Listener wichtig, den Sie schreiben:

  • Er läuft in einem Queue-Worker. Das Relay versendet aus dem Prozess heraus, der den Auftrag abgeschlossen hat: aus einem Queue-Worker oder aus der geplanten Wiederherstellungsaufgabe. Ein Web-Request ist daran nicht beteiligt, und getContext() liefert einen Kontext aus einer System-Source. Darin gibt es weder einen Administrationsbenutzer noch einen Verkaufskanal zu lesen.
  • Er kann für denselben Auftrag mehr als einmal laufen. Die Zustellung erfolgt mindestens einmal. Halten Sie den Listener idempotent, oder deduplizieren Sie über jobId.

Wenn Sie mehr als die sechs Skalare brauchen, laden Sie die Entität nice_translate_job über ihr Repository anhand von jobId. Die Admin-API nennt die Liste der Entitäten und die nötigen Berechtigungen.

modernice Extensions für Shopware 6. Shopware ist eine Marke der shopware AG — diese Dokumentation steht in keiner Verbindung zur shopware AG.