
# Групування пакетами

Пакети дають змогу запускати групу завдань і відстежувати їх як єдине ціле. Можна зареєструвати зворотні виклики, які спрацьовують після успішного завершення всіх завдань, помилки будь-якого завдання або завершення пакета незалежно від результату. Queuety автоматично відстежує поступ, помилки та стан скасування.

## Створення пакета

Використовуйте `Queuety::create_batch()`, щоб запустити `BatchBuilder`. Передайте масив екземплярів `Contracts\Job`, а потім викличте `dispatch()`, щоб створити запис пакета й усі завдання.

```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()` повертає об’єкт-значення `Batch` зі станом пакета.

Замість екземплярів завдань можна також передати масиви обробник+дані:

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

## Зворотні виклики

Зареєструйте класи обробників, які викликатимуться на різних етапах життєвого циклу пакета. Кожен клас обробника повинен мати метод `handle( Batch $batch )`.

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

| Метод | Коли спрацьовує |
|---|---|
| `then( $handler_class )` | Усі завдання успішно завершено (або задано `allow_failures()`) |
| `catch( $handler_class )` | Перше завдання в пакеті завершується з помилкою |
| `finally( $handler_class )` | Пакет завершується незалежно від успіху чи помилки |

### Написання обробника зворотного виклику

```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.
    }
}
```

## Дозвіл на помилки

За замовчуванням зворотний виклик `then` спрацьовує, лише якщо всі завдання в пакеті завершилися успішно. Викличте `allow_failures()`, щоб зворотний виклик `then` спрацьовував, навіть якщо деякі завдання завершаться з помилкою.

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

## Відстеження поступу

Об’єкт-значення `Batch` містить властивості й методи для відстеження поступу.

```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
```

| Метод/властивість | Тип | Опис |
|---|---|---|
| `progress()` | `int` | Відсоток завершення від 0 до 100 |
| `finished()` | `bool` | Чи завершився пакет (усі завдання виконано або завершилося з помилкою) |
| `has_failures()` | `bool` | Чи завершилося будь-яке завдання в пакеті з помилкою |
| `cancelled()` | `bool` | Чи скасовано пакет |
| `$total_jobs` | `int` | Загальна кількість завдань у пакеті |
| `$pending_jobs` | `int` | Кількість завдань, що очікують виконання |
| `$failed_jobs` | `int` | Кількість завдань із помилками |
| `$failed_job_ids` | `array` | ID завдань, що завершилися з помилкою |
| `$name` | `string\|null` | Необов’язкова назва пакета |
| `$created_at` | `DateTimeImmutable` | Час створення пакета |
| `$finished_at` | `DateTimeImmutable\|null` | Час завершення пакета |
| `$cancelled_at` | `DateTimeImmutable\|null` | Час скасування пакета |

## Скасування

Скасуйте пакет через `BatchManager`. Скасування пакета задає значення `cancelled_at` і `finished_at` та запускає зворотний виклик `finally`.

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

Скасування позначає пакет як скасований і завершений та запускає його зворотний виклик `finally`. Завдання не змінюються: завдання, що очікують виконання в скасованому пакеті, все одно запускаються, але їхні результати більше не оновлюють лічильники пакета й не запускають `then`, `catch` або `finally`. Якщо завдання не повинні виконуватися, видаліть їх самостійно.

## Пошук пакетів

Знайдіть пакет за його ID:

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

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

## Видалення старих пакетів

Видаліть завершені пакети, старші за вказану кількість днів:

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

Видаляються лише пакети з непорожньою часовою позначкою `finished_at`. Активні пакети не видаляються.
