{"id":14983907,"url":"https://github.com/tomatomcz/jobqueuebundle","last_synced_at":"2025-04-10T19:43:20.533Z","repository":{"id":183481608,"uuid":"670186746","full_name":"TomAtomCZ/JobQueueBundle","owner":"TomAtomCZ","description":"Symfony bundle which aims to replace JMSJobQueueBundle for scheduling console commands, with complete browser interface.","archived":false,"fork":false,"pushed_at":"2025-03-03T11:05:09.000Z","size":513,"stargazers_count":3,"open_issues_count":1,"forks_count":1,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-03-24T17:21:24.197Z","etag":null,"topics":["command-scheduler","job-queue","job-scheduler","scheduler","symfony-bundle"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TomAtomCZ.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-07-24T13:34:49.000Z","updated_at":"2025-03-03T11:03:57.000Z","dependencies_parsed_at":null,"dependency_job_id":"2d149088-0cd4-4cdb-b584-549b66ec76e2","html_url":"https://github.com/TomAtomCZ/JobQueueBundle","commit_stats":{"total_commits":28,"total_committers":3,"mean_commits":9.333333333333334,"dds":0.5,"last_synced_commit":"847e7980667c0c9b49d80a6e52a824a9b4be00ba"},"previous_names":["tomatomcz/jobqueuebundle"],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TomAtomCZ%2FJobQueueBundle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TomAtomCZ%2FJobQueueBundle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TomAtomCZ%2FJobQueueBundle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TomAtomCZ%2FJobQueueBundle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TomAtomCZ","download_url":"https://codeload.github.com/TomAtomCZ/JobQueueBundle/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248281424,"owners_count":21077423,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["command-scheduler","job-queue","job-scheduler","scheduler","symfony-bundle"],"created_at":"2024-09-24T14:08:09.504Z","updated_at":"2025-04-10T19:43:20.497Z","avatar_url":"https://github.com/TomAtomCZ.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# JobQueueBundle\n\n### Symfony bundle which aims to replace JMSJobQueueBundle for scheduling console commands, with complete browser interface.\n\n## Table of Contents\n\n1. [Features](#features)\n2. [Installation](#installation)\n3. [Configuration](#configuration)\n    - [Bundles](#configbundlesphp)\n    - [Routes](#configroutesyaml)\n    - [Messenger](#configpackagesmessengeryaml)\n    - [Security](#configpackagessecurityyaml)\n    - [Job Queue](#configpackagesjob_queueyaml)\n4. [Usage](#usage)\n    - [Job Types](#types-of-ways-jobs-can-be-run)\n    - [Creating Jobs Programmatically](#manually-creating-the-jobs-in-your-application)\n    - [Creating Jobs via Browser](#creating-jobs-via-the-browser-interface)\n5. [Testing](#testing)\n6. [Dependencies](#dependencies)\n7. [TODO](#todo)\n8. [Additional info / Contributing](#additional-info--contributing)\n\n## Features\n\n- Schedule any command from your app as a server-side job, either programmatically or through a browser interface.\n- Run jobs right away, postpone them or make them recurring.\n- Browse jobs and see their details in browser.\n- Cancel and retry jobs.\n- Add related entity and parent job.\n- Capture and store specific output from commands in the job's output parameters.\n\n## Installation\n\n```\ncomposer require tomatom/jobqueuebundle\n```\n\n## Configuration\n\n#### config/bundles.php:\n\n```php\nTomAtom\\JobQueueBundle\\JobQueueBundle::class =\u003e ['all' =\u003e true]\n```\n\n\u003chr\u003e\n\n#### config/routes.yaml:\n\n```yaml\njob_queue:\n  resource: \"@JobQueueBundle/src/Controller/\"\n  type: attribute\n```\n\n\u003chr\u003e\n\n#### config/packages/messenger.yaml:\n\nYou can create your own transport for the job messages - or just use *async* transport\n\n```yaml\nframework:\n  messenger:\n    # Your messenger config\n    transports:\n    # Your other transports\n    job_message:\n      dsn: \"%env(MESSENGER_TRANSPORT_DSN)%\"\n      options:\n        queue_name: job_message\n    routing:\n      TomAtom\\JobQueueBundle\\Message\\JobMessage: job_message # or async\n```\n\n\u003chr\u003e\n\n#### config/packages/security.yaml:\n\nThe bundle uses the role security system to control access for the jobs/command scheduling. You can assign roles based\non the\nlevel of access you want to grant to each user.\n\nAvailable roles are:\n\n**ROLE_JQB_ALL** - The main role with full permissions. Provides unrestricted access to all features of\nthe bundle.\n\n**ROLE_JQB_JOBS** - Grants full permissions for jobs (JOB_ roles).\n\n**ROLE_JQB_COMMANDS** - Grants full permissions for command scheduling (COMMAND_ roles).\n\n**ROLE_JQB_JOB_LIST** - Allows access to view the job list.\n\n**ROLE_JQB_JOB_READ** - Allows access to view job details.\n\n**ROLE_JQB_JOB_CREATE** - Allows creating new jobs.\n\n**ROLE_JQB_JOB_DELETE** - Allows deleting jobs.\n\n**ROLE_JQB_JOB_CANCEL** - Allows canceling jobs.\n\n**ROLE_JQB_COMMAND_SCHEDULE** - Allows scheduling commands.\n\n(Also with constants in [JobQueuePermissions.php](src/Security/JobQueuePermissions.php))\n\nTo grant full access to users, add **ROLE_JQB_ALL** to the role hierarchy:\n\n```yaml\nsecurity:\n  role_hierarchy:\n    ROLE_ADMIN:\n      - ROLE_JQB_ALL\n```\n\nTo restrict access for example to only viewing the job list and job details (without creation or scheduling), configure\nthe roles like this:\n\n```yaml\nsecurity:\n  role_hierarchy:\n    ROLE_USER:\n      - ROLE_JQB_JOB_LIST\n      - ROLE_JQB_JOB_READ\n```\n\n#### Note - jobs creation is always possible where security has no loaded user, for example if created in a command.\n\n\u003chr\u003e\n\n#### config/packages/job_queue.yaml:\n\nYou do not have to create this file for the bundle to work, but you can edit some parameters\n\n```yaml\njob_queue:\n  database:\n    job_table_name: \"your_job_table_name\" # Default = job_queue\n    job_recurring_table_name: \"your_job_recurring_table_name\" # Default = job_recurring_queue\n  scheduling:\n    heartbeat_interval: \"1 hour\" # Default = 1 minute\n```\n\n#### Update your database so the job tables are created\n\n```shell\nphp bin/console d:s:u --force\n```\n\nor via migrations.\n\n#### Do not forget to run the messenger\n\nThis is up to you and where your project runs, but you need to have the messenger consuming the right transport for the\nbundle to work.\n\n```shell\nphp bin/console messenger:consume job_message\n```\n\nFor recurring messages you also need the scheduler running so the jobs are created\n\n```shell\nphp bin/console messenger:consume scheduler_job_recurring\n```\n\n## Usage\n\n#### Types of ways jobs can be run\n\n- **Once** - Runs once right after creation (Job entity)\n- **Once postponed** - Runs once on given time (Job entity)\n- **Recurring**\n    - Runs repeatedly on time by the\n      given [Symfony scheduler cron expression](https://symfony.com/doc/current/scheduler.html#cron-expression-triggers)\n      (JobRecurring entity which\n      creates new Job entity on every run)\n    - Changes in the recurring jobs (adding/deleting/editing) are handled by the \"heartbeat\" message, which runs on the\n      given interval (default is 1 minute but can be edited in the config file - if you do not add / edit them often,\n      you can set it to higher value)\n\nOnce job is created, [Symfony messenger](https://symfony.com/doc/current/messenger.html) message is created which\nhandles the run of the command from the job.\n\n### Manually creating the jobs in your application:\n\nThe function __createCommandJob__ from __CommandJobFactory__ accepts:\n\n* command name\n* command parameters\n* ID of related entity (optional)\n* name of related entity class - (optional)\n* job entity for parent job (optional)\n* entity of recurring parent job (optional)\n* datetime of postponed job start (optional)\n\nand returns the created job.\n\n**Basic example**:\n\n```php\n$commandName = 'app:your:command';\n\n$params = [\n    '--param1=' . $request-\u003eget('param1'),\n    '--param2=' . $request-\u003eget('param2'),\n];\n\n// Try to create the command job\ntry {\n    $job = $this-\u003ecommandJobFactory-\u003ecreateCommandJob($commandName, $params);\n} catch (OptimisticLockException|ORMException|CommandJobException $e) {\n    // Redirect back upon failure\n    $this-\u003elogger-\u003eerror('createCommandJob error: ' . $e-\u003egetMessage());\n    return $this-\u003eredirectToRoute('your_route');\n}\n\n// Redirect to the command job detail\nreturn $this-\u003eredirectToRoute('job_queue_detail', ['id' =\u003e $job-\u003egetId()]);\n```\n\n**Adding a related entity**:\n\nPurpose of this is to filter jobs seen in the list by the related entity.\n\nFor example, if you have a Customer entity:\n\n```php\n$job = $this-\u003ecommandJobFactory-\u003ecreateCommandJob($commandName, $params, $customer-\u003egetId(), Customer::class);\n```\n\nIf you then go to the job list with parameters /job/list/**Customer**/**1** (which is being automatically\nadded if going from the detail with related entity) or if you add it to the list path yourself like:\n\n```twig\n\u003ca href=\"{{ path('job_queue_list', {'name': constant('class', customer), 'id': customer.id}) }}\"\u003e{{ 'job.job_list'|trans }}\u003c/a\u003e\n```\n\nthen the job list only contains jobs for that given customer.\n\nYou can also only add the entity name to get all jobs for a given entity.\n\n**Adding a parent job**:\n\nJobs can have another one as a parent job. One job can have multiple children jobs.\n\nThis can be used if for example you need to create a job that has to run after another job finishes.\n\n(Recreating jobs also creates a new one with the original as a parent.)\n\n```php\n// Retrieve another job entity to add as a parent job\n$parentJob = $this-\u003eentityManager-\u003egetRepository(Job::class)-\u003efindOneBy(['command' =\u003e $command, 'status' =\u003e Job::STATUS_COMPLETED]);\n$job = $this-\u003ecommandJobFactory-\u003ecreateCommandJob($commandName, $params, null, null, $parentJob);\n```\n\nIf jobs have any children/parent there will be button links to them in the job detail (for parents also in job list).\n\n**Creating a postponed job**:\n\nIf you want to set a command to run once in given time - set $startAt of type DateTimeImmutable\n\n```php\n$startAt = DateTimeImmutable::createFromFormat('Y-m-d\\TH:i', $postponedDateTime);\n$job = $commandJobFactory-\u003ecreateCommandJob($commandName, $params, $listId, $listName, null, null, $startAt);\n```\n\n**Creating / updating a recurring job**:\n\n```php\n$jobRecurring = $this-\u003eentityManager-\u003egetRepository(JobRecurring::class)-\u003efind($id);\nif ($jobRecurring) {\n    $commandJobFactory-\u003eupdateRecurringCommandJob($jobRecurring, $commandName, $params, $frequency, $active);\n} else {\n    $commandJobFactory-\u003ecreateRecurringCommandJob($commandName, $params, $frequency, $active);\n}\n```\n\nWhere both functions call the function __saveRecurringCommandJob__ from __CommandJobFactory__, which accepts:\n\n* job recurring - updated recurring job (only on updateRecurringCommandJob)\n* command name\n* command params\n* frequency\n  of type [Symfony scheduler cron expression](https://symfony.com/doc/current/scheduler.html#cron-expression-triggers)\n* is active\n\n**Saving values from the command output**:\n\nIf you need to retrieve and save any data from the output of a command that is running from a job, you can do that by\nadding anything after\nconstant **Job::COMMAND_OUTPUT_PARAMS** in the command output, for example:\n\n```php\n$io-\u003einfo(Job::COMMAND_OUTPUT_PARAMS . $customerId) // $customerId = 123;\n```\n\nThis will output in the console **OUTPUT PARAMS: 123** and the '123' will be saved in the job's **outputParams**, which\ncan be then used for example to retrieve the customer entity.\n\n```php\n$customer = $this-\u003eentityManager-\u003egetRepository(Customer::class)-\u003efind($job-\u003egetOutputParams());\n```\n\nOutput params are saved in the database as a TEXT and you can save multiple values, which are then separated by a comma,\nfor example:\n\n```php\n$io-\u003einfo(Job::COMMAND_OUTPUT_PARAMS . 123);\n$io-\u003einfo(Job::COMMAND_OUTPUT_PARAMS . 'some text value');\n$io-\u003einfo(Job::COMMAND_OUTPUT_PARAMS . implode(['a', 'b']));\n```\n\nthis will be saved as '123, some text value, ab' and then you need to individually handle getting the values by\nwhat you've\nsaved.\n\n### Creating jobs via the browser interface:\n\nAvailable urls:\n\n- **command/schedule** - Create a command to run as job\n- **command/schedule/{id}** - Edit recurring job\n- **job/list/{name}/{id}** - List jobs (related entity name+id)\n- **job/recurring/list** - List recurring jobs\n- **job/{id}** - Job detail with command output\n\n| Schedule Command                                                 | Job List                                         | Job Detail                                           |\n|------------------------------------------------------------------|--------------------------------------------------|------------------------------------------------------|\n| \u003cimg src=\"docs/img_schedule_command.png\" alt=\"Schedule Command\"\u003e | \u003cimg src=\"docs/img_job_list.png\" alt=\"Job List\"\u003e | \u003cimg src=\"docs/img_job_detail.png\" alt=\"Job Detail\"\u003e |\n\nJob detail gets updated automatically while the job is running.\n\n**All the pages are also responsive for mobile use.**\n\nExtending the templates can be done like this:\n\n```twig\n{# templates/job/detail.html.twig #}\n\n{% extends '@JobQueue/job/detail.html.twig' %}\n\n{% block title %}...{% endblock %}\n\n{% block header %}...{% endblock %}\n\n{% block body %}...{% endblock %}\n```\n\nTo change or add translations for a new locale, use translation variables from bundle's translations in your\ntranslations/messages.{locale}.yaml:\n\n(Currently there are only translations for *en* and *cs* locales)\n\n## Testing\n\nThe bundle has ready tests for job creations in the tests/ folder.\nRunning tests in your app can be done like this:\n\n```bash\nvendor/bin/phpunit vendor/tomatom/jobqueuebundle/tests/\n```\n\nThe tests are also run on every push / pull request on GitHub.\n\n## Dependencies\n\n* \"php\": \"\u003e=8.1\",\n* \"doctrine/doctrine-bundle\": \"^2\",\n* \"doctrine/orm\": \"^2|^3\",\n* \"dragonmantank/cron-expression\": \"^3\",\n* \"knplabs/knp-paginator-bundle\": \"^6\",\n* \"spiriitlabs/form-filter-bundle\": \"^11\",\n* \"symfony/form\": \"6.4 || ^7.2\",\n* \"symfony/framework-bundle\": \"6.4 || ^7.2\",\n* \"symfony/lock\": \"6.4 || ^7.2\",\n* \"symfony/messenger\": \"6.4 || ^7.2\",\n* \"symfony/process\": \"6.4 || ^7.2\",\n* \"symfony/scheduler\": \"6.4 || ^7.2\",\n* \"symfony/security-bundle\": \"6.4 || ^7.2\",\n* \"symfony/translation\": \"6.4 || ^7.2\",\n* \"twig/twig\": \"^2|^3\"\n\n## TODO\n\n[Handle getting changes of recurring jobs in better way](/../../issues/3)\n\n## Additional info / Contributing\n\nSpecial thanks to [schmittjoh](https://github.com/schmittjoh) for the\noriginal [JMSJobQueueBundle](https://github.com/schmittjoh/JMSJobQueueBundle).\n\nThis bundle **is not a fork, nor is building on top of the original bundle**, it's our own take on the console\ncommand scheduling, so please bear that in mind when using it. However, going from the original to this bundle should be\nseamless.\n\nFeel free to open any issues or pull requests if you find something wrong or missing what you'd like the bundle to have!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomatomcz%2Fjobqueuebundle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftomatomcz%2Fjobqueuebundle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomatomcz%2Fjobqueuebundle/lists"}