Repeticiones
Queuety workflows son cadenas ordenadas de pasos. Usa un paso de control de repetición para volver a un paso anterior con nombre, en lugar de escribir a mano la lógica de _next_step.
Elige la forma que mejor se adapte al flujo de trabajo:
- un
state_keypúblico cuando la condición es sencilla - un
condition_classcuando la repetición necesita una lógica más elaborada max_iterationscuando la repetición necesita un límite estricto
Ambos métodos apuntan a un paso anterior con nombre, por lo que la repetición sigue siendo serializable y reproducible, y está protegida por el mismo entorno de ejecución del flujo de trabajo que los demás pasos.
La condición de repetición se evalúa a partir del estado público persistido del flujo de trabajo una vez completado el paso anterior, por lo que cada iteración se conserva aunque se reinicien los workers.
repeat_until() con una clave de estado
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 puede convertir los datos de decisión en una clave pública sencilla:
class NormalizeReviewOutcomeStep implements \Queuety\Step {
public function handle( array $state ): array {
return [
'review_approved' => 'approved' === ( $state['review']['outcome'] ?? null ),
];
}
public function config(): array {
return [];
}
}
Si review_approved todavía no es true, el paso de repetición vuelve a draft. Cuando pasa a ser true, el flujo de trabajo continúa en PublishBriefStep.
Las señales se almacenan, no se consumen. En la segunda pasada por wait_for_decision(), la señal anterior de rejected todavía coincide y la espera termina de inmediato, por lo que el bucle se repetiría hasta que max_transitions() lo detuviera. Asigna una clave a cada ronda: haz que DraftBriefStep devuelva un review_round incremental, pasa correlation_key: 'review_round' a wait_for_decision() y haz que el revisor envíe el mismo review_round en los datos de la señal. Así, la espera solo termina cuando review_round coincide con el valor actual del estado.
repeat_until() con una clase de condición
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 ] );
namespace App\Workflows;
use Queuety\Contracts\RepeatCondition;
final class ReviewApprovedCondition implements RepeatCondition {
public function matches( array $state ): bool {
return 'approved' === ( $state['review']['outcome'] ?? null );
}
}
Las clases de condición reciben el estado público del flujo de trabajo, por lo que el predicado se puede serializar e inspeccionar sin depender de cierres.
repeat_while()
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 ] );
Este patrón resulta útil cuando un paso escribe un valor booleano sencillo como should_poll_again, has_more_pages o needs_revision.
Por qué las repeticiones están separadas de _next_step
Aún puedes crear ramas manualmente devolviendo _next_step desde un paso normal. Las funciones auxiliares de repetición están pensadas para el caso más concreto en que:
- el destino es un paso anterior
- la condición procede del estado público del flujo de trabajo
- quieres que la lógica de repetición siga visible en la definición del flujo de trabajo
Así resulta más fácil inspeccionar las repeticiones en las exportaciones, los registros de eventos y la documentación que si la conexión de retorno queda oculta en código arbitrario de un paso.
Cómo limitar las repeticiones
Usa max_iterations cuando quieras establecer un límite estricto para una repetición concreta:
Queuety::workflow( 'poll_remote_status' )
->then( PollRemoteStatusStep::class, 'poll' )
->repeat_while( 'poll', 'should_poll_again', true, max_iterations: 8 )
->dispatch();
Usa max_transitions() cuando necesites un mecanismo más amplio para detener todo el flujo de trabajo:
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();
Si una repetición supera max_iterations o el flujo de trabajo supera max_transitions(), Queuety marca la ejecución como fallida en vez de dejarla en un bucle infinito.