Vai al contenuto

Ripetizioni

I flussi di lavoro Queuety sono catene ordinate di passaggi. Usate un passaggio di controllo della ripetizione per tornare a un passaggio precedente con nome, invece di scrivere manualmente la logica _next_step.

Scegliete la forma più adatta al flusso di lavoro:

  • un state_key pubblico quando la condizione è semplice
  • un condition_class quando la ripetizione richiede una logica più articolata
  • max_iterations quando la ripetizione deve avere un limite massimo

Entrambi i metodi puntano a un passaggio precedente con nome, così la ripetizione resta serializzabile, riproducibile e protetta dallo stesso runtime del flusso di lavoro di ogni altro passaggio.

La condizione di ripetizione viene valutata sullo stato pubblico persistente del flusso di lavoro dopo il completamento del passaggio precedente, così ogni iterazione resta durevole anche dopo il riavvio dei worker.

repeat_until() con una chiave di stato

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 può appiattire i dati della decisione in una semplice chiave pubblica:

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 [];
    }
}

Se review_approved non è ancora true, il passaggio di ripetizione torna a draft. Quando diventa true, il flusso di lavoro prosegue con PublishBriefStep.

I segnali vengono memorizzati, non consumati. Al secondo passaggio per wait_for_decision(), il segnale rejected precedente corrisponde ancora e l'attesa termina subito, quindi il ciclo continuerebbe all'infinito finché max_transitions() non lo interrompe. Assegnate a ogni iterazione una chiave distinta: fate in modo che DraftBriefStep restituisca un review_round incrementale, passate correlation_key: 'review_round' a wait_for_decision() e chiedete al revisore di inviare lo stesso review_round nel payload del segnale. L'attesa termina quindi solo per un segnale il cui review_round corrisponde al valore attuale dello stato.

repeat_until() con una classe di condizione

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 );
    }
}

Le classi di condizione ricevono lo stato pubblico del flusso di lavoro, così il predicato resta serializzabile e ispezionabile senza dipendere da closure.

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 ] );

Questo schema è utile quando un passaggio scrive un semplice valore booleano, ad esempio should_poll_again, has_more_pages o needs_revision.

Perché le ripetizioni sono separate da _next_step

Potete comunque gestire le diramazioni manualmente restituendo _next_step da un passaggio normale. Le funzioni di supporto per la ripetizione servono per il caso più specifico in cui:

  • la destinazione è un passaggio precedente
  • la condizione deriva dallo stato pubblico del flusso di lavoro
  • volete che la logica della ripetizione resti visibile nella definizione stessa del flusso di lavoro

In questo modo è più facile esaminare le ripetizioni nelle esportazioni, nei registri degli eventi e nella documentazione, rispetto a nascondere i collegamenti all'indietro in codice arbitrario dei passaggi.

Proteggere le ripetizioni

Usate max_iterations quando una specifica ripetizione deve avere un limite massimo locale:

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

Usate max_transitions() quando l'intero flusso di lavoro richiede un interruttore di emergenza più ampio:

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();

Se una ripetizione supera max_iterations o il flusso di lavoro supera max_transitions(), Queuety interrompe l'esecuzione invece di lasciare che il ciclo continui all'infinito.