انتقل إلى المحتوى

إشارات سير العمل

يمكن إيقاف مهام سير العمل مؤقتًا وانتظار إدخال خارجي باستخدام الإشارات. تُدرج طريقتا المنشئ wait_for_signal() وwait_for_signals() خطوات تحظر سير العمل إلى أن يرسل تطبيقك أو أحد المشغلين أو نظام آخر الإشارة المتوقعة عبر Queuety::signal().

لتدفقات العمل التي تتضمن تدخلًا بشريًا، تتضمن Queuety أيضًا:

  • wait_for_approval() لحمولة موافقة ذات مساحة أسماء
  • wait_for_input() لإدخال بشري منظّم
  • wait_for_decision() لنتائج الموافقة أو الرفض الصريحة

للاطلاع على مثال شامل أكبر يجمع بين تشغيل الوكلاء ومراحل التحقق البشري، راجع تنسيق الوكلاء.

طريقة ->wait_for_signal()

أضف خطوة انتظار إشارة إلى سير العمل:

PHP
use Queuety\Queuety;

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

عندما يصل سير العمل إلى خطوة wait_for_signal، تُضبط حالته على waiting_for_signal ويتوقف مؤقتًا. لا تعمل أي مهام ولا تُستهلك أي موارد أثناء الانتظار.

افتراضيًا، تُدمج حمولة إشارة واحدة في حالة سير العمل ذات المستوى الأعلى. إذا أردت وضع الحمولة تحت مساحة أسماء بدلًا من ذلك، فمرّر result_key:

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

بعد وصول الإشارة، يحتوي $state['approval'] على مصفوفة حمولة الإشارة.

انتظار إشارات متعددة

استخدم wait_for_signals() عندما ينبغي إلغاء حظر سير العمل عند وصول أي من عدة إشارات محتملة، أو عندما يلزم جمع كل الإشارات المطلوبة قبل المتابعة.

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 حتى تسجيل كل إشارة مُهيأة.
  • يستأنف WaitMode::Any التنفيذ فور وصول أول إشارة مُهيأة.

باستخدام result_key، يخزّن انتظار الإشارات المتعددة مصفوفة ترابطية مفاتيحها أسماء الإشارات:

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

من دون result_key، تُدمج حمولات الإشارات المطابقة في الحالة ذات المستوى الأعلى وفق ترتيب إعداد الخطوة.

المطابقة والربط

يمكن لخطوات انتظار الإشارة تجاهل الأحداث غير المرتبطة بمطابقة محتوى الحمولة أو الربط باستخدام مفتاح مشترك.

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

في ذلك المثال، لا يُستأنف الانتظار إلا عندما:

  • يكون اسم الإشارة review_ready
  • تحتوي الحمولة على source: editorial
  • تطابق الحمولة task_id القيمة $state['task_id']

يفيد ذلك عندما تنشر عدة أنظمة خارجية أو وكلاء أسماء الأحداث نفسها، لكن تنتمي حمولة واحدة فقط إلى تشغيل سير العمل الحالي.

أدوات مساعدة للموافقة والإدخال البشريين

تُعد wait_for_approval() وwait_for_input() غلافين ملائمين حول انتظار الإشارات. وهما تعملان مثل wait_for_signal()، لكنهما تستخدمان افتراضيًا مفتاح نتيجة ضمن مساحة أسماء.

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

بعد إشارة approval، يحتوي $state['approval'] على حمولة الموافقة. وبعد إشارة input، يحتوي $state['input'] على حمولة الإدخال.

استخدم wait_for_decision() عندما تريد لخطوة واحدة أن تستأنف التنفيذ سواء بالموافقة أو الرفض:

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

بعد وصول الإشارة، يحتوي $state['review'] على:

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

إرسال إشارة

أرسل إشارة إلى سير العمل باستخدام Queuety::signal():

PHP
Queuety::signal( $workflow_id, 'approved' );

عندما تطابق الإشارة ما ينتظره سير العمل، يستأنف سير العمل التنفيذ وينتقل إلى الخطوة التالية.

لتدفقات العمل التي تتضمن تدخلًا بشريًا، تتيح الواجهة أيضًا أدوات مساعدة دلالية:

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

بيانات الإشارة

يمكنك تمرير مصفوفة بيانات مع الإشارة. تُدمج البيانات في حالة سير العمل، لتصبح متاحة للخطوات اللاحقة:

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

في الخطوة التالية:

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

المفاتيح التي تبدأ بـ _ (شرطة سفلية) محجوزة للاستخدام الداخلي ولا تُدمج في الحالة.

الإشارات الموجودة مسبقًا

يمكن إرسال الإشارات قبل وصول سير العمل إلى خطوة الانتظار. عند وصول إشارة، تُسجل في جدول queuety_signals بصرف النظر عن الحالة الحالية لسير العمل. وعندما يصل سير العمل لاحقًا إلى خطوة wait_for_signal، يتحقق من وجود إشارات مسجلة مسبقًا ويواصل التنفيذ فورًا إذا وجد إحداها.

وهذا يعني أن الترتيب لا يهم:

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.

حالة سير العمل أثناء انتظار الإشارة

أثناء انتظار إشارة، تكون حالة سير العمل 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, ...

حالة استخدام: موافقة بشرية

أنشئ سير عمل للموافقة يتطلب موافقة مراجع بشري على المحتوى قبل نشره:

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 رسالة بريد إلكتروني أو رسالة Slack تتضمن رابطَي الموافقة والرفض. يستدعي هذان الرابطان وحدة التحكم لديك، التي ترسل الإشارة:

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

تقرأ خطوة HandleDecisionHandler القيمة $state['review']['outcome']، ثم تنشر المسودة أو تؤرشفها.

حالة استخدام: استدعاء API خارجي

انتظر إشعار ويب من خدمة خارجية:

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

عندما ترسل الخدمة الخارجية طلبًا إلى نقطة نهاية إشعار الويب لديك:

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

بوابات إشارات متعددة في سير عمل واحد

يمكن لسير العمل انتظار الإشارات في نقاط متعددة:

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

كل خطوة انتظار إشارة مستقلة. يمكنك المزج بين wait_for_signal() وwait_for_signals() وwait_for_approval() وwait_for_input() وwait_for_decision() في سير العمل نفسه.