Aller au contenu

Boucle dynamique sur chaque élément

Utilisez ->for_each() lorsque le nombre de branches du flux de travail est inconnu au moment de la création. Queuety lit un tableau dans l’état du flux, répartit une tâche de branche par élément et ne poursuit que lorsque la condition d’achèvement configurée est remplie.

La méthode ->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 ] );

L’étape du planificateur écrit pages dans l’état du flux de travail. L’étape de boucle lit ce tableau et répartit une tâche de branche par élément.

Gestionnaires de branche

Les gestionnaires de branche de la boucle implémentent 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',
        ];
    }
}

Dans config(), backoff prend un nom de stratégie : exponential, linear ou fixed. Une liste de délais entre tentatives y est actuellement ignorée et les relances sont immédiates (#16) ; définissez plutôt les délais entre tentatives dans backoff de la définition de l’étape.

$state contient l’état complet du flux de travail, $item est l’élément individuel de la boucle et $index sa position à partir de zéro.

Modes d’achèvement

ForEachMode::All

L’étape ne se termine que lorsque toutes les branches ont réussi. L’échec définitif d’une branche fait échouer la boucle.

ForEachMode::FirstSuccess

L’étape se termine dès qu’une première branche réussit. Les branches actives restantes sont mises en attente comme étant supplantées.

ForEachMode::Quorum

L’étape se termine lorsqu’au moins quorum branches ont réussi. Si les branches restantes ne peuvent plus atteindre le quorum, le flux de travail échoue.

Réducteurs

Par défaut, Queuety stocke un agrégat structuré sous result_key :

  • mode
  • quorum
  • total
  • succeeded
  • failed
  • settled
  • winner
  • results
  • failures

Pour créer un récapitulatif personnalisé, transmettez une classe de réducteur qui implémente Contracts\ForEachReducer. L’agrégat reste stocké sous result_key, et les clés renvoyées par le réducteur sont fusionnées dans l’état du flux de travail à côté de celui-ci :

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

La sortie du réducteur est fusionnée dans l’état du flux de travail comme toute autre sortie d’étape. Elle peut aussi renvoyer _next_step pour créer une branche après la fin de la boucle.

Comportement des relances

Les branches de la boucle sont relancées indépendamment selon config() du gestionnaire de branche. Si l’étape de boucle échoue et que vous appelez retry_workflow(), Queuety développe à nouveau l’étape et remet en file d’attente uniquement les branches encore manquantes ou en échec. Les résultats des branches réussies restent dans l’état du flux de travail.

Choisir entre parallel() et for_each()

Utilisez parallel() lorsque la liste des branches est définie dans le code.

Utilisez for_each() lorsque la liste des branches est découverte à l’exécution à partir de l’état du flux de travail et que vous avez besoin de modes d’achèvement comme la première réussite ou le quorum.