Laravelスケジューラーとは
Laravelのスケジューラーは、Linuxのcrontabの代わりにPHPコードでジョブのスケジュールを管理できる仕組みです。
crontabは設定が分散しやすく、バージョン管理もできません。Laravelのスケジューラーを使うと、全てのスケジュール定義をコードベースに集約できます。
# サーバーのcrontabに追加するのはこの1行だけ
* * * * * cd /path/to/project && php artisan schedule:run >> /dev/null 2>&1
スケジューラーの定義場所
Laravel 11では routes/console.php または bootstrap/app.php でスケジュールを定義します。
// bootstrap/app.php
use Illuminate\Console\Scheduling\Schedule;
->withSchedule(function (Schedule $schedule) {
$schedule->command('app:send-daily-report')->dailyAt('09:00');
$schedule->command('app:cleanup-old-records')->weekly()->mondays()->at('03:00');
$schedule->job(new SendNewsletterJob)->monthly()->at('10:00');
})
Artisanコマンドの作成
php artisan make:command SendDailyReport
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Services\ReportService;
class SendDailyReport extends Command
{
protected $signature = 'app:send-daily-report
{--date= : レポート対象日(YYYY-MM-DD)}
{--dry-run : 実際には送信しない}';
protected $description = '日次レポートをメールで送信する';
public function __construct(
private readonly ReportService $reportService,
) {
parent::__construct();
}
public function handle(): int
{
$date = $this->option('date')
? now()->parse($this->option('date'))
: now()->yesterday();
$this->info("レポート生成: {$date->format('Y-m-d')}");
$report = $this->reportService->generate($date);
if ($this->option('dry-run')) {
$this->table(
['指標', '値'],
collect($report->summary)->map(fn($v, $k) => [$k, $v])->values()->toArray()
);
$this->warn('Dry-runモード: メールは送信しません');
return self::SUCCESS;
}
$this->reportService->sendEmail($report);
$this->info("送信完了: {$report->recipients->count()}名");
return self::SUCCESS;
}
}
スケジュール頻度の指定
$schedule->command('app:task')
->everyMinute() // 毎分
->everyFiveMinutes() // 5分ごと
->everyFifteenMinutes() // 15分ごと
->hourly() // 毎時
->hourlyAt(30) // 毎時30分
->daily() // 毎日(0:00)
->dailyAt('09:00') // 毎日9時
->twiceDaily(8, 20) // 8時と20時
->weekly() // 毎週(日曜0:00)
->weeklyOn(1, '08:00') // 毎週月曜8時
->monthly() // 毎月1日0:00
->monthlyOn(15, '09:00') // 毎月15日9時
->quarterly() // 四半期ごと
->yearly() // 毎年1月1日
->cron('0 9 * * 1-5'); // 平日9時(カスタムcron式)
実践的なスケジュール設定例
->withSchedule(function (Schedule $schedule) {
// EC価格収集:平日の業務時間帯に1時間ごと
$schedule->command('ec:collect-prices')
->hourly()
->weekdays()
->between('08:00', '20:00')
->withoutOverlapping(30) // 30分以内に前の実行が完了しない場合はスキップ
->onFailure(function () {
// Slackに通知
});
// 日次レポート:毎朝9時
$schedule->command('app:send-daily-report')
->dailyAt('09:00')
->timezone('Asia/Tokyo')
->onSuccess(fn() => Log::info('日次レポート送信完了'))
->onFailure(fn() => Log::error('日次レポート送信失敗'));
// データベースバックアップ:毎晩2時
$schedule->command('backup:run --only-db')
->dailyAt('02:00')
->onOneServer() // 複数サーバーでも1台だけ実行
->runInBackground();
// 古いレコードの削除:毎週日曜3時
$schedule->command('app:prune-old-data --days=365')
->weekly()->sundays()->at('03:00');
// キャッシュウォームアップ:5分ごと
$schedule->call(function () {
cache()->remember('global.stats', 300, fn() => computeStats());
})->everyFiveMinutes();
})
withoutOverlapping——重複実行を防ぐ
時間のかかるジョブが次の実行時刻までに終わらない場合、withoutOverlapping で重複を防げます。
$schedule->command('app:heavy-batch')
->everyFifteenMinutes()
->withoutOverlapping(60); // 前のジョブが60分以内に終わらなければスキップ
onOneServer——複数サーバー環境での排他制御
// Redisなどのキャッシュドライバーが必要
$schedule->command('app:monthly-report')
->monthly()
->onOneServer();
スケジュールのテスト
# スケジュール一覧を確認
php artisan schedule:list
# 特定のコマンドをすぐに実行(スケジュールなしで)
php artisan app:send-daily-report --date=2024-01-15
# ドライランで確認
php artisan app:send-daily-report --dry-run
# スケジューラーをローカルで連続実行
php artisan schedule:work
まとめ
Laravelのスケジューラーはcrontabを1行に集約し、全てのスケジュール定義をコードで管理できます。withoutOverlapping・onOneServer・失敗時コールバックなどを組み合わせることで、本番運用に耐える定期バッチ処理を実装できます。弊社では価格収集・レポート生成・データ同期などの定期処理をスケジューラーで管理しています。
LaravelによるWebシステム開発のご相談はお気軽にどうぞ。