
# Toistot

Queuety-työnkulut ovat järjestettyjä vaiheketjuja. Käytä toiston ohjausvaihetta siirtyäksesi aiempaan nimettyyn vaiheeseen sen sijaan, että kirjoittaisit `_next_step`-logiikan käsin.

Valitse työnkulkuun sopiva muoto:

- julkinen `state_key`, kun ehto on yksinkertainen
- `condition_class`, kun toisto vaatii monipuolisempaa logiikkaa
- `max_iterations`, kun toistolle tarvitaan ehdoton pysäytysraja

Molemmat menetelmät kohdistuvat aiempaan nimettyyn vaiheeseen, joten toisto pysyy sarjallistettavana, toistettavana ja saman ajonaikaisen työnkulun hallinnan alaisena kuin kaikki muutkin vaiheet.

Toistoehto arvioidaan tallennetusta julkisesta työnkulun tilasta edellisen vaiheen valmistuttua, joten jokainen kierros säilyy myös työntekijöiden uudelleenkäynnistyessä.

## `repeat_until()` tilan avaimella

```php
Queuety::workflow( 'approval_revision_loop' )
    ->max_transitions( 12 )
    ->then( DraftBriefStep::class, 'draft' )
    ->wait_for_decision( result_key: 'review' )
    ->then( NormalizeReviewOutcomeStep::class, 'review_outcome' )
    ->repeat_until( 'draft', 'review_approved', true, 'repeat_draft_until_approved' )
    ->then( PublishBriefStep::class )
    ->dispatch( [ 'brief_id' => 42 ] );
```

`NormalizeReviewOutcomeStep` voi litistää päätöksen hyötykuorman yksinkertaiseksi julkiseksi avaimeksi:

```php
class NormalizeReviewOutcomeStep implements \Queuety\Step {
    public function handle( array $state ): array {
        return [
            'review_approved' => 'approved' === ( $state['review']['outcome'] ?? null ),
        ];
    }

    public function config(): array {
        return [];
    }
}
```

Jos `review_approved` ei ole vielä `true`, toistovaihe palaa vaiheeseen `draft`. Kun siitä tulee `true`, työnkulku jatkuu vaiheeseen `PublishBriefStep`.

Signaalit tallennetaan, eikä niitä kuluteta. Kun `wait_for_decision()` suoritetaan toisen kerran, aiempi signaali `rejected` vastaa yhä ehtoa ja odotus päättyy heti, jolloin silmukka jatkaisi toistoaan, kunnes `max_transitions()` pysäyttää sen. Anna jokaiselle kierrokselle oma avain: pyydä toimintoa `DraftBriefStep` palauttamaan kasvava `review_round`, välitä `correlation_key: 'review_round'` toiminnolle `wait_for_decision()` ja pyydä tarkastajaa lähettämään sama `review_round` signaalin hyötykuormassa. Odotus päättyy tällöin vain, jos signaalin `review_round` vastaa nykyistä tila-arvoa.

## `repeat_until()` ehtoluokan kanssa

```php
use App\Workflows\ReviewApprovedCondition;

Queuety::workflow( 'approval_revision_loop' )
    ->then( DraftBriefStep::class, 'draft' )
    ->wait_for_decision( result_key: 'review' )
    ->repeat_until(
        target_step: 'draft',
        condition_class: ReviewApprovedCondition::class,
        max_iterations: 5,
        name: 'repeat_draft_until_approved',
    )
    ->then( PublishBriefStep::class )
    ->dispatch( [ 'brief_id' => 42 ] );
```

```php
namespace App\Workflows;

use Queuety\Contracts\RepeatCondition;

final class ReviewApprovedCondition implements RepeatCondition {
    public function matches( array $state ): bool {
        return 'approved' === ( $state['review']['outcome'] ?? null );
    }
}
```

Ehtoluokat saavat julkisen työnkulun tilan, joten predikaatti pysyy sarjallistettavana ja tarkasteltavana ilman sulkeumia.

## `repeat_while()`

```php
Queuety::workflow( 'poll_remote_status' )
    ->max_transitions( 20 )
    ->then( PollRemoteStatusStep::class, 'poll' )
    ->delay( seconds: 30 )
    ->repeat_while( 'poll', 'should_poll_again', true, 'repeat_poll' )
    ->then( FinalizeImportStep::class )
    ->dispatch( [ 'import_id' => 99 ] );
```

Tämä malli on hyödyllinen, kun vaihe kirjoittaa yksinkertaisen totuusarvon, kuten `should_poll_again`, `has_more_pages` tai `needs_revision`.

## Miksi toistot ovat erillään kohteesta `_next_step`

Voit silti haarauttaa työnkulun itse palauttamalla arvon `_next_step` tavallisesta vaiheesta. Toistoapuista on hyötyä suppeammassa tilanteessa, jossa:

- kohteena on aiempi vaihe
- ehto tulee työnkulun julkisesta tilasta
- haluat pitää toistologiikan näkyvissä työnkulun määritelmässä

Näin toistot on helpompi tarkastaa vienneistä, tapahtumalokeista ja dokumentaatiosta kuin jos paluureitti olisi piilotettu mielivaltaiseen vaihekoodiin.

## Toistojen rajoittaminen

Käytä arvoa `max_iterations`, kun haluat asettaa yksittäiselle toistolle paikallisen ehdottoman rajan:

```php
Queuety::workflow( 'poll_remote_status' )
    ->then( PollRemoteStatusStep::class, 'poll' )
    ->repeat_while( 'poll', 'should_poll_again', true, max_iterations: 8 )
    ->dispatch();
```

Käytä arvoa `max_transitions()`, kun haluat asettaa koko työnkululle laajemman hätäpysäytyksen:

```php
Queuety::workflow( 'safe_revision_loop' )
    ->max_transitions( 10 )
    ->then( DraftBriefStep::class, 'draft' )
    ->wait_for_decision( result_key: 'review' )
    ->then( NormalizeReviewOutcomeStep::class )
    ->repeat_until( 'draft', 'review_approved', true )
    ->dispatch();
```

Jos toisto ylittää arvon `max_iterations` tai työnkulku ylittää arvon `max_transitions()`, Queuety epäonnistuu sen sijaan, että antaisi suorituksen jatkua loputtomiin.

## Aiheeseen liittyvät dokumentit

- [Ehdollinen haarautuminen](https://queuety.inline0.dev/fi/docs/workflows/conditional)
- [Signaalit](https://queuety.inline0.dev/fi/docs/workflows/signals)
- [Suojaukset](https://queuety.inline0.dev/fi/docs/workflows/guardrails)
- [PHP API](https://queuety.inline0.dev/fi/docs/api)
