
# Δυναμικό For-Each

Χρησιμοποιήστε το `->for_each()` όταν ο αριθμός των διακλαδώσεων της ροής εργασίας δεν είναι γνωστός κατά τη δημιουργία της. Το Queuety διαβάζει έναν πίνακα από την κατάσταση της ροής εργασίας, αποστέλλει μία εργασία διακλάδωσης ανά στοιχείο και προχωρά μόνο όταν ικανοποιηθεί η διαμορφωμένη συνθήκη ολοκλήρωσης.

## Η μέθοδος `->for_each()`

```php
use Queuety\Contracts\ForEachHandler;
use Queuety\Contracts\ForEachReducer;
use Queuety\Enums\ForEachMode;
use Queuety\Queuety;

Queuety::workflow( 'index_site' )
    ->then( PlanPagesStep::class )
    ->for_each(
        items_key: 'pages',
        handler_class: IndexPageStep::class,
        result_key: 'page_results',
        mode: ForEachMode::Quorum,
        quorum: 2,
        reducer_class: IndexSummaryReducer::class,
        name: 'index_pages',
    )
    ->then( PublishIndexStep::class )
    ->dispatch( [ 'site_id' => 42 ] );
```

Το βήμα σχεδιασμού γράφει το `pages` στην κατάσταση της ροής εργασίας. Το βήμα for-each διαβάζει αυτόν τον πίνακα και αποστέλλει μία εργασία διακλάδωσης ανά στοιχείο.

## Χειριστές διακλάδωσης

Οι χειριστές διακλάδωσης for-each υλοποιούν το `Contracts\ForEachHandler`:

```php
use Queuety\Contracts\ForEachHandler;

final class IndexPageStep implements ForEachHandler {
    public function handle_item( array $state, mixed $item, int $index ): array {
        $page = is_array( $item ) ? $item : [ 'url' => (string) $item ];

        return [
            'url' => $page['url'],
            'title' => fetch_title( $page['url'] ),
            'index' => $index,
        ];
    }

    public function config(): array {
        return [
            'max_attempts' => 3,
            'backoff' => 'exponential',
        ];
    }
}
```

Στο `config()`, το `backoff` δέχεται ένα όνομα στρατηγικής: `exponential`, `linear` ή `fixed`. Μια λίστα καθυστερήσεων ανά προσπάθεια απορρίπτεται αυτήν τη στιγμή και μετατρέπεται σε άμεσες επαναλήψεις ([#16](https://github.com/inline0/queuety/issues/16))· ορίστε τις καθυστερήσεις ανά προσπάθεια στο `backoff` του ορισμού του βήματος.

Το `$state` περιέχει ολόκληρη την κατάσταση της ροής εργασίας, το `$item` είναι το μεμονωμένο στοιχείο for-each και το `$index` είναι η θέση του, με αρίθμηση από το μηδέν.

## Λειτουργίες ολοκλήρωσης

`ForEachMode::All`

Το βήμα ολοκληρώνεται μόνο αφού επιτύχουν όλες οι διακλαδώσεις. Οποιαδήποτε οριστική αποτυχία διακλάδωσης προκαλεί αποτυχία του βήματος for-each.

`ForEachMode::FirstSuccess`

Το βήμα ολοκληρώνεται μόλις επιτύχει η πρώτη διακλάδωση. Οι υπόλοιπες ενεργές διακλαδώσεις θάβονται ως αντικατασταθείσες.

`ForEachMode::Quorum`

Το βήμα ολοκληρώνεται όταν επιτύχουν τουλάχιστον `quorum` διακλαδώσεις. Αν οι υπόλοιπες διακλαδώσεις δεν μπορούν πλέον να ικανοποιήσουν την απαρτία, η ροή εργασίας αποτυγχάνει.

## Συναρτήσεις μείωσης

Από προεπιλογή, το Queuety αποθηκεύει μια δομημένη συνάθροιση στο `result_key`:

- `mode`
- `quorum`
- `total`
- `succeeded`
- `failed`
- `settled`
- `winner`
- `results`
- `failures`

Αν θέλετε μια προσαρμοσμένη σύνοψη, περάστε μια κλάση reducer που υλοποιεί το `Contracts\ForEachReducer`. Η συνάθροιση εξακολουθεί να αποθηκεύεται στο `result_key` και τα κλειδιά που επιστρέφει ο reducer συγχωνεύονται στην κατάσταση της ροής εργασίας δίπλα της:

```php
use Queuety\Contracts\ForEachReducer;

final class IndexSummaryReducer implements ForEachReducer {
    public function reduce( array $state, array $for_each ): array {
        return [
            'indexed_count' => count( $for_each['results'] ),
            'winning_url' => $for_each['winner']['output']['url'] ?? null,
        ];
    }
}
```

Η έξοδος του reducer συγχωνεύεται στην κατάσταση της ροής εργασίας όπως κάθε άλλη έξοδος βήματος. Μπορεί επίσης να επιστρέψει το `_next_step` για διακλάδωση μετά την ολοκλήρωση του for-each.

## Συμπεριφορά επανάληψης

Οι διακλαδώσεις for-each επαναλαμβάνονται ανεξάρτητα, σύμφωνα με το `config()` του χειριστή διακλάδωσης. Αν αποτύχει το βήμα for-each και καλέσετε το `retry_workflow()`, το Queuety επεκτείνει ξανά το βήμα for-each και επαναθέτει στην ουρά μόνο τις διακλαδώσεις που εξακολουθούν να λείπουν ή να έχουν αποτύχει. Τα αποτελέσματα των επιτυχημένων διακλαδώσεων παραμένουν στην κατάσταση της ροής εργασίας.

## Επιλογή μεταξύ `parallel()` και `for_each()`

Χρησιμοποιήστε το `parallel()` όταν η λίστα διακλαδώσεων είναι καθορισμένη στον κώδικα.

Χρησιμοποιήστε το `for_each()` όταν η λίστα διακλαδώσεων εντοπίζεται κατά την εκτέλεση από την κατάσταση της ροής εργασίας και χρειάζεστε λειτουργίες ολοκλήρωσης, όπως η πρώτη επιτυχία ή η απαρτία.
