dmstr / symfony-job-queue-bundle
Persisted job tracking on top of Symfony Messenger: Job entity with status, JobQueueService for dispatch, JobMessage/JobMessageHandler for async execution.
Package info
github.com/dmstr/symfony-job-queue-bundle
Type:symfony-bundle
pkg:composer/dmstr/symfony-job-queue-bundle
Requires
- php: >=8.5
- dmstr/openapi-json-schema-bundle: ^0.4.0
- doctrine/doctrine-bundle: ^2.0
- doctrine/orm: ^3.0
- opis/json-schema: ^2.6
- symfony/framework-bundle: ^7.0
- symfony/messenger: ^7.0
- symfony/uid: ^7.0
Requires (Dev)
- ext-pdo_sqlite: *
- api-platform/doctrine-orm: ^4.3.18
- api-platform/symfony: ^4.3.18
- doctrine/dbal: ^3.7
- doctrine/doctrine-migrations-bundle: ^3.7
- doctrine/migrations: ^3.9
- phpunit/phpunit: ^12.0
- symfony/browser-kit: ^7.4
- symfony/expression-language: ^7.4
- symfony/security-bundle: ^7.4
- symfony/yaml: ^7.4
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-29 20:56:06 UTC
README
dmstr/symfony-job-queue-bundle
Persisted job tracking on top of Symfony Messenger. The bundle stores every dispatched job in a Doctrine entity, validates its input against a JSON schema, runs it asynchronously through a Messenger handler and exposes the jobs as a read-only API Platform resource.
JobDoctrine entity with status, payload, timestamps and resultJobQueueService::dispatch()validates the input, persists the job and dispatches it to MessengerJobMessage/JobMessageHandlerwrap the asynchronous execution- Status transitions:
pending → running → done | failed Jobis exposed as read-only API Platform resource under/admin/jobs, guarded by the permissionjob:read
Installation
composer require dmstr/symfony-job-queue-bundle
Requires PHP >= 8.5 and Symfony 7. Besides the Symfony packages (framework-bundle, messenger, uid) and Doctrine ORM with DoctrineBundle, the bundle declares dmstr/openapi-json-schema-bundle and opis/json-schema as runtime dependencies, because JobQueueService uses them. They pull api-platform/metadata, json-schema and openapi in at runtime; applications built on za7-core already have all of this.
Configuration
The application has to provide four things.
-
Doctrine ORM mapping for the entity directory of the bundle:
doctrine: orm: mappings: DmstrSymfonyJobQueue: type: attribute is_bundle: false dir: '%kernel.project_dir%/vendor/dmstr/symfony-job-queue-bundle/src/Entity' prefix: 'Dmstr\SymfonyJobQueue\Entity' -
API Platform mapping, so the
Jobresource is discovered:api_platform: mapping: paths: - '%kernel.project_dir%/vendor/dmstr/symfony-job-queue-bundle/src/Entity' -
The service
Dmstr\OpenApiJsonSchema\Service\OperationInputSchemaResolvermust be registered. za7-core does this; without it, register the service yourself. -
Messenger must be enabled, because
JobQueueServiceneedsMessageBusInterface.
The job resource
Job is a read-only API Platform resource with the operations Get and GetCollection, served below the API prefix of the application:
GET /admin/jobslists jobsGET /admin/jobs/{id}returns one job
The collection does not offer order[id]; a submitted order[id] parameter is ignored.
Permission job:read
Both operations require the permission job:read (security: "is_granted('job:read')"). The permission is declared by the final class Dmstr\SymfonyJobQueue\Security\JobPermissions (JobPermissions::READ), which the bundle registers in config/services.yaml with the container tag za7_core.permission and the attribute permission: job:read.
The bundle has no config node, no role map and no dependency on za7-core. Who holds the permission is decided by the application: the za7-core PermissionVoter collects the tagged permissions and decides through its role map za7_core.permissions:
za7_core:
permissions:
job:read: ['<role that may read jobs>']
Two meanings of the same string
job:read is also the Serializer normalization group of the Job resource. These are two separate worlds that happen to share a string: as voter attribute it answers "may this user call the endpoint", as serializer group it selects which properties appear in the response. Mapping the permission in za7_core.permissions does not change the serialized fields, and the group does not grant access.
Running without za7-core
Without za7-core the tag is inert. Unless another voter in the application supports the attribute job:read, every request to the job resource gets 403 (fail-closed). To use the bundle without za7-core, add a voter for job:read.
Migrations
If DoctrineMigrationsBundle is registered, the bundle prepends doctrine_migrations.migrations_paths with the namespace Dmstr\SymfonyJobQueue\Migrations, so doctrine:migrations:migrate creates the table dmstr_job. Without DoctrineMigrationsBundle nothing is prepended and the schema has to be created by the application.
Migrating to 0.4.0
- Upgrade the bundle to 0.4.0 and the runtime to PHP 8.5.
- Deploy the za7-core release that ships the
PermissionVoter. - Map
job:readto the desired role inza7_core.permissions. - Up to 0.3.x the job resource only required an authenticated user with
ROLE_USER. Applications that so far let ordinary users read jobs either grant the mapped role to them explicitly or build their own scoped endpoint.
Until step 3 is done, everyone gets 403 on /admin/jobs. See the CHANGELOG.
Tests
PHPUnit (^12). The bundle ships a CLI-only Docker Compose runner without a database server; no host PHP is required:
docker compose run --rm php
It installs the dependencies and runs vendor/bin/phpunit, including functional tests with a small test kernel (API Platform, Doctrine on SQLite, a header-based test authenticator X-Test-Roles). The same suite runs in CI via .github/workflows/tests.yml.
License
MIT © diemeisterei GmbH