
# Bedingte Verzweigung

Workflows können anhand der Ausgabe eines vorherigen Schritts zu einem benannten Schritt springen. Dazu wird ein spezieller Schlüssel `_next_step` im Rückgabewert des Schritts zurückgegeben.

Wenn du anhand des Workflow-Zustands gezielt zu einem früheren Schritt zurückspringen möchtest, findest du weitere Informationen unter [Wiederholungen](https://queuety.inline0.dev/de/docs/workflows/repeats). So bleibt der Rücksprung in der Workflow-Definition explizit, statt in einer Schrittklasse verborgen zu sein.

## Benannte Schritte

Gib Schritten mit dem zweiten Argument von `->then()` einen Namen:

```php
use Queuety\Queuety;

Queuety::workflow( 'process_order' )
    ->then( ValidateOrderHandler::class, 'validate' )
    ->then( ChargePaymentHandler::class, 'charge' )
    ->then( FulfillOrderHandler::class, 'fulfill' )
    ->then( SendConfirmationHandler::class, 'confirm' )
    ->then( HandleFailureHandler::class, 'failure' )
    ->dispatch( [ 'order_id' => 123 ] );
```

Ohne expliziten Namen werden Schritte anhand ihres Index benannt (`'0'`, `'1'`, `'2'` usw.).

## Der Schlüssel `_next_step`

Ein Schritt kann `_next_step` zurückgeben, um zu einem benannten Schritt zu springen und alle Schritte dazwischen zu überspringen. Schritte nach dem Sprungziel werden weiterhin der Reihe nach ausgeführt: Ohne Sprung fährt der Workflow einfach mit dem nächsten Schritt in der Liste fort. Im obigen Beispiel wird ein erfolgreicher Schritt `confirm` also mit `failure` fortfahren, sofern er nicht `_next_step` zurückgibt, um diesen Schritt zu überspringen, oder `HandleFailureHandler` zuerst auf einen Schlüssel `error` prüft:

```php
use Queuety\Step;

class ValidateOrderHandler implements Step {
    public function handle( array $state ): array {
        $order = wc_get_order( $state['order_id'] );

        if ( ! $order || $order->get_status() === 'cancelled' ) {
            return [
                'error'  => 'Order is invalid or cancelled',
                '_next_step'  => 'failure',
            ];
        }

        return [
            'order_total' => $order->get_total(),
            'customer_id' => $order->get_customer_id(),
        ];
    }

    public function config(): array {
        return [];
    }
}
```

Wenn `ValidateOrderHandler` `_next_step => 'failure'` zurückgibt, überspringt der Workflow `charge`, `fulfill` und `confirm` und springt direkt zum Schritt `failure`. Der Schlüssel `_next_step` wird von der Workflow-Engine verarbeitet und nicht im Zustand gespeichert.

## Verzweigungsmuster

### Bei Bedingung überspringen

```php
class CheckInventoryHandler implements Step {
    public function handle( array $state ): array {
        $in_stock = check_inventory( $state['sku'] );

        if ( ! $in_stock ) {
            return [
                'reason' => 'Out of stock',
                '_next_step'  => 'notify_backorder',
            ];
        }

        return [ 'inventory_reserved' => true ];
    }

    public function config(): array {
        return [];
    }
}
```

### Vorzeitig abschließen

Springe zum letzten Schritt, um unnötige Verarbeitung zu überspringen:

```php
class CheckCacheHandler implements Step {
    public function handle( array $state ): array {
        $cached = get_transient( "report_{$state['user_id']}" );

        if ( $cached ) {
            return [
                'report_url' => $cached,
                '_next_step'      => 'done',
            ];
        }

        return [ 'cache_miss' => true ];
    }

    public function config(): array {
        return [];
    }
}
```

## Vollständiges Beispiel

```php
Queuety::workflow( 'content_pipeline' )
    ->then( CheckCacheHandler::class, 'check_cache' )
    ->then( FetchDataHandler::class, 'fetch' )
    ->then( CallLLMHandler::class, 'generate' )
    ->then( SaveResultHandler::class, 'save' )
    ->then( ReturnCachedHandler::class, 'done' )
    ->dispatch( [ 'user_id' => 42 ] );
```

Wenn die Cache-Prüfung einen Treffer findet, springt der Workflow direkt von `check_cache` zu `done` und überspringt `fetch`, `generate` und `save`. Bei einem Fehlschlag werden die Schritte der Reihe nach ausgeführt, und `save` fährt auch mit `done` fort. Daher muss `ReturnCachedHandler` beide Abläufe unterstützen.
