mcpField
L
laravel-task-scheduling

laravel-task-scheduling

github.com/iserter開発4886 回インストール29 週間前に更新
リポジトリ

laravel-task-scheduling について

laravel-task-scheduling は、Laravel でスケジュールタスクを定義・実行するためのリファレンススキルです。コマンド、ジョブ、クロージャ、シェルコマンドに加え、実行条件、監視フック、出力管理を扱います。

主な機能

  1. 01コマンド、ジョブ、クロージャ、シェルコマンドのスケジュール
  2. 02時間と環境の実行条件設定
  3. 03タスクの重複実行防止
  4. 04単一サーバーでの実行制限
  5. 05フックとヘルスチェック ping の設定
  6. 06タスク出力の管理

活用シーン

  1. 01定期的なレポート生成
  2. 02指定した曜日や環境へのタスク制限
  3. 03複数サーバーでのタスク重複実行防止
  4. 04タスク出力の記録やメール送信

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/iserter/laravel-claude-agents --skill laravel-task-scheduling

インストール後、Claude Code で/laravel-task-schedulingを入力して読み込まれたか確認します。

詳細

タスクのスケジューリング

routes/console.php でのスケジュールの定義

<?php

use Illuminate\Support\Facades\Schedule;

// Schedule an Artisan command
Schedule::command('reports:generate')->daily();

// Schedule a job
Schedule::job(new ProcessDailyMetrics)->dailyAt('01:00');

// Schedule a closure
Schedule::call(function () {
    Cache::flush();
})->weekly();

// Schedule a shell command
Schedule::exec('node /home/forge/script.js')->daily();

スケジュールの種類

Artisan コマンド
// With arguments and options
Schedule::command('emails:send', ['--force'])->daily();
Schedule::command('reports:generate --type=weekly')->sundays();

// Using command class
Schedule::command(SendEmailsCommand::class, ['--force'])->daily();
キューに登録されたジョブ
// Dispatch a job
Schedule::job(new CleanUpExpiredTokens)->daily();

// Dispatch to a specific queue and connection
Schedule::job(new ProcessAnalytics, 'analytics', 'redis')->hourly();
クロージャ
Schedule::call(function () {
    DB::table('sessions')
        ->where('last_activity', '<', now()->subHours(24))
        ->delete();
})->hourly()->description('Clean expired sessions');
シェルコマンド
Schedule::exec('pg_dump mydb > /backups/db.sql')->daily();

実行頻度のメソッド

// Common frequencies
Schedule::command('task')->everyMinute();
Schedule::command('task')->everyTwoMinutes();
Schedule::command('task')->everyFiveMinutes();
Schedule::command('task')->everyTenMinutes();
Schedule::command('task')->everyFifteenMinutes();
Schedule::command('task')->everyThirtyMinutes();
Schedule::command('task')->hourly();
Schedule::command('task')->hourlyAt(17);            // At :17 past each hour
Schedule::command('task')->everyOddHour();
Schedule::command('task')->everyTwoHours();
Schedule::command('task')->everyThreeHours();
Schedule::command('task')->everyFourHours();
Schedule::command('task')->everySixHours();
Schedule::command('task')->daily();
Schedule::command('task')->dailyAt('13:00');
Schedule::command('task')->twiceDaily(1, 13);       // At 1:00 and 13:00
Schedule::command('task')->twiceDailyAt(1, 13, 15); // At 1:15 and 13:15
Schedule::command('task')->weekly();
Schedule::command('task')->weeklyOn(1, '8:00');     // Monday at 8:00
Schedule::command('task')->monthly();
Schedule::command('task')->monthlyOn(4, '15:00');   // 4th of month at 15:00
Schedule::command('task')->twiceMonthly(1, 16);     // 1st and 16th
Schedule::command('task')->lastDayOfMonth('17:00');
Schedule::command('task')->quarterly();
Schedule::command('task')->quarterlyOn(4, '14:00'); // 4th day of quarter
Schedule::command('task')->yearly();
Schedule::command('task')->yearlyOn(6, 1, '17:00'); // June 1st at 17:00

