Examples

Email Notification Queue

Send emails in the background to avoid blocking the request:

use WPZylos\Framework\Queue\Job;

class SendOrderConfirmation extends Job
{
    protected int $tries = 3;
    protected int $retryAfter = 120;
    protected string $queue = 'emails';

    public function __construct(
        private int $orderId,
        private string $recipientEmail
    ) {}

    public function handle(): void
    {
        $order = wc_get_order($this->orderId);
        
        $subject = "Order #{$this->orderId} Confirmed";
        $body = $this->buildEmailBody($order);
        
        $headers = ['Content-Type: text/html; charset=UTF-8'];
        
        if (!wp_mail($this->recipientEmail, $subject, $body, $headers)) {
            throw new \RuntimeException("Failed to send email to {$this->recipientEmail}");
        }
    }

    private function buildEmailBody($order): string
    {
        ob_start();
        wc_get_template('emails/customer-completed-order.php', ['order' => $order]);
        return ob_get_clean();
    }

    public function failed(\Throwable $exception): void
    {
        error_log("Order confirmation email failed for #{$this->orderId}: {$exception->getMessage()}");
    }
}

// Usage
$queue->push(new SendOrderConfirmation(123, '[email protected]'));

Image Processing

Resize uploaded images in the background:

use WPZylos\Framework\Queue\Job;

class ResizeImage extends Job
{
    protected int $tries = 2;
    protected int $timeout = 120;
    protected string $queue = 'media';

    public function __construct(
        private int $attachmentId,
        private int $width,
        private int $height
    ) {}

    public function handle(): void
    {
        $filePath = get_attached_file($this->attachmentId);
        
        if (!$filePath || !file_exists($filePath)) {
            throw new \RuntimeException("Attachment file not found: {$this->attachmentId}");
        }

        $editor = wp_get_image_editor($filePath);
        
        if (is_wp_error($editor)) {
            throw new \RuntimeException($editor->get_error_message());
        }

        $editor->resize($this->width, $this->height, true);
        
        $newFile = str_replace('.', "-{$this->width}x{$this->height}.", $filePath);
        $editor->save($newFile);

        // Update attachment metadata
        $metadata = wp_get_attachment_metadata($this->attachmentId);
        $metadata['sizes']["custom-{$this->width}x{$this->height}"] = [
            'file'   => basename($newFile),
            'width'  => $this->width,
            'height' => $this->height,
        ];
        wp_update_attachment_metadata($this->attachmentId, $metadata);
    }
}

// Usage
$queue->push(new ResizeImage(456, 800, 600));
$queue->push(new ResizeImage(456, 400, 300));

API Data Sync

Sync data with an external API in the background:

use WPZylos\Framework\Queue\Job;

class SyncProductToApi extends Job
{
    protected int $tries = 5;
    protected int $retryAfter = 300; // 5 minutes
    protected int $timeout = 30;
    protected string $queue = 'sync';

    public function __construct(
        private int $productId,
        private string $action // 'create', 'update', 'delete'
    ) {}

    public function handle(): void
    {
        $product = wc_get_product($this->productId);
        
        if (!$product && $this->action !== 'delete') {
            throw new \RuntimeException("Product {$this->productId} not found");
        }

        $response = wp_remote_request('https://api.example.com/products', [
            'method'  => $this->getHttpMethod(),
            'body'    => wp_json_encode($this->buildPayload($product)),
            'headers' => [
                'Content-Type'  => 'application/json',
                'Authorization' => 'Bearer ' . get_option('api_token'),
            ],
            'timeout' => 15,
        ]);

        if (is_wp_error($response)) {
            throw new \RuntimeException($response->get_error_message());
        }

        $code = wp_remote_retrieve_response_code($response);
        
        if ($code >= 400) {
            throw new \RuntimeException("API returned HTTP {$code}");
        }

        update_post_meta($this->productId, '_api_synced_at', current_time('mysql'));
    }

    private function getHttpMethod(): string
    {
        return match ($this->action) {
            'create' => 'POST',
            'update' => 'PUT',
            'delete' => 'DELETE',
            default  => 'POST',
        };
    }

    private function buildPayload($product): array
    {
        return [
            'id'    => $this->productId,
            'name'  => $product->get_name(),
            'price' => $product->get_price(),
            'sku'   => $product->get_sku(),
        ];
    }

    public function failed(\Throwable $exception): void
    {
        update_post_meta($this->productId, '_api_sync_error', $exception->getMessage());
    }
}

// Usage — sync product when updated
add_action('woocommerce_update_product', function (int $productId) use ($queue) {
    $queue->push(new SyncProductToApi($productId, 'update'));
});

Bulk Operations

Process a large batch of items using multiple jobs:

use WPZylos\Framework\Queue\Job;

class ProcessBulkImport extends Job
{
    protected int $tries = 2;
    protected int $timeout = 300;
    protected string $queue = 'imports';

    public function __construct(
        private array $rows,   // chunk of CSV rows
        private int $batchNum
    ) {}

    public function handle(): void
    {
        foreach ($this->rows as $row) {
            wp_insert_post([
                'post_title'   => sanitize_text_field($row['title']),
                'post_content' => wp_kses_post($row['content']),
                'post_status'  => 'draft',
                'post_type'    => 'product',
            ]);
        }
    }
}

// Dispatch in chunks of 50
$csvRows = parse_csv_file($uploadedFile);
$chunks = array_chunk($csvRows, 50);

foreach ($chunks as $i => $chunk) {
    $queue->push(new ProcessBulkImport($chunk, $i + 1));
}

Scheduled Cleanup

Dispatch a cleanup job with a delay:

use WPZylos\Framework\Queue\Job;

class CleanupExpiredTokens extends Job
{
    protected int $tries = 1;
    protected string $queue = 'maintenance';

    public function handle(): void
    {
        global $wpdb;
        
        $deleted = $wpdb->query(
            $wpdb->prepare(
                "DELETE FROM {$wpdb->prefix}api_tokens WHERE expires_at < %s",
                current_time('mysql', true)
            )
        );

        if ($deleted !== false) {
            error_log("Cleaned up {$deleted} expired tokens");
        }
    }
}

// Run cleanup in 1 hour
$queue->later(3600, new CleanupExpiredTokens());