
# Semnale pentru fluxuri de lucru

Fluxurile de lucru se pot suspenda și pot aștepta date externe folosind semnale. Metodele constructorului `wait_for_signal()` și `wait_for_signals()` inserează pași care blochează fluxul de lucru până când aplicația dumneavoastră, un operator sau un alt sistem trimite semnalul așteptat prin `Queuety::signal()`.

Pentru fluxurile cu intervenție umană, Queuety include și:

- `wait_for_approval()` pentru o structură de date de aprobare cu spațiu de nume
- `wait_for_input()` pentru date structurate introduse de o persoană
- `wait_for_decision()` pentru rezultate explicite de aprobare/respingere

Pentru un exemplu mai amplu, de la un capăt la altul, care combină execuțiile agenților cu puncte de control umane, consultați [Orchestrarea agenților](https://queuety.inline0.dev/ro/docs/workflows/agent-orchestration).

## Metoda `->wait_for_signal()`

Adăugați un pas de așteptare a unui semnal într-un flux de lucru:

```php
use Queuety\Queuety;

$workflow_id = Queuety::workflow( 'content_approval' )
    ->then( DraftContentHandler::class )
    ->wait_for_signal( 'approved' )
    ->then( PublishContentHandler::class )
    ->dispatch( [ 'post_id' => 99 ] );
```

Când fluxul de lucru ajunge la pasul `wait_for_signal`, își setează starea la `waiting_for_signal` și se suspendă. Nu se execută joburi și nu se consumă resurse în timpul așteptării.

În mod implicit, datele unui singur semnal sunt îmbinate cu starea fluxului de lucru de la nivelul superior. Dacă doriți ca datele să aibă în schimb un spațiu de nume, transmiteți un `result_key`:

```php
$workflow_id = Queuety::workflow( 'content_approval' )
    ->then( DraftContentHandler::class )
    ->wait_for_signal( 'approved', 'approval' )
    ->then( PublishContentHandler::class )
    ->dispatch( [ 'post_id' => 99 ] );
```

După sosirea semnalului, `$state['approval']` conține tabloul cu datele semnalului.

## Așteptarea mai multor semnale

Folosiți `wait_for_signals()` când fluxul de lucru trebuie deblocat de oricare dintre mai multe semnale posibile sau când trebuie să colecteze toate semnalele necesare înainte de a continua.

```php
use Queuety\Enums\WaitMode;

$workflow_id = Queuety::workflow( 'moderation_gate' )
    ->then( SubmitForReviewHandler::class )
    ->wait_for_signals(
        [ 'editor_approved', 'legal_approved' ],
        WaitMode::All,
        'approvals'
    )
    ->then( PublishContentHandler::class )
    ->dispatch();
```

- `WaitMode::All` așteaptă până când sunt înregistrate toate semnalele configurate.
- `WaitMode::Any` continuă imediat ce sosește primul semnal configurat.

Cu un `result_key`, o așteptare pentru mai multe semnale stochează un tablou asociativ indexat după numele semnalului:

```php
[
    'editor_approved' => [ 'by' => 'editor@example.com' ],
    'legal_approved' => [ 'by' => 'legal@example.com' ],
]
```

Fără un `result_key`, datele semnalelor care corespund sunt îmbinate în starea de nivel superior în ordinea în care a fost configurat pasul.

## Potrivire și corelare

Așteptările de semnale pot ignora evenimentele fără legătură, potrivind conținutul datelor sau corelându-le pe baza unei chei comune.

```php
$workflow_id = Queuety::workflow( 'review_gate' )
    ->wait_for_signal(
        name: 'review_ready',
        result_key: 'review',
        match_payload: [ 'source' => 'editorial' ],
        correlation_key: 'task_id',
    )
    ->dispatch( [ 'task_id' => 'task-42' ] );
```

În acel exemplu, așteptarea continuă numai când:

- numele semnalului este `review_ready`
- datele conțin `source: editorial`
- `task_id` din date corespunde cu `$state['task_id']`

Acest lucru este util când mai multe sisteme externe sau agenți publică aceleași nume de evenimente, dar numai datele unei singure sarcini curente de flux de lucru îi aparțin.

## Funcții auxiliare pentru aprobarea și introducerea datelor de către oameni

`wait_for_approval()` și `wait_for_input()` sunt funcții wrapper convenabile pentru așteptarea semnalelor. Se comportă ca `wait_for_signal()`, dar folosesc implicit o cheie de rezultat cu spațiu de nume.

```php
$workflow_id = Queuety::workflow( 'blog_review' )
    ->then( GenerateDraftHandler::class )
    ->wait_for_approval()
    ->wait_for_input()
    ->then( HandleDecisionHandler::class )
    ->dispatch();
```

După semnalul `approval`, `$state['approval']` conține datele aprobării. După semnalul `input`, `$state['input']` conține datele introduse.

Folosiți `wait_for_decision()` când doriți ca un singur pas să continue fie la aprobare, fie la respingere:

```php
$workflow_id = Queuety::workflow( 'legal_review' )
    ->then( DraftContractHandler::class )
    ->wait_for_decision( result_key: 'review' )
    ->then( HandleReviewDecisionHandler::class )
    ->dispatch();
```

După sosirea semnalului, `$state['review']` conține:

```php
[
    'outcome' => 'approved', // or 'rejected'
    'signal'  => 'approved', // or 'rejected'
    'data'    => [ 'reviewer' => 'ops@example.com' ],
]
```

## Trimiterea unui semnal

Trimiteți un semnal către un flux de lucru folosind `Queuety::signal()`:

```php
Queuety::signal( $workflow_id, 'approved' );
```

Când semnalul corespunde celui așteptat de fluxul de lucru, acesta continuă și trece la pasul următor.

Pentru fluxurile cu intervenție umană, interfața de nivel superior oferă și funcții semantice auxiliare:

```php
Queuety::approve_workflow( $workflow_id, [ 'reviewer' => 'editor@example.com' ] );
Queuety::reject_workflow( $workflow_id, [ 'reason' => 'needs citations' ] );
Queuety::submit_workflow_input( $workflow_id, [ 'notes' => 'Ship after copy edit' ] );
```

### Datele semnalului

Puteți transmite împreună cu semnalul un tablou de date. Datele sunt îmbinate cu starea fluxului de lucru și devin disponibile pașilor următori:

```php
Queuety::signal( $workflow_id, 'approved', [
    'approved_by' => 'admin@example.com',
    'approved_at' => time(),
] );
```

În pasul următor:

```php
class PublishContentHandler implements Step {
    public function handle( array $state ): array {
        // $state['approved_by'] is 'admin@example.com'
        // $state['approved_at'] is the timestamp
        // $state['post_id'] is 99
        wp_update_post( [
            'ID'          => $state['post_id'],
            'post_status' => 'publish',
        ] );
        return [ 'published' => true ];
    }

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

Cheile care încep cu `_` (underscore) sunt rezervate pentru uz intern și nu sunt îmbinate cu starea.

## Semnale existente dinainte

Semnalele pot fi trimise înainte ca fluxul de lucru să ajungă la pasul de așteptare. Când sosește un semnal, acesta este înregistrat în tabelul `queuety_signals`, indiferent de starea curentă a fluxului de lucru. Când fluxul de lucru ajunge ulterior la pasul `wait_for_signal`, caută semnale existente dinainte și continuă imediat dacă găsește unul.

Asta înseamnă că ordinea nu contează:

```php
// Dispatch workflow
$workflow_id = Queuety::workflow( 'order_fulfillment' )
    ->then( PrepareOrderHandler::class )
    ->wait_for_signal( 'payment_confirmed' )
    ->then( ShipOrderHandler::class )
    ->dispatch( [ 'order_id' => 123 ] );

// Signal arrives before the workflow reaches the wait step
Queuety::signal( $workflow_id, 'payment_confirmed', [ 'transaction_id' => 'txn_abc' ] );

// When the workflow finishes PrepareOrderHandler and reaches the wait step,
// it sees the pre-existing signal and continues immediately.
```

## Starea fluxului de lucru în timpul așteptării unui semnal

În timp ce așteaptă un semnal, starea fluxului de lucru este `WorkflowStatus::WaitingForSignal` (`waiting_for_signal`):

```php
$state = Queuety::workflow_status( $workflow_id );
echo $state->status->value; // 'waiting_for_signal'
echo $state->wait_type;     // 'signal'
echo $state->wait_mode;     // 'all' or 'any'
print_r( $state->waiting_for ); // ['approved']
print_r( $state->wait_details ); // matched/remaining, correlation info, result key, ...
```

## Caz de utilizare: aprobare umană

Construiți un flux de lucru de aprobare în care un evaluator uman trebuie să aprobe conținutul înainte de publicare:

```php
$workflow_id = Queuety::workflow( 'blog_review' )
    ->then( GenerateDraftHandler::class )
    ->then( NotifyReviewerHandler::class )
    ->wait_for_decision( result_key: 'review' )
    ->then( HandleDecisionHandler::class )
    ->dispatch( [ 'topic' => 'PHP middleware patterns' ] );
```

`NotifyReviewerHandler` trimite un e-mail sau un mesaj Slack cu linkuri de aprobare/respingere. Aceste linkuri apelează controllerul dumneavoastră, care trimite semnalul:

```php
// In your approval controller
Queuety::approve_workflow(
    $workflow_id,
    [
        'reviewer'    => $current_user->user_email,
        'reviewed_at' => time(),
    ],
    'approved'
);
```

Pasul `HandleDecisionHandler` citește `$state['review']['outcome']` și fie publică, fie arhivează ciorna.

## Caz de utilizare: callback API extern

Așteptați un webhook de la un serviciu extern:

```php
$workflow_id = Queuety::workflow( 'generate_video' )
    ->then( SubmitToRenderServiceHandler::class )
    ->wait_for_signal( 'render_complete' )
    ->then( DownloadVideoHandler::class )
    ->then( NotifyUserHandler::class )
    ->dispatch( [ 'template_id' => 'promo_v2', 'user_id' => 42 ] );
```

Când serviciul extern trimite o solicitare către endpointul dumneavoastră webhook:

```php
// In your webhook handler
$payload = json_decode( file_get_contents( 'php://input' ), true );
$workflow_id = (int) $payload['metadata']['workflow_id'];

Queuety::signal( $workflow_id, 'render_complete', [
    'video_url'  => $payload['output_url'],
    'duration'   => $payload['duration_seconds'],
] );
```

## Mai multe puncte de așteptare a semnalelor într-un singur flux de lucru

Un flux de lucru poate aștepta semnale în mai multe puncte:

```php
Queuety::workflow( 'multi_approval' )
    ->then( CreateProposalHandler::class )
    ->wait_for_signal( 'manager_approval' )
    ->then( EscalateToDirectorHandler::class )
    ->wait_for_signal( 'director_approval' )
    ->then( ExecuteProposalHandler::class )
    ->dispatch( [ 'proposal_id' => 7 ] );
```

Fiecare pas de așteptare a unui semnal este independent. Puteți combina `wait_for_signal()`, `wait_for_signals()`, `wait_for_approval()`, `wait_for_input()` și `wait_for_decision()` în același flux de lucru.
