
# Dünaamiline igaühe jaoks

Kasutage `->for_each()`, kui töövoog ei tea harusid koostamise ajal loendada. Queuety loeb töövoo olekust massiivi, käivitab iga üksuse jaoks eraldi harutöö ning jätkab alles siis, kui seadistatud lõpetamistingimus on täidetud.

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

Planeerimissamm kirjutab `pages` töövoo olekusse. Igaühe jaoks samm loeb selle massiivi ja käivitab iga üksuse jaoks eraldi harutöö.

## Harukäitlejad

Igaühe jaoks harukäitlejad rakendavad `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()`-s võtab `backoff` strateegianime: `exponential`, `linear` või `fixed`. Seal määratud katsetevaheliste viivituste loend jäetakse praegu kõrvale ja korduskatsed toimuvad kohe ([#16](https://github.com/inline0/queuety/issues/16)); määrake katsetevahelised viivitused sammu definitsioonis oleva `backoff` abil.

`$state` sisaldab kogu töövoo olekut, `$item` on üksik igaühe jaoks üksus ning `$index` selle nullist algav järjekorranumber.

## Lõpetamisrežiimid

`ForEachMode::All`

Samm lõpetatakse alles siis, kui kõik harud õnnestuvad. Mis tahes püsiv harutõrge põhjustab igaühe jaoks sammu nurjumise.

`ForEachMode::FirstSuccess`

Samm lõpetatakse kohe, kui esimene haru õnnestub. Ülejäänud aktiivsed harud maetakse aegununa.

`ForEachMode::Quorum`

Samm lõpetatakse, kui õnnestub vähemalt `quorum` haru. Kui ülejäänud harud ei suuda enam kvoorumit täita, siis töövoog nurjub.

## Reduktorid

Vaikimisi salvestab Queuety struktureeritud koondtulemuse võtme `result_key` alla:

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

Kohandatud kokkuvõtte saamiseks andke edasi reduktori klass, mis rakendab `Contracts\ForEachReducer`. Koondtulemus salvestatakse endiselt võtme `result_key` alla ning reduktori tagastatud võtmed liidetakse selle kõrvale töövoo olekusse:

```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,
        ];
    }
}
```

Reduktori väljund liidetakse töövoo olekusse nagu mis tahes muu sammu väljund. See võib tagastada ka `_next_step`, et pärast igaühe jaoks sammu lõpetamist hargneda.

## Korduskatsete käitumine

Igaühe jaoks harud teevad korduskatseid üksteisest sõltumatult, vastavalt harukäitleja `config()`-le. Kui igaühe jaoks samm nurjub ja kutsute välja `retry_workflow()`, laiendab Queuety igaühe jaoks sammu uuesti ning paneb uuesti järjekorda ainult puuduvad või nurjunud harud. Õnnestunud harutulemused jäävad töövoo olekusse.

## Millal valida `parallel()` või `for_each()`

Kasutage `parallel()`, kui harude loend on koodis fikseeritud.

Kasutage `for_each()`, kui harude loend tuvastatakse käitusajal töövoo oleku põhjal ning vajate lõpetamisrežiime, nagu esimese õnnestumise või kvoorumi alusel lõpetamine.
