Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
6b07307
queue retry command done
Nov 13, 2025
21b5fdf
Merge pull request #1 from doppar/commands
techmahedy Nov 13, 2025
fe7213d
console output improved and before and after callback added for comma…
Nov 13, 2025
a1f668d
Merge pull request #2 from doppar/commands
techmahedy Nov 13, 2025
3d07c2e
make:job command for queue
Nov 13, 2025
fafeb61
Merge pull request #3 from doppar/commands
techmahedy Nov 13, 2025
e02b357
queue:flush command for queue
Nov 13, 2025
435d785
Merge pull request #4 from doppar/commands
techmahedy Nov 13, 2025
b4400de
queue:faild command to list all failed jobs
Nov 13, 2025
5ae4260
Merge pull request #5 from doppar/commands
techmahedy Nov 13, 2025
5dba5ee
composer.json updated
Nov 14, 2025
465795e
queue test environment setup done
Nov 14, 2025
f0ce8ae
first test push to queue
Nov 14, 2025
b70acd0
mock jobs class generate
Nov 14, 2025
51c1f0b
custom queue name test
Nov 14, 2025
a7fd93b
push with job delay test
Nov 14, 2025
1eb482c
pop job from queue
Nov 14, 2025
31787a5
pop job as per avaiale_at
Nov 14, 2025
8d560e1
pop job from specific queue
Nov 14, 2025
0ff2f0d
test job execution
Nov 14, 2025
e717e2d
job execution failer and retry
Nov 14, 2025
3ca54f5
Test Job Moved To Failed After Max Attempts
Nov 14, 2025
1a7a3e5
test queue size
Nov 14, 2025
c70ae4f
test queue clear job
Nov 14, 2025
daf70ea
test failed jobs
Nov 14, 2025
a1d877e
test queue model
Nov 14, 2025
1f9bb4d
test mulitple queues
Nov 14, 2025
04b8c3d
test query binding for available and test integration
Nov 14, 2025
041f104
Merge pull request #6 from doppar/unittest
techmahedy Nov 14, 2025
0eab690
dispatch method for chainable way to dispatch a queue job:
Nov 14, 2025
d14ee72
Merge pull request #7 from doppar/queue
techmahedy Nov 14, 2025
7760b45
queue:monitor command
Nov 14, 2025
b4f9bf5
Merge pull request #8 from doppar/queue
techmahedy Nov 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "doppar/guard",
"description": "A authorization package for doppar framework",
"name": "doppar/queue",
"description": "A lightweight queue management library for the Doppar framework.",
"type": "library",
"license": "MIT",
"support": {
"issues": "https://github.com/doppar/guard/issues",
"source": "https://github.com/doppar/guard"
"issues": "https://github.com/doppar/queue/issues",
"source": "https://github.com/doppar/queue"
},
"authors": [
{
Expand All @@ -15,16 +15,17 @@
],
"require-dev": {
"mockery/mockery": "^1.6",
"phpunit/phpunit": "^12.1.5"
"phpunit/phpunit": "^12.1.5",
"doppar/framework": "^3.0.0"
},
"autoload": {
"psr-4": {
"Doppar\\Authorizer\\": "src/"
"Doppar\\Queue\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Doppar\\Authorizer\\Tests\\": "tests/"
"Doppar\\Queue\\Tests\\": "tests/"
}
},
"extra": {
Expand All @@ -33,7 +34,7 @@
},
"doppar": {
"providers": [
"Doppar\\Authorizer\\GuardServiceProvider"
"Doppar\\Queue\\QueueServiceProvider"
]
}
},
Expand Down
16 changes: 12 additions & 4 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit colors="true">
<phpunit backupGlobals="false" beStrictAboutTestsThatDoNotTestAnything="false" colors="true" processIsolation="false" stopOnError="false" stopOnFailure="false" cacheDirectory=".phpunit.cache" backupStaticProperties="false">
<testsuites>
<testsuite name="Unit">
<directory suffix="Test.php">./tests/Unit</directory>
<testsuite name="Doppar Test Suite">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
<php>
<env name="APP_KEY" value="base64:7n/+NIB4i3LQ6+ZbrclxuwyEqG5Uprufs90NJGL2pls="/>
<ini name="date.timezone" value="UTC" />
<ini name="intl.default_locale" value="C.UTF-8" />
<ini name="memory_limit" value="2048M" />
<env name="DB_CONNECTION" value="testing" />
<!--
<env name="REDIS_CLIENT" value="phpredis" />
<env name="REDIS_HOST" value="127.0.0.1" />
<env name="REDIS_PORT" value="6379" />
-->
</php>
</phpunit>
107 changes: 107 additions & 0 deletions src/Commands/MakeJobCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

namespace Doppar\Queue\Commands;

use Phaseolies\Console\Schedule\Command;

class MakeJobCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $name = 'make:job {name}';

/**
* The description of the console command.
*
* @var string
*/
protected $description = 'Create a new Job class';

