
# Планиране

Queuety има вградена поддръжка за повтарящи се задачи. Вместо да разчитат на `wp_cron`, графиците се съхраняват в базата данни и се обработват приблизително веднъж в минута от продължително работещ процес `wp queuety work` (или от родителя на пул от работници). `wp queuety work --once`, `wp queuety flush` и вграденият резервен механизъм WP-Cron на приставката не обработват графици ([#14](https://github.com/inline0/queuety/issues/14)); без продължително работещ работник стартирайте `wp queuety schedule run` сами.

## Планиране по интервал

Използвайте `every()` за прости графици, базирани на интервали. Изразът може да бъде всеки низ, съвместим с PHP `DateTimeImmutable::modify()`:

```php
use Queuety\Queuety;

Queuety::schedule( 'cleanup_handler' )
    ->every( '1 hour' )
    ->on_queue( 'maintenance' );

Queuety::schedule( 'sync_inventory' )
    ->every( '30 minutes' )
    ->on_queue( 'sync' );

Queuety::schedule( 'daily_report', [ 'type' => 'summary' ] )
    ->every( '1 day' );
```

## Планиране чрез Cron

Използвайте `cron()` за стандартни 5-полеви изрази cron:

```php
// Every day at 3am
Queuety::schedule( 'nightly_cleanup' )
    ->cron( '0 3 * * *' );

// Every Monday at 9am
Queuety::schedule( 'weekly_digest' )
    ->cron( '0 9 * * 1' )
    ->on_queue( 'emails' );

// Every 15 minutes
Queuety::schedule( 'health_check' )
    ->cron( '*/15 * * * *' );
```

## Опции на графика

Конструкторът `PendingSchedule` поддържа следните методи:

| Метод | Описание |
|---|---|
| `every( string $interval )` | Задава израз за интервал |
| `cron( string $expression )` | Задава израз cron |
| `on_queue( string $queue )` | Задава целевата опашка (по подразбиране: `'default'`) |
| `overlap( OverlapPolicy $policy )` | Задава правилото за припокриване (по подразбиране: `Allow`) |

Трябва да извикате или `every()`, или `cron()`, преди графикът да изпрати задачата. Ако не е зададено нито едно, се хвърля `RuntimeException`.

## Правила за припокриване

Когато планирана задача все още се изпълнява (в изчакване или обработка) в момента на следващия цикъл, правилото за припокриване определя какво се случва. Задайте го с метода `overlap()`:

```php
use Queuety\Queuety;
use Queuety\Enums\OverlapPolicy;

Queuety::schedule( 'sync_inventory' )
    ->every( '5 minutes' )
    ->overlap( OverlapPolicy::Skip )
    ->on_queue( 'sync' );
```

Enum типът `OverlapPolicy` има три варианта:

| Правило | Стойност | Поведение |
|---|---|---|
| `OverlapPolicy::Allow` | `allow` | Винаги изпраща нова задача, дори ако предишното изпълнение все още е активно. Това е поведението по подразбиране. |
| `OverlapPolicy::Skip` | `skip` | Пропуска текущия цикъл, ако обработчикът има задачи в изчакване или обработка. `next_run` се обновява нормално, така че графикът да запази ритъма си. |
| `OverlapPolicy::Buffer` | `buffer` | Подобно на Skip: цикълът не се изпраща и `next_run` преминава към следващото редовно изпълнение. За разлика от Skip, `last_run` не се обновява. |

### Разрешаване (по подразбиране)

Няколко екземпляра на един и същ обработчик могат да работят едновременно. Използвайте това за идемпотентни обработчици, при които припокриването е безвредно.

```php
Queuety::schedule( 'send_heartbeat_ping' )
    ->every( '1 minute' )
    ->overlap( OverlapPolicy::Allow );
```

### Пропускане

Цикълът се пропуска тихо, ако обработчикът все още работи. Използвайте това за обработчици, при които втори екземпляр би бил излишен или би причинил конфликти.

```php
Queuety::schedule( 'rebuild_search_index' )
    ->every( '10 minutes' )
    ->overlap( OverlapPolicy::Skip );
```

### Буфериране

Днес работи като Skip: когато обработчикът все още има задача в изчакване или обработка, цикълът не се изпраща и `next_run` преминава към следващото редовно изпълнение. Единствената разлика е, че Buffer оставя `last_run` непроменено, докато Skip записва пропуснатия цикъл като последно изпълнение.

```php
Queuety::schedule( 'sync_inventory' )
    ->every( '5 minutes' )
    ->overlap( OverlapPolicy::Buffer );
```

## Как работи

Когато се създава график, Queuety го съхранява в таблицата `queuety_schedules` заедно с израза, обработчика, полезния товар и следващото изчислено време за изпълнение.

При всеки цикъл на планировчика (приблизително веднъж в минута при продължително работещ работник или когато стартирате `wp queuety schedule run`) планировчикът проверява графиците, чието време `next_run` е настъпило. За всеки график, готов за изпълнение, той изпраща нова задача и обновява `next_run` до следващото изпълнение.

## Управление на графици чрез CLI

```bash
# List all schedules
wp queuety schedule list

# Add a recurring schedule
wp queuety schedule add cleanup_handler --every="1 hour"
wp queuety schedule add nightly_report --cron="0 3 * * *" --queue=reports

# Remove a schedule
wp queuety schedule remove cleanup_handler

# Manually trigger the scheduler
wp queuety schedule run
```

## Управление на графици чрез PHP

```php
// Get the scheduler instance
$scheduler = Queuety::scheduler();

// List all schedules
$schedules = $scheduler->list();

// Remove a schedule by handler name
$scheduler->remove( 'cleanup_handler' );

// Manually trigger a tick
$count = $scheduler->tick();
```

## Полезен товар

Графиците могат да включват статичен полезен товар, който се предава на всяка изпратена задача:

```php
Queuety::schedule( 'generate_report', [ 'type' => 'daily', 'format' => 'pdf' ] )
    ->every( '1 day' )
    ->on_queue( 'reports' );
```

Всеки път, когато графикът се задейства, се изпраща нова задача с този полезен товар.
