
# التكرارات

مسارات العمل في Queuety سلاسل خطوات مرتبة. استخدم خطوة تحكم بالتكرار للعودة إلى خطوة سابقة مسماة بدلًا من كتابة منطق `_next_step` يدويًا.

اختر الصيغة المناسبة لسير العمل:

- استخدم `state_key` عامًا عندما يكون الشرط بسيطًا
- استخدم `condition_class` عندما يحتاج التكرار إلى منطق أكثر تعقيدًا
- استخدم `max_iterations` عندما يحتاج التكرار نفسه إلى حد توقف صارم

تستهدف الطريقتان خطوة سابقة مسماة، لذا يظل التكرار قابلًا للتسلسل وإعادة التشغيل، ومحكومًا بوقت تشغيل سير العمل نفسه كسائر الخطوات.

يُقيّم شرط التكرار من حالة سير العمل العامة المحفوظة بعد اكتمال الخطوة السابقة، لذا تظل كل دورة مستدامة عبر عمليات إعادة تشغيل العامل.

## `repeat_until()` باستخدام مفتاح حالة

```php
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` اختزال حمولة القرار إلى مفتاح عام بسيط:

```php
class NormalizeReviewOutcomeStep implements \Queuety\Step {
    public function handle( array $state ): array {
        return [
            'review_approved' => 'approved' === ( $state['review']['outcome'] ?? null ),
        ];
    }

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

إذا لم تصبح `review_approved` بعدُ `true`، تقفز خطوة التكرار عائدةً إلى `draft`. وعندما تصبح `true`، ينتقل سير العمل إلى `PublishBriefStep`.

تُخزن الإشارات ولا تُستهلك. في المرور الثاني عبر `wait_for_decision()`، تظل الإشارة السابقة `rejected` مطابقة، وينتهي الانتظار فورًا، لذا ستستمر الحلقة في الدوران حتى يوقفها `max_transitions()`. خصص مفتاحًا لكل جولة: اجعل `DraftBriefStep` يعيد `review_round` متزايدًا، ومرر `correlation_key: 'review_round'` إلى `wait_for_decision()`، واجعل المراجع يرسل `review_round` نفسه في حمولة الإشارة. عندئذٍ لا ينهي الانتظار إلا signal whose `review_round` يساوي قيمة الحالة الحالية.

## `repeat_until()` باستخدام فئة شرط

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

```php
namespace App\Workflows;

use Queuety\Contracts\RepeatCondition;

final class ReviewApprovedCondition implements RepeatCondition {
    public function matches( array $state ): bool {
        return 'approved' === ( $state['review']['outcome'] ?? null );
    }
}
```

تتلقى فئات الشروط حالة سير العمل العامة، لذا يظل الشرط قابلًا للتسلسل والفحص دون الاعتماد على الإغلاقات.

## `repeat_while()`

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

يفيد هذا النمط عندما تكتب إحدى الخطوات قيمة منطقية بسيطة مثل `should_poll_again` أو `has_more_pages` أو `needs_revision`.

## لماذا تكون التكرارات منفصلة عن `_next_step`

لا يزال بإمكانك إنشاء تفرع يدويًا بإرجاع `_next_step` من خطوة عادية. صُممت أدوات التكرار المساعدة للحالات الأضيق التالية:

- تكون الخطوة المستهدفة خطوة سابقة
- يأتي الشرط من حالة سير العمل العامة
- تريد أن يظل منطق التكرار ظاهرًا في تعريف سير العمل نفسه

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

## وضع قيود على التكرارات

استخدم `max_iterations` عندما يحتاج تكرار محدد إلى حد إيقاف محلي:

```php
Queuety::workflow( 'poll_remote_status' )
    ->then( PollRemoteStatusStep::class, 'poll' )
    ->repeat_while( 'poll', 'should_poll_again', true, max_iterations: 8 )
    ->dispatch();
```

استخدم `max_transitions()` عندما يحتاج سير العمل بأكمله إلى مفتاح إيقاف طارئ أوسع نطاقًا:

```php
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();
```

إذا تجاوز تكرار `max_iterations`، أو تجاوز سير العمل `max_transitions()`، فستُفشل Queuety التشغيل بدلًا من تركه يدور إلى الأبد.

## مستندات ذات صلة

- [التفرع الشرطي](https://queuety.inline0.dev/ar/docs/workflows/conditional)
- [الإشارات](https://queuety.inline0.dev/ar/docs/workflows/signals)
- [ضوابط الحماية](https://queuety.inline0.dev/ar/docs/workflows/guardrails)
- [PHP API](https://queuety.inline0.dev/ar/docs/api)