/**
* Execute the console command.
*
* @return int
*/
protected function handle(): int
{
return $this->executeWithTiming(function () {
$name = $this->argument('name');
$parts = explode('/', $name);
$className = array_pop($parts);

// Ensure class name ends with Job
if (!str_ends_with($className, 'Job')) {
$className .= 'Job';
}

$namespace = 'App\\Jobs' . (count($parts) > 0 ? '\\' . implode('\\', $parts) : '');
$filePath = base_path('app/Jobs/' . str_replace('/', DIRECTORY_SEPARATOR, $name) . '.php');

// Check if Job already exists
if (file_exists($filePath)) {
$this->displayError('Job already exists at:');
$this->line('<fg=white>' . str_replace(base_path(), '', $filePath) . '</>');
return Command::FAILURE;
}

// Create directory if needed
$directoryPath = dirname($filePath);
if (!is_dir($directoryPath)) {
mkdir($directoryPath, 0755, true);
}

// Generate and save Job class
$content = $this->generateJobContent($namespace, $className);
file_put_contents($filePath, $content);

$this->displaySuccess('Job created successfully');
$this->line('<fg=yellow>📦 File:</> <fg=white>' . str_replace(base_path(), '', $filePath) . '</>');
$this->newLine();
$this->line('<fg=yellow>⚙️ Class:</> <fg=white>' . $className . '</>');

return Command::SUCCESS;
});
}

/**
* Generate Job class content.
*/
protected function generateJobContent(string $namespace, string $className): string
{
return <<<EOT
<?php

namespace {$namespace};

use Doppar\Queue\Job;

class {$className} extends Job
{
/**
* Execute the job.
*
* @return void
*/
public function handle(): void
{
//
}

/**
* Handle a job failure.
*
* @param \\Throwable \$exception
* @return void
*/
public function failed(\\Throwable \$exception): void
{
//
}
}

EOT;
}
}
67 changes: 67 additions & 0 deletions src/Commands/QueueFailedCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

namespace Doppar\Queue\Commands;

use Phaseolies\Console\Schedule\Command;
use Doppar\Queue\QueueManager;
use Doppar\Queue\Models\FailedJob;

class QueueFailedCommand extends Command
{
/**
* The name of the console command.
*
* @var string
*/
protected $name = 'queue:failed';

/**
* The command description.
*
* @var string
*/
protected $description = 'List all failed jobs';

/**
* Execute the console command
* Example: php pool queue:failed
*
* @return int
*/
protected function handle(): int
{
$failedJobs = FailedJob::orderBy('failed_at', 'desc')->get();

if ($failedJobs->isEmpty()) {
$this->info("No failed jobs found.");
return Command::SUCCESS;
}

// Create table
$table = $this->createTable();
$table->setHeaders(['ID', 'Job', 'Queue', 'Failed At']);

foreach ($failedJobs as $job) {
$payload = $job->payload;
$data = unserialize($payload);

$jobClass = null;
if ($data && isset($data['job']) && is_object($data['job'])) {
$jobClass = get_class($data['job']);
}

$failedAt = date('Y-m-d H:i:s', $job->failed_at);
$table->addRow([
$job->id,
$jobClass,
$job->queue,
$failedAt
]);
}

// Render table
$table->render();

return Command::SUCCESS;
}
}
62 changes: 62 additions & 0 deletions src/Commands/QueueFlushCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

namespace Doppar\Queue\Commands;

use Phaseolies\Console\Schedule\Command;
use Doppar\Queue\QueueManager;
use Doppar\Queue\Models\FailedJob;

class QueueFlushCommand extends Command
{
/**
* The name of the console command.
*
* @var string
*/
protected $name = 'queue:flush {--id=}';

/**
* The command description.
*
* @var string
*/
protected $description = 'Delete failed job(s) by ID or all if no ID is provided';

/**
* Execute the console command
* Example: php pool queue:flush --id=1
*
* @return int
*/
protected function handle(): int
{
$id = $this->option('id');

if ($id) {
return $this->flushJobById($id);
}

FailedJob::query()
->cursor(function (FailedJob $failedJob) {
$failedJob->delete();
$this->info("✔ Job with ID {$failedJob->id} has been deleted.");
});

return Command::SUCCESS;
}

protected function flushJobById(int $id): int
{
$failedJob = FailedJob::find($id);

if (!$failedJob) {
$this->error("Failed job with ID {$id} not found.");
return Command::FAILURE;
}

$failedJob->delete();
$this->info("✔ Job with ID {$failedJob->id} has been deleted.");

return Command::SUCCESS;
}
}
71 changes: 71 additions & 0 deletions src/Commands/QueueMonitorCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

namespace Doppar\Queue\Commands;

use Phaseolies\Console\Schedule\Command;
use Doppar\Queue\Models\QueueJob;
use Doppar\Queue\Models\FailedJob;

class QueueMonitorCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $name = 'queue:monitor';

/**
* The description of the console command.
*
* @var string
*/
protected $description = 'Monitor queue statistics';

/**
* Execute the console command.
*
* @return int
*/
protected function handle(): int
{
$queues = QueueJob::groupBy('queue')->pluck('queue');

// Create table for queue statistics
$table = $this->createTable();
$table->setHeaders(['Queue', 'Pending', 'Processing']);

foreach ($queues ?? [] as $queue) {
$pending = QueueJob::where('queue', $queue)
->whereNull('reserved_at')
->count();

$processing = QueueJob::where('queue', $queue)
->whereNotNull('reserved_at')
->count();

$table->addRow([
$queue,
$pending,
$processing,
]);
}

// Render queue table
$this->newLine();
$this->info("Queue Statistics");
$table->render();

// Failed jobs table
$failedCount = FailedJob::count();

$failedTable = $this->createTable();
$failedTable->setHeaders(['Metric', 'Value']);
$failedTable->addRow(['Failed Jobs', $failedCount]);

$this->info("\nFailed Jobs Summary");
$failedTable->render();

return Command::SUCCESS;
}
}
Loading