Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Symfony Schedule Bundle Laravel Package

m-adamski/symfony-schedule-bundle

A Symfony bundle for scheduling recurring tasks and console commands. Define schedules in configuration, run jobs on cron-like intervals, and integrate cleanly with Symfony's console and dependency injection.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require m-adamski/symfony-schedule-bundle:^4.0

4.0.0 is a Symfony 7.x release. The changelog is a single line — Symfony 7.x — so treat the composer.json constraint as the real release note: confirm whether ^6.4 is still permitted before assuming this is a drop-in. If you're on Symfony 6.x, stay on the 3.x line and pin it; if you're on 6.4 and planning the 7 upgrade, do the bundle bump in the same window so you're not testing two major bumps independently.

If Flex doesn't auto-register it, add MAdamski\SymfonyScheduleBundle\ScheduleBundle::class => ['all' => true] to config/bundles.php. There is essentially no configuration to publish — with a bundle this size, the DI extension class (DependencyInjection/*Extension.php) is the documentation, and it's a five-minute read. Re-read it after the major bump; it's the file most likely to have moved or been renamed.

First use case: a single job. Wrap whatever you already do in a console command — e.g. pruning stale sessions — in a service that implements the bundle's task contract (check the Schedule/Task namespace for the exact interface and method names; it's the only real API surface), give it a cron expression, and add exactly one system cron entry:

* * * * * cd /var/www/app && bin/console schedule:run >> /dev/null 2>&1

Run it once by hand with bin/console schedule:run -vvv before trusting the cron. That single command is the entire mental model: a once-per-minute dispatcher that evaluates expressions and invokes due services.

On a fresh Symfony 7 app, read the last Gotcha before committing to this — the bundle now targets the same major as symfony/scheduler, so the "native is only for greenfield" argument no longer applies.

Implementation Patterns

  • Tasks are services. One class per job, autowired, injecting repositories and HTTP clients normally. Keep the scheduled method a thin wrapper — ideally dispatching a Messenger message — so retries, async workers, and failure transport come for free.
  • Attribute vs. interface. If the bundle supports a method-level attribute, use that for one-off jobs and fall back to the interface + DI tag when tasks live in third-party code or need conditional registration. On 4.0.0, verify the attribute/interface signatures against source rather than blog posts — a major bump that changes the Symfony constraint very often carries small signature changes that no changelog entry mentions.
  • Overlap protection is your job. A * * * * * cron plus a slow task means concurrent executions. Wrap the body in symfony/lock ($lock->acquire() / early return) rather than hoping jobs stay fast.
  • Idempotency first. Assume any job can run twice — a missed run, a DST fold, a manual invocation. Design for "safe to repeat" and you never need to think about it again.
  • Environment gating. Disable schedules in dev/test via config (when@dev), not by commenting out cron entries, so the container stays consistent across environments.
  • Testing. Because tasks are plain services, unit-test them by calling __invoke() directly and assert the cron expression as a string literal. For integration coverage, boot the kernel in a test and invoke schedule:run against a frozen clock. Symfony 7's Clock component makes the frozen-clock half much cleaner than it was on 5.4/6.x.
  • Upgrading 3.x → 4.x. Do it on a branch, run the full schedule test suite, and diff config/bundles.php/service tags. Because job bodies live in your own classes the blast radius is small, but registration tags are the thing that silently breaks — a task that stops being tagged simply never runs, with no error.
  • Observability. Log start/end with a job name and duration, or decorate the runner service. Without this you have no idea whether "nothing happened" means "not due" or "silently broke."

Gotchas and Tips

  • The cron entry is the scheduler. If it's missing, the binary path is wrong for the cron user, or the working directory isn't the project root, nothing runs — and nothing errors loudly. Add a heartbeat job that pings a healthcheck endpoint so silence is detectable.
  • The 4.0.0 changelog tells you nothing. A one-line entry for a major version is a signal to read the diff, not the release notes. Check the constraint, the service definitions, and the task interface before upgrading anything you care about in production.
  • Timezone and DST. Expressions evaluate against PHP's configured timezone. Set it explicitly in php.ini or the front controller; 30 2 * * *-style jobs skip or double-fire on DST transitions.
  • Resource limits. CLI PHP limits apply — memory and max_execution_time. Anything heavy should be dispatched to a worker, not executed inline.
  • Debugging "it doesn't fire." Check registration first: debug:container --tag=<bundle task tag> shows whether the container actually sees your task. This is the single most likely 4.0.0 upgrade breakage. Then -vvv on schedule:run. Expression bugs (five fields vs. six, */5 vs. 5) are the next most common cause.
  • Extend by decoration, not patching. Decorate the runner command/service to add logging, metrics, or filtering. Editing vendor code guarantees pain on the next composer update.
  • Vendor risk is the real caveat. Single maintainer, tiny install base, and a multi-year gap followed by a bare-minimum "Symfony 7.x" bump — that's maintenance out of necessity, not a roadmap. Pin the exact version and budget for migration.
  • On Symfony 7, prefer the native scheduler. 4.0.0's entire raison d'être was "run on Symfony 7"; symfony/scheduler does that natively, is first-party, and is stable. Use this bundle to move an existing 3.x/6.x codebase forward, or as a stepping stone — not as the starting point for new work. Migration is mostly deleting the interface and adding an attribute: a mechanical afternoon, not a rewrite.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log