// Cron expression
Schedule::command('task')->cron('0 */6 * * *');     // Every 6 hours

制約

時刻の制約
// Between specific times
Schedule::command('task')->hourly()->between('8:00', '17:00');

// Outside specific times
Schedule::command('task')->hourly()->unlessBetween('23:00', '04:00');
曜日の制約
Schedule::command('task')->daily()->weekdays();
Schedule::command('task')->daily()->weekends();
Schedule::command('task')->daily()->sundays();
Schedule::command('task')->daily()->mondays();
Schedule::command('task')->daily()->tuesdays();
Schedule::command('task')->daily()->wednesdays();
Schedule::command('task')->daily()->thursdays();
Schedule::command('task')->daily()->fridays();
Schedule::command('task')->daily()->saturdays();
Schedule::command('task')->daily()->days([0, 3]);   // Sunday and Wednesday
環境の制約
// ✅ Only run in production
Schedule::command('analytics:process')->daily()->environments(['production']);

// ✅ Skip in testing
Schedule::command('reports:send')->daily()->environments(['production', 'staging']);
条件付きの制約
// Run only when condition is true
Schedule::command('emails:send')
    ->daily()
    ->when(function () {
        return config('services.email.enabled');
    });

// Skip when condition is true
Schedule::command('maintenance:run')
    ->daily()
    ->skip(function () {
        return app()->isDownForMaintenance();
    });

重複実行の防止

// ✅ Prevent overlapping: skip if previous instance is still running
Schedule::command('reports:generate')
    ->hourly()
    ->withoutOverlapping();

// With custom expiration (default is 24 hours)
Schedule::command('reports:generate')
    ->hourly()
    ->withoutOverlapping(expiresAt: 60); // Lock expires after 60 minutes

分散環境(onOneServer)

// ✅ Only run on a single server (requires memcached, redis, or database cache driver)
Schedule::command('reports:generate')
    ->daily()
    ->onOneServer();

// Combine with overlap prevention
Schedule::command('analytics:process')
    ->hourly()
    ->onOneServer()
    ->withoutOverlapping();

スケジュールグループ

// ✅ Apply shared configuration to multiple scheduled tasks
Schedule::command('analytics:aggregate')
    ->daily()
    ->onOneServer()
    ->withoutOverlapping()
    ->emailOutputOnFailure('[email protected]');

Schedule::command('analytics:cleanup')
    ->daily()
    ->onOneServer()
    ->withoutOverlapping()
    ->emailOutputOnFailure('[email protected]');

// Group with shared config (if using a helper)
collect([
    'analytics:aggregate',
    'analytics:cleanup',
    'analytics:summary',
])->each(function ($command) {
    Schedule::command($command)
        ->daily()
        ->onOneServer()
        ->withoutOverlapping()
        ->emailOutputOnFailure('[email protected]');
});

フック(before、after、onSuccess、onFailure、ping)

Schedule::command('reports:generate')
    ->daily()
    ->before(function () {
        Log::info('Starting report generation...');
    })
    ->after(function () {
        Log::info('Report generation complete.');
    })
    ->onSuccess(function () {
        Notification::route('slack', '#ops')
            ->notify(new ScheduledTaskSucceeded('reports:generate'));
    })
    ->onFailure(function () {
        Notification::route('slack', '#alerts')
            ->notify(new ScheduledTaskFailed('reports:generate'));
    });
URL への ping(ヘルスチェック)
// Ping a URL before and after
Schedule::command('reports:generate')
    ->daily()
    ->pingBefore('https://health.example.com/start')
    ->thenPing('https://health.example.com/end');

// Ping only on success or failure
Schedule::command('reports:generate')
    ->daily()
    ->pingOnSuccess('https://health.example.com/success')
    ->pingOnFailure('https://health.example.com/failure');

