
# Batchverarbeitung

Mit Batches kannst du eine Gruppe von Jobs auslösen und als Einheit verfolgen. Du kannst Rückrufe registrieren, die beim Abschluss des Batches, beim Fehlschlagen eines beliebigen Jobs oder beim Ende des Batches unabhängig vom Ergebnis ausgeführt werden. Queuety erfasst Fortschritt, Fehler und den Abbruchstatus automatisch.

## Batch erstellen

Verwende `Queuety::create_batch()`, um einen `BatchBuilder` zu starten. Übergib ein Array mit Instanzen von `Contracts\Job` und rufe dann `dispatch()` auf, um den Batch-Eintrag und alle Jobs zu erstellen.

```php
use Queuety\Queuety;

$batch = Queuety::create_batch( [
    new ImportUsersJob( $csv_chunk_1 ),
    new ImportUsersJob( $csv_chunk_2 ),
    new ImportUsersJob( $csv_chunk_3 ),
] )
    ->name( 'Import users' )
    ->on_queue( 'imports' )
    ->dispatch();

echo $batch->id; // The batch ID
```

`dispatch()` gibt ein `Batch`-Wertobjekt mit dem Batch-Status zurück.

Du kannst statt Job-Instanzen auch Arrays mit Handler und Nutzdaten übergeben:

```php
$batch = Queuety::create_batch( [
    [ 'handler' => 'import_users', 'payload' => [ 'chunk' => 1 ] ],
    [ 'handler' => 'import_users', 'payload' => [ 'chunk' => 2 ] ],
] )->dispatch();
```

## Rückrufe

Registriere Handler-Klassen, die an verschiedenen Punkten im Lebenszyklus des Batches aufgerufen werden. Jede Handler-Klasse muss über eine Methode `handle( Batch $batch )` verfügen.

```php
$batch = Queuety::create_batch( $jobs )
    ->name( 'Import users' )
    ->then( ImportCompleteHandler::class )
    ->catch( ImportFailedHandler::class )
    ->finally( ImportCleanupHandler::class )
    ->dispatch();
```

| Methode | Zeitpunkt der Ausführung |
|---|---|
| `then( $handler_class )` | Alle Jobs wurden erfolgreich abgeschlossen (oder `allow_failures()` ist gesetzt) |
| `catch( $handler_class )` | Der erste Job im Batch schlägt fehl |
| `finally( $handler_class )` | Der Batch endet, unabhängig davon, ob er erfolgreich war oder fehlschlug |

### Einen Rückruf-Handler schreiben

```php
use Queuety\Batch;

class ImportCompleteHandler {
    public function handle( Batch $batch ): void {
        // All users imported. Send a notification.
        error_log( "Batch {$batch->id} completed: {$batch->total_jobs} jobs processed." );
    }
}

class ImportFailedHandler {
    public function handle( Batch $batch ): void {
        error_log( "Batch {$batch->id} had a failure. Failed jobs: {$batch->failed_jobs}" );
    }
}

class ImportCleanupHandler {
    public function handle( Batch $batch ): void {
        // Runs regardless of outcome. Clean up temp files, etc.
    }
}
```

## Fehler zulassen

Standardmäßig wird der Rückruf `then` nur ausgeführt, wenn alle Jobs im Batch erfolgreich sind. Rufe `allow_failures()` auf, damit der Rückruf `then` auch bei fehlgeschlagenen Jobs ausgeführt wird.

```php
$batch = Queuety::create_batch( $jobs )
    ->then( ReportHandler::class )
    ->allow_failures()
    ->dispatch();
```

## Fortschritt verfolgen

Das Wertobjekt `Batch` stellt Eigenschaften und Methoden zur Fortschrittsverfolgung bereit.

```php
$batch = Queuety::find_batch( $batch_id );

$batch->progress();      // 0-100 percentage
$batch->finished();      // true if all jobs are done
$batch->has_failures();  // true if any job failed
$batch->cancelled();     // true if the batch was cancelled

$batch->total_jobs;      // Total number of jobs
$batch->pending_jobs;    // Jobs still pending
$batch->failed_jobs;     // Number of failed jobs
$batch->failed_job_ids;  // Array of failed job IDs
```

| Methode/Eigenschaft | Typ | Beschreibung |
|---|---|---|
| `progress()` | `int` | Fortschritt in Prozent von 0 bis 100 |
| `finished()` | `bool` | Gibt an, ob der Batch beendet ist (alle Jobs abgeschlossen oder fehlgeschlagen) |
| `has_failures()` | `bool` | Gibt an, ob Jobs im Batch fehlgeschlagen sind |
| `cancelled()` | `bool` | Gibt an, ob der Batch abgebrochen wurde |
| `$total_jobs` | `int` | Gesamtzahl der Jobs im Batch |
| `$pending_jobs` | `int` | Anzahl der noch ausstehenden Jobs |
| `$failed_jobs` | `int` | Anzahl der fehlgeschlagenen Jobs |
| `$failed_job_ids` | `array` | IDs der fehlgeschlagenen Jobs |
| `$name` | `string\|null` | Optionaler Batchname |
| `$created_at` | `DateTimeImmutable` | Zeitpunkt der Batcherstellung |
| `$finished_at` | `DateTimeImmutable\|null` | Zeitpunkt des Batchabschlusses |
| `$cancelled_at` | `DateTimeImmutable\|null` | Zeitpunkt des Batchabbruchs |

## Abbrechen

Brich einen Batch über `BatchManager` ab. Dadurch werden `cancelled_at` und `finished_at` gesetzt und der Rückruf `finally` ausgeführt.

```php
Queuety::batch_manager()->cancel( $batch_id );
```

Beim Abbrechen wird der Batch als abgebrochen und beendet markiert und sein Rückruf `finally` ausgeführt. Die Jobs bleiben unberührt: Ausstehende Jobs eines abgebrochenen Batches werden weiterhin ausgeführt, ihre Ergebnisse aktualisieren jedoch nicht mehr die Batch-Zähler und lösen `then`, `catch` oder `finally` nicht aus. Lösche die Jobs selbst, wenn sie nicht ausgeführt werden sollen.

## Batches finden

Einen Batch anhand seiner ID suchen:

```php
$batch = Queuety::find_batch( $batch_id );

if ( null === $batch ) {
    // Batch not found.
}
```

## Alte Batches bereinigen

Beendete Batches löschen, die älter als eine bestimmte Anzahl von Tagen sind:

```php
$deleted = Queuety::batch_manager()->prune( 30 ); // Delete batches finished > 30 days ago
```

Gelöscht werden nur Batches mit einem Zeitstempel für `finished_at`, der nicht null ist. Aktive Batches werden nie bereinigt.
