
# Dinamiska apstrāde katram elementam

Izmantojiet `->for_each()`, ja darbplūsmā būvēšanas laikā nav zināms zaru skaits. Queuety nolasa masīvu no darbplūsmas stāvokļa, nosūta vienu zara uzdevumu katram elementam un turpinās tikai tad, kad ir izpildīts konfigurētais pabeigšanas nosacījums.

## Metode `->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 ] );
```

Plānotāja solis ieraksta `pages` darbplūsmas stāvoklī. Apstrādes katram elementam solis nolasa šo masīvu un nosūta vienu zara uzdevumu katram elementam.

## Zaru apstrādātāji

Apstrādātāji katra elementa zaram īsteno `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',
        ];
    }
}
```

Sadaļā `config()` `backoff` norāda stratēģijas nosaukumu: `exponential`, `linear` vai `fixed`. Atkārtojumu aizkavju saraksts tur pašlaik tiek atmests, un tā vietā notiek tūlītēji atkārtojumi ([#16](https://github.com/inline0/queuety/issues/16)); atkārtojumu aizkaves norādiet soļa definīcijas `backoff`.

`$state` satur pilnu darbplūsmas stāvokli, `$item` ir atsevišķais apstrādes katram elementam elements, bet `$index` ir tā pozīcija, skaitot no nulles.

## Pabeigšanas režīmi

`ForEachMode::All`

Solis tiek pabeigts tikai tad, kad visi zari ir veiksmīgi izpildīti. Jebkura neatgriezeniska zara kļūme izraisa apstrādes katram elementam soļa kļūmi.

`ForEachMode::FirstSuccess`

Solis tiek pabeigts, tiklīdz veiksmīgi izpildās pirmais zars. Pārējie aktīvie zari tiek atzīmēti kā aizstāti.

`ForEachMode::Quorum`

Solis tiek pabeigts, kad veiksmīgi izpildās vismaz `quorum` zari. Ja pārējie zari vairs nespēj sasniegt kvorumu, darbplūsma cietīs neveiksmi.

## Apkopotāji

Pēc noklusējuma Queuety saglabā strukturētu apkopojumu ar atslēgu `result_key`:

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

Ja vēlaties pielāgotu kopsavilkumu, norādiet reducer klasi, kas īsteno `Contracts\ForEachReducer`. Apkopotie dati joprojām tiek glabāti zem `result_key`, un reducer atgrieztās atslēgas tiek sapludinātas darbplūsmas stāvoklī blakus tam:

```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 izvade tiek sapludināta darbplūsmas stāvoklī tāpat kā jebkuras citas darbības izvade. Tā var arī atgriezt `_next_step`, lai pēc for-each izpildes noteiktu atzaru.

## Atkārtotas mēģināšanas darbība

For-each atzari atkārtoti mēģina izpildi neatkarīgi, ievērojot atzara apstrādātāja `config()`. Ja for-each darbība neizdodas un izsaucat `retry_workflow()`, Queuety no jauna izvērš for-each darbību un atkārtoti ievieto rindā tikai tos atzarus, kuru vēl trūkst vai kuri ir neizdevušies. Veiksmīgo atzaru rezultāti paliek darbplūsmas stāvoklī.

## Izvēle starp `parallel()` un `for_each()`

Izmantojiet `parallel()`, ja atzaru saraksts kodā ir fiksēts.

Izmantojiet `for_each()`, ja atzaru saraksts tiek noteikts izpildlaikā no darbplūsmas stāvokļa un nepieciešami tādi pabeigšanas režīmi kā first-success vai kvorums.