// Works with services like Oh Dear, Healthchecks.io, Cronitor
Schedule::command('reports:generate')
    ->daily()
    ->pingBefore('https://hc-ping.com/your-uuid/start')
    ->thenPing('https://hc-ping.com/your-uuid');
メール出力
Schedule::command('reports:generate')
    ->daily()
    ->sendOutputTo('/var/log/reports.log')
    ->emailOutputTo('[email protected]');

// Only email on failure
Schedule::command('reports:generate')
    ->daily()
    ->emailOutputOnFailure('[email protected]');

サブ分単位のスケジューリング

// ✅ Run every second (Laravel 11+)
Schedule::call(function () {
    // Process real-time data
    MetricsCollector::capture();
})->everySecond();

// Every 10 seconds
Schedule::call(function () {
    QueueMonitor::check();
})->everyTenSeconds();

// Every 15 seconds
Schedule::command('queue:monitor')->everyFifteenSeconds();

// Every 20 / 30 seconds
Schedule::call(fn () => HealthCheck::ping())->everyTwentySeconds();
Schedule::call(fn () => HealthCheck::ping())->everyThirtySeconds();

出力の管理

// Send output to a file
Schedule::command('reports:generate')
    ->daily()
    ->sendOutputTo('/var/log/schedule/reports.log');

// Append output to a file
Schedule::command('reports:generate')
    ->daily()
    ->appendOutputTo('/var/log/schedule/reports.log');

// Write output to storage
Schedule::command('reports:generate')
    ->daily()
    ->appendOutputTo(storage_path('logs/reports.log'));

// Discard output
Schedule::command('reports:generate')
    ->daily()
    ->sendOutputTo('/dev/null');

メンテナンスモードでの実行

// ✅ Run even during maintenance mode
Schedule::command('queue:work --stop-when-empty')
    ->everyMinute()
    ->evenInMaintenanceMode();

スケジューラーの実行

# Add to crontab (required for scheduling to work)
* * * * * cd /path-to-project && php artisan schedule:run >> /dev/null 2>&1

# For sub-minute scheduling
* * * * * cd /path-to-project && php artisan schedule:run >> /dev/null 2>&1

# Local development - runs scheduler in the foreground
php artisan schedule:work

# List all scheduled tasks
php artisan schedule:list

# Test a specific scheduled command
php artisan schedule:test

ベストプラクティス

// ✅ Always give scheduled closures a description
Schedule::call(function () {
    // ...
})->daily()->description('Clean expired sessions');

// ✅ Use onOneServer for distributed deployments
Schedule::command('sitemap:generate')->daily()->onOneServer();

// ✅ Use withoutOverlapping for long-running tasks
Schedule::command('import:products')->hourly()->withoutOverlapping();

// ✅ Monitor with hooks and pings
Schedule::command('billing:charge')
    ->daily()
    ->onSuccess(fn () => Log::info('Billing completed'))
    ->onFailure(fn () => Log::critical('Billing failed'))
    ->pingOnFailure('https://alerts.example.com/billing');

// ❌ Don't schedule heavy work without overlap prevention
Schedule::command('heavy:task')->everyMinute(); // Could stack up

// ❌ Don't forget to set the timezone if needed
Schedule::command('report:daily')
    ->dailyAt('09:00')
    ->timezone('America/New_York');

チェックリスト

  • スケジュールが routes/console.php で定義されている
  • 実行頻度のメソッドが実際の業務要件に合っている
  • 実行時間の長いタスクに withoutOverlapping() を使用している
  • マルチサーバー構成で onOneServer() を使用している
  • 適切な場合に環境制約を適用している
  • 監視用にフック(before、after、onSuccess、onFailure)を設定している
  • 重要なタスクにヘルスチェック用の ping を設定している
  • デバッグ用に出力をログに記録するか、メールで送信している
  • schedule:list で確認できるよう、クロージャに説明を付けている
  • schedule:run を実行する crontab エントリをサーバーに追加している
  • 時刻が重要な場合はタイムゾーンを明示的に設定している
  • 必須タスクに evenInMaintenanceMode() を使用している

コメント