Laravel: Task Scheduling trong laravel

Hôm nay mình sẽ tổng hợp và giới thiệu cho các bạn một bài khá hay về 1 tính năng của Laravel. Đó là Task Scheduling (Lập lịch làm việc) có thể áp dụng vào việc thiết kế web đặt lịch, sự kiện hoặc các website có tính chất cần vận hành theo lịch định trước nào đó.

Giới thiệu về Task Scheduling trong laravel

Trước đây, có thể bạn đã từng tạo một Cron cho một tác vụ nào đó mà bạn muốn lập lịch để chạy trên server của bạn. Tuy nhiên, sẽ khá phiền phức nếu bạn muốn thay đổi hoặc thêm các schedules mới khi bạn phải SSH đến server của bạn để thực hiện việc này.

Lệnh scheduler của Laravel cho phép bạn xác định lịch trình của mình một cách dễ dàng ngay trong chính Laravel. Khi sử dụng scheduler, bạn chỉ cần duy nhất một Cron entry trên server của bạn. Lịch trình làm việc của bạn sẽ được định nghĩa trong phương thức schedule trên file app/Console/Kernel.php. Để giúp bạn bắt đầu, một ví dụ đơn giản được định nghĩa trong phương thức:

Starting The Scheduler

Khi sử dụng scheduler, bạn chỉ cần thêm 1 mục Cron sau vào server của bạn. Nếu bạn không biết làm thế nào để thêm các mục Cron vào server của mình, hãy xem xét sử dụng 1 service như mà có thể quản lý các mục Cron cho bạn:

* * * * * php /path/to/artisan schedule:run >> /dev/null 2>&1

Cron này sẽ gọi lệnh Laravel scheduler mỗi phút. Khi lệnh schedule:run được thực thi, Laravel sẽ tìm biểu thức scheduled task của bạn và chạy các task đến kỳ hạn.

Defining Schedules

Bạn có thể định nghĩa tất cả các scheduled task trong phương thức schedule của lớp App\Console\Kernel. Để bắt đầu, hãy xem ví dụ về lập lịch 1 task. Trong ví dụ này, chúng ta sẽ lạp 1 Closure để được gọi mỗi ngày vào lúc nửa đêm. Trong Closure, chúng ta sẽ thực hiện một truy vấn cơ sở dữ liệu để xóa bảng:

<?php

namespace App\Console;

use DB;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel
{
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        \App\Console\Commands\Inspire::class,
    ];

    /**
     * Define the application's command schedule.
     *
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
        $schedule->call(function () {
            DB::table('recent_users')->delete();
        })->daily();
    }
}

Ngoài việc lên kế hoạch cho Closure, bạn cũng có thể lập lịch các lên Artisan và các lệnh hệ điều hành. Ví dụ, bạn có thể sử dụng phương thức command để lập lịch cho 1 Artisan command sử dụng tên command hoặc class:

$schedule->command('emails:send --force')->daily();

$schedule->command(EmailsCommand::class, ['--force'])->daily();

Lệnh exec có thể ddwwocj sử dụng để đưa ra các lệnh cho hệ điều hành:

$schedule->exec('node /home/forge/script.js')->daily();

Schedule Frequency Options

Dĩ nhiên, có nhiều lựa chọn về thời gian để bạn có thể lập lịch cho task của mình:

Những phương thức này có thể được kết hợp với các ràng buộc bổ sung để tạo ra các lịch trình tinh vi hơn nữa mà chỉ chạy trong những ngày nhất định trong tuần. Ví dụ: để lập lịch một lệnh để chạy hàng tuần vào thứ hai:

// Run once per week on Monday at 1 PM...
$schedule->call(function () {
    //
})->weekly()->mondays()->at('13:00');

// Run hourly from 8 AM to 5 PM on weekdays...
$schedule->command('foo')
          ->weekdays()
          ->hourly()
          ->timezone('America/Chicago')
          ->between('8:00', '17:00');

Dưới đây là danh sách các ràng buộc thời gian bổ sung:

Between Time Constraints

Phương thức between có thể được sử dụng để hạn chế việc thực hiện một task dựa trên khoảng thời gian trong ngày:

$schedule->command('reminders:send')
                    ->hourly()
                    ->between('7:00', '22:00');

Tương tự, phương thức exceptBetween được sử dụng để loại trừ việc thực hiện một task trong một khoảng thời gian:

$schedule->command('reminders:send')
                    ->hourly()
                    ->unlessBetween('23:00', '4:00');

Preventing Task Overlaps

Theo mặc định, các task scheduled sẽ được chạy ngay cả khi instance trước đó của task vẫn đang chạy. Để ngăn chặn điều này, bạn có thể sử dụng phương thức withoutOverLapping

$schedule->command('emails:send')->withoutOverlapping();

Trong ví dụ này, câu lệnh Artisan emails:send sẽ được chạy mỗi phút nếu nó chưa chạy. Phương pháp withoutOverLapping đặc biệt hữu ích nếu bạn các task thay đổi đáng kể trong thời gian thực hiện của chúng, ngăn không cho bạn dự đoán chính xác thời gian của một task nhất định sẽ mất.

Maintenance Mode

scheduled task của Laravel sẽ không chạy khi Laravel đang ở vì họ không muốn công việc của bạn can thiệp vào bất kỳ bảo trì chưa hoàn thành nào bạn có thể thực hiện trên server của bạn, Tuy nhiên, nếu bạn muốn buộc một task chạy ngay cả trong chế độ bảo trì, bạn có thể sử dụng phương thức evenInMaintenanceMode:

$schedule->command('emails:send')->evenInMaintenanceMode();

Task Output

Laravel scheduler cung cấp một số phương thức thuận tiện để làm việc với output được tạo ra bởi các scheduled task. Đầu tiên, bằng cách sử dụng phương thức sendOutputTo, bạn có thể gửi output tới một file để kiểm duyệt sau:

$schedule->command('emails:send')
         ->daily()
         ->sendOutputTo($filePath);

Nếu bạn muốn nối output vào 1 file nhất định, bạn có thể sử dụng phương thức appendOutputTo:

$schedule->command('emails:send')
         ->daily()
         ->appendOutputTo($filePath);

Sử dụng phương thức emailOutputTo, bạn có thể gửi email output đến 1 địa chỉ email bạn chọn. Trước khi gửi email output của một task, bạn nên cấu hình các của Laravel:

$schedule->command('foo')
         ->daily()
         ->sendOutputTo($filePath)
         ->emailOutputTo('');

Task Hooks

Sử dụng phương thức beforeafter, bạn có thể chỉ định mã được thực hiện trước và sau khi công việc đã được lập lịch hoàn tất.

$schedule->command('emails:send')
         ->daily()
         ->before(function () {
             // Task is about to start...
         })
         ->after(function () {
             // Task is complete...
         });