Installation
composer require bkstg/schedule-bundle
Register the bundle in config/app.php under providers:
Backstage\ScheduleBundle\ScheduleServiceProvider::class,
Publish Configuration
php artisan vendor:publish --provider="Backstage\ScheduleBundle\ScheduleServiceProvider"
This creates config/schedule.php with default settings.
First Use Case: Basic Scheduling
Define a scheduled job in app/Console/Kernel.php:
protected function schedule(Schedule $schedule)
{
$schedule->command('emails:send')->dailyAt('10:00');
}
Register the Kernel in app/Console/Kernel.php:
protected $commands = [
\Backstage\ScheduleBundle\Console\ScheduleCommand::class,
];
Run the Scheduler
php artisan schedule:run
For production, set up a cron job (e.g., * * * * * cd /path-to-project && php artisan schedule:run >> /dev/null 2>&1).
Command-Based Scheduling
$schedule->command('backup:run')->hourly();
$schedule->command('report:generate {argument}')->daily()->withArguments(['--type=monthly']);
Closure-Based Jobs
$schedule->call(function () {
Log::info('Running custom scheduled task');
// Business logic here
})->everyFiveMinutes();
Event-Based Triggers
$schedule->job(new DeployPostHookJob)->after('deploy');
Environment-Specific Scheduling
when() to conditionally run jobs:
$schedule->command('cache:clear')->when(function () {
return app()->environment('production');
})->weekly();
Database Backend
SCHEDULE_RUNNER=database in .env and running:
php artisan schedule:install
scheduled_tasks table; verify with:
\Backstage\ScheduleBundle\Models\ScheduledTask::latest()->take(10)->get();
Custom Job Classes
\Backstage\ScheduleBundle\Jobs\Job for reusable logic:
namespace App\Jobs\Scheduled;
use Backstage\ScheduleBundle\Jobs\Job;
class CleanupJob extends Job
{
public function handle()
{
// Custom logic
}
}
$schedule->job(new \App\Jobs\Scheduled\CleanupJob)->daily();
Logging and Monitoring
app/Console/Kernel.php:
$schedule->command('log:clean')->daily()->onOneServer();
$schedule->call(function () {
\Log::debug('Scheduled task executed at: ' . now());
})->everyMinute();
Testing
$schedule = $this->app->make(\Backstage\ScheduleBundle\Schedule::class);
$schedule->shouldReceive('command')->once()->with('test:run');
Cron Misconfiguration
* * * * *). Without this, the scheduler won’t execute.crontab -l
Timezone Issues
config/schedule.php:
'timezone' => 'America/New_York',
\Carbon\Carbon::now()->timezone;
Database Locking
scheduled_tasks table has proper indexes:
Schema::table('scheduled_tasks', function (Blueprint $table) {
$table->index('due_at');
$table->index('status');
});
Overlapping Jobs
onOneServer() or withoutOverlapping():
$schedule->command('long-running:task')->everyThirtyMinutes()->onOneServer();
Check Last Run
scheduled_tasks table or log files for the last execution time:
SELECT * FROM scheduled_tasks ORDER BY id DESC LIMIT 1;
Force Run
php artisan schedule:run --force
Disable Jobs
SCHEDULE_ENABLED=false in .env.Log Output
php artisan schedule:run >> /var/log/schedule.log 2>&1
Custom Runners
\Backstage\ScheduleBundle\Runners\RunnerInterface for alternative runners (e.g., Redis):
namespace App\Runners;
use Backstage\ScheduleBundle\Runners\RunnerInterface;
class RedisRunner implements RunnerInterface
{
public function run()
{
// Custom Redis-based logic
}
}
ScheduleServiceProvider:
$this->app->bind(RunnerInterface::class, function () {
return new \App\Runners\RedisRunner();
});
Job Events
JobStarting, JobFinished) via Laravel events:
event(new \Backstage\ScheduleBundle\Events\JobStarting(
$job,
$schedule
));
Dynamic Scheduling
$schedules = \App\Models\DynamicSchedule::where('active', true)->get();
foreach ($schedules as $schedule) {
$this->schedule->command($schedule->command)
->{$schedule->frequency}()
->at($schedule->time);
}
Rate Limiting
$schedule->command('api:rate-limit-check')->everyFiveMinutes()
->limit(1)->withoutOverlapping();
How can I help you explore Laravel packages today?