إشارات سير العمل
يمكن إيقاف مهام سير العمل مؤقتًا وانتظار إدخال خارجي باستخدام الإشارات. تُدرج طريقتا المنشئ wait_for_signal() وwait_for_signals() خطوات تحظر سير العمل إلى أن يرسل تطبيقك أو أحد المشغلين أو نظام آخر الإشارة المتوقعة عبر Queuety::signal().
لتدفقات العمل التي تتضمن تدخلًا بشريًا، تتضمن Queuety أيضًا:
wait_for_approval()لحمولة موافقة ذات مساحة أسماءwait_for_input()لإدخال بشري منظّمwait_for_decision()لنتائج الموافقة أو الرفض الصريحة
للاطلاع على مثال شامل أكبر يجمع بين تشغيل الوكلاء ومراحل التحقق البشري، راجع تنسيق الوكلاء.
طريقة ->wait_for_signal()
أضف خطوة انتظار إشارة إلى سير العمل:
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:
$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() عندما ينبغي إلغاء حظر سير العمل عند وصول أي من عدة إشارات محتملة، أو عندما يلزم جمع كل الإشارات المطلوبة قبل المتابعة.
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، يخزّن انتظار الإشارات المتعددة مصفوفة ترابطية مفاتيحها أسماء الإشارات:
[
'editor_approved' => [ 'by' => 'editor@example.com' ],
'legal_approved' => [ 'by' => 'legal@example.com' ],
]
من دون result_key، تُدمج حمولات الإشارات المطابقة في الحالة ذات المستوى الأعلى وفق ترتيب إعداد الخطوة.
المطابقة والربط
يمكن لخطوات انتظار الإشارة تجاهل الأحداث غير المرتبطة بمطابقة محتوى الحمولة أو الربط باستخدام مفتاح مشترك.
$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()، لكنهما تستخدمان افتراضيًا مفتاح نتيجة ضمن مساحة أسماء.
$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() عندما تريد لخطوة واحدة أن تستأنف التنفيذ سواء بالموافقة أو الرفض:
$workflow_id = Queuety::workflow( 'legal_review' )
->then( DraftContractHandler::class )
->wait_for_decision( result_key: 'review' )
->then( HandleReviewDecisionHandler::class )
->dispatch();
بعد وصول الإشارة، يحتوي $state['review'] على:
[
'outcome' => 'approved', // or 'rejected'
'signal' => 'approved', // or 'rejected'
'data' => [ 'reviewer' => 'ops@example.com' ],
]
إرسال إشارة
أرسل إشارة إلى سير العمل باستخدام Queuety::signal():
Queuety::signal( $workflow_id, 'approved' );
عندما تطابق الإشارة ما ينتظره سير العمل، يستأنف سير العمل التنفيذ وينتقل إلى الخطوة التالية.
لتدفقات العمل التي تتضمن تدخلًا بشريًا، تتيح الواجهة أيضًا أدوات مساعدة دلالية:
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' ] );
بيانات الإشارة
يمكنك تمرير مصفوفة بيانات مع الإشارة. تُدمج البيانات في حالة سير العمل، لتصبح متاحة للخطوات اللاحقة:
Queuety::signal( $workflow_id, 'approved', [
'approved_by' => 'admin@example.com',
'approved_at' => time(),
] );
في الخطوة التالية:
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، يتحقق من وجود إشارات مسجلة مسبقًا ويواصل التنفيذ فورًا إذا وجد إحداها.
وهذا يعني أن الترتيب لا يهم:
// 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):
$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, ...
حالة استخدام: موافقة بشرية
أنشئ سير عمل للموافقة يتطلب موافقة مراجع بشري على المحتوى قبل نشره:
$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 تتضمن رابطَي الموافقة والرفض. يستدعي هذان الرابطان وحدة التحكم لديك، التي ترسل الإشارة:
// In your approval controller
Queuety::approve_workflow(
$workflow_id,
[
'reviewer' => $current_user->user_email,
'reviewed_at' => time(),
],
'approved'
);
تقرأ خطوة HandleDecisionHandler القيمة $state['review']['outcome']، ثم تنشر المسودة أو تؤرشفها.
حالة استخدام: استدعاء API خارجي
انتظر إشعار ويب من خدمة خارجية:
$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 ] );
عندما ترسل الخدمة الخارجية طلبًا إلى نقطة نهاية إشعار الويب لديك:
// 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'],
] );
بوابات إشارات متعددة في سير عمل واحد
يمكن لسير العمل انتظار الإشارات في نقاط متعددة:
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() في سير العمل نفسه.