Laravel Task Scheduling: Panduan Lengkap

    Dalam artikel ini kita akan belajar tentang schedule / penjadwalan pada laravel, saya akan berusaha sebisa mungkin untuk menjelaskan case ini agar Anda dapat mengerti dengan baik.

    Irsyad A. Panjaitan

    10 min read·22 Nov 2022

    Laravel Task Scheduling: Panduan Lengkap

    Dalam artikel ini kita akan belajar tentang schedule / penjadwalan pada laravel, saya akan berusaha sebisa mungkin untuk menjelaskan case ini agar Anda dapat mengerti dengan baik.

    Instal Laravel

    Kita akan mulai dari awal disini, sehingga kita akan lakukan dari yang pertama kali yaitu instalasi laravel nya, buka terminal Anda dan jalankan perintah berikut.

    laravel new schedule
    

    Jika Anda tidak bisa instal laravel menggunakan laravel cli, Anda bisa menggunakan composer seperti berikut ini:

    composer create-project laravel/laravel schedule
    

    Jika sudah, sekarang kita akan menjalankan perintah migrate, namun sebelum itu, pastikan dulu Anda sudah mempunyai database dan kemudian sesuaikan pada file .env.

    DB_CONNECTION=mysql
    DB_HOST=127.0.0.1
    DB_PORT=3306
    DB_DATABASE=schedule
    DB_USERNAME=root
    DB_PASSWORD=
    

    Setelah itu, mari kita lakukan migrate dengan menjalan perintah berikut.

    php artisan migrate
    

    Factories dan Seeders

    Oia, sepertinya kita butuh data untuk itu. Oleh karena itu, mari kita lakukan itu dengan factory. By default, laravel akan membawa yang factory untuk User yang itu bisa dilihat di database/factories/UserFactory.php, dan jika kita buka file nya kurang lebih akan seperti ini:

    <?php
    
    namespace Database\Factories;
    
    use Illuminate\Database\Eloquent\Factories\Factory;
    use Illuminate\Support\Str;
    
    /**
     * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
     */
    class UserFactory extends Factory
    {
        public function definition(): array
        {
            return [
                'name' => fake()->name(),
                'email' => fake()->unique()->safeEmail(),
                'email_verified_at' => now(),
                'password' => bcrypt('password'),
                'remember_token' => Str::random(10),
            ];
        }
    
        public function unverified(): static
        {
            return $this->state(fn (array $attributes) => [
                'email_verified_at' => null,
            ]);
        }
    }
    

    Sebelum kita menjalankan nya, saya akan modifikasi dulu terlebih dahulu untuk field created_at and updated_at nya jadi random.

    public function definition(): array
    {
        return [
            'name' => fake()->name(),
            'email' => fake()->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password' => bcrypt('password'),
            'remember_token' => Str::random(10),
            'created_at' => $createdAt = collect([
                now()->subMonth(2),
                now()->subMonth(1),
                now()->subWeek(2),
                now()->subWeek(1),
                now()
            ])->random(1)->first(),
            'updated_at' => $createdAt,
        ];
    }
    

    Setalah itu, silakan hilangkan komentar factory pada file database/seeders/DatabaseSeeder.php.

    <?php
    
    namespace Database\Seeders;
    
    use Illuminate\Database\Seeder;
    
    class DatabaseSeeder extends Seeder
    {
        public function run(): void
        {
            \App\Models\User::factory(30)->create();
    
            // \App\Models\User::factory()->create([
            //     'name' => 'Test User',
            //     'email' => '[email protected]',
            // ]);
        }
    }
    

    Jika sudah, sekarang kita buka terminal kembali, dan sekarang kita akan menjalankan perintah untuk seeder nya seperti:

    php artisan migrate:fresh --seed
    

    Dan jika sudah, harusnya pada tabel users akan sudah terbuat user sampai 30 dengan masing-masing created_at dan updated_at berbeda. Untuk melihatnya Anda bisa langsung pergi ke tinker dengan menjalankan perintah berikut:

    php artisan tinker
    

    Dan kemudian pluck created_at nya seperti:

    Psy Shell v0.11.9 (PHP 8.1.11 — cli) by Justin Hileman
    > App\Models\User::pluck('created_at');
    

    Dan harusnya output yang keluar pastinya seperti berikut:

    = Illuminate\Support\Collection {#4639
        all: [
          Illuminate\Support\Carbon @1669116509 {#4636
            date: 2022-11-22 11:28:29.0 UTC (+00:00),
          },
          Illuminate\Support\Carbon @1667906909 {#4635
            date: 2022-11-08 11:28:29.0 UTC (+00:00),
          },
          Illuminate\Support\Carbon @1668511709 {#4634
            date: 2022-11-15 11:28:29.0 UTC (+00:00),
    

    Perhatikan pada date nya, mereka sudah berbeda-beda jika Anda perhatikan baik-baik. Tetapi tidak cukup hanya itu, disini kita akan buat sampel bagaimana membuat schedule untuk menghapus pengguna yang tidak melakukan verifikasi email yang mana telah mendaftar 1/2 bulan lalu.

    Make oleh karena itu, kita akan langsung directly dari tinker melakukan factory nya dengan state unverified yant telah di sediakan oleh laravel yaitu.

    public function unverified(): static
    {
        return $this->state(fn (array $attributes) => [
            'email_verified_at' => null,
        ]);
    }
    

    Nah untuk itu, buka silakan masuk lagi ke tinker dengan menjalankan php artisan tinker, setelah itu lakukan perintah berikut.

    > App\Models\User::factory(30)->unverified()->create();
    

    Setelah itu, maka kita akan mempunyai total 60 pengguna, 30 yang verified dan 30 lagi tidak verified. Verified akan di lihat dari null nya field dari email_verified_at nya.

    Sehingga sekarang, jika kita buat di tinker perintah:

    > App\Models\User::whereNull('email_verified_at')->count();
    = 30
    

    Model Pruning

    Yang paling pertama, mari kita buat pruning untuk tabel user ini, jadi kita akan melakukan penghapusan secara otomatis jika user tidak melakukan verifikasi selama 1/2 bulan misalnya.

    Pertama sekali, untuk menggunakan pruning ini, kita harus memasukkan trait yang namanya Prunable. Untuk itu, buka model User.php dan tambahkan dia seperti:

    ...
    use Illuminate\Database\Eloquent\Prunable;
    
    class User extends Authenticatable
    {
        use HasApiTokens, HasFactory, Notifiable, Prunable;
    

    Setelah itu, kita akan menambahkan metode baru pada model ini dengan nama prunable seperti:

    class User extends Authenticatable
    {
        use HasApiTokens, HasFactory, Notifiable, Prunable;
    
        protected $fillable = ['name', 'email', 'password'];
    
        protected $hidden = ['password', 'remember_token'];
    
        protected $casts = ['email_verified_at' => 'datetime'];
    
        public function prunable()
        {
            return static::whereNull('email_verified_at')
                    ->where('created_at', '<=', now()
                    ->subMonth());
        }
    }
    

    Isi dari query tersebut adalah untuk memastikan kita akan mengapus user yang tidak verifikasi selama 1 bulan terakhir. Dan sekarang, Anda bisa buka file app/Console/Kernel.php untuk memasukkan schedule dengan menambahkan nya tepat di dalam methode schedule seperti:

    class Kernel extends ConsoleKernel
    {
        protected function schedule(Schedule $schedule): void
        {
            $schedule->command('model:prune')->weekly();
            // $schedule->command('inspire')->hourly();
        }
        
        protected function commands(): void{...}
    }
    

    Setelah itu, kita akan memeriksa apakah schedule itu sudah masuk atau belum di dalam tasklist nya dengan menjalankan perintah berikut:

    php artisan schedule:list
    

    Maka harusnya, output yang dihasilkan kurang lebih akan seperti ini:

    0 0 * * 0  php artisan model:prune ........................ Next Due: 4 days from now
    

    Nah, untuk menjalankannya, kita menggunakan perintah schedule:run seperti:

    php artisan schedule:run
    

    Dan harusnya, yang muncul adalah seperti:

    INFO  No scheduled commands are ready to run.
    

    Arti dari info tersebut adalah tidak ada belum schedule yang mau di jalankan. Masuk akal memang, karena jika kita perhatikan tadi pada list schedule nya, itu akan di jalankan 4 hari lagi dari sekarang. Maka untuk itu, kita bisa modifikasi dia dengan cara mengganti weekly() dengan everyMinute() untuk contoh ini saja.

    class Kernel extends ConsoleKernel
    {
        protected function schedule(Schedule $schedule): void
        {
            $schedule->command('model:prune')->everyMinute();
            // $schedule->command('inspire')->hourly();
        }
        
        protected function commands(): void{...}
    }
    

    Pastinya jika ini di production, harusnya untuk case ini kita bisa jalankan sekali seminggu saja, namun untuk test saja, kita akan buat dia everyMinute agar kita yang mana telah di hapusnya.

    Menjalankan Schedule di Lokal

    Sekarang, untuk menjalankan schedule tersebut di background, kita bisa menggunakan perintah schedule:work seperti berikut:

    php artisan schedule:work
    

    Sekarang jika Anda lihat outputnya setelah 1 menit akan seperti berikut:

     INFO  Running schedule tasks every minute.
    
      2022-11-22 11:55:00 Running ['artisan' model:prune] ............. 234ms DONE
      ⇂ '/opt/homebrew/Cellar/php/8.1.11/bin/php' 'artisan' model:prune > '/dev/null' 2>&1
    

    Dan perintah tersebut akan terus jalan setiap 1 menit. Maka nya sekarang di saya sudah 2 kali:

      INFO  Running schedule tasks every minute.
    
      2022-11-22 11:55:00 Running ['artisan' model:prune] ............. 234ms DONE
      ⇂ '/opt/homebrew/Cellar/php/8.1.11/bin/php' 'artisan' model:prune > '/dev/null' 2>&1
    
      2022-11-22 11:56:00 Running ['artisan' model:prune] ............. 183ms DONE
      ⇂ '/opt/homebrew/Cellar/php/8.1.11/bin/php' 'artisan' model:prune > '/dev/null' 2>&1
    

    Anda bisa tekan ctrl+c untuk menghentikan perintahnya. Dan sekarang, mari kita kembali ke tinker untuk menjalakan perintah yang tadi yaitu melihat jumlah yang tidak verifikasi tinggal berapa:

    php artisan tinker
    

    Dan jalankan query seperti yang tadi:

    User::whereNull('email_verified_at')->count()
    = 20
    

    Karena tadi kita buat random, maka hasilnya harusnya beda dengan Anda, kalau di saya hasilnya 20 seperti yang Anda lihat di atas, mungkin Anda bisa saja entah berapa, karena tergantung field created_at yang telah dibuat factory tadi.

    Pastinya Anda akan menggunakan php artisan schedule:run pada saat production mode.

    Dalam artikel ini, kita hanya memperaktekkan everyMinute, namun sebenarnya itu terserah Anda. Seperti yang saya bilang, harusnya untuk case ini, biasa saya hanya melakukan weekly dari pada everyMinute, karena jika ini terus-terus di jalankan, beban pada server akan semakin besar.

    Daftar Penjadwalan

    Berikut ini adalah metode-metode yang bisa Anda gunakan untuk pilihan waktu menjalankannya.

    Metode Deskripsi
    ->cron('* * * * *'); Menjalankan schedule dengan pilihan waktu sendiri
    ->everyMinute(); Jalankan tugas setiap menit
    ->everyTwoMinutes(); Jalankan tugas setiap dua menit
    ->everyThreeMinutes(); Jalankan tugas setiap tiga menit
    ->everyFourMinutes(); Jalankan tugas setiap empat menit
    ->everyFiveMinutes(); Jalankan tugas setiap lima menit
    ->everyTenMinutes(); Jalankan tugas setiap sepuluh menit
    ->everyFifteenMinutes(); Jalankan tugas setiap lima belas menit
    ->everyThirtyMinutes(); Jalankan tugas setiap tiga puluh menit
    ->hourly(); Jalankan tugas setiap jam
    ->hourlyAt(17); Jalankan tugas setiap jam pada 17 menit lewat satu jam
    ->everyOddHour(); Jalankan tugas setiap jam ganjil
    ->everyTwoHours(); Jalankan tugas setiap dua jam
    ->everyThreeHours(); Jalankan tugas setiap tiga jam
    ->everyFourHours(); Jalankan tugas setiap empat jam
    ->everySixHours(); Jalankan tugas setiap enam jam
    ->daily(); Jalankan tugas setiap hari pada tengah malam
    ->dailyAt('13:00'); Jalankan tugas setiap hari pukul 13:00
    ->twiceDaily(1, 13); Jalankan tugas setiap hari pada pukul 1:00 dan 13:00
    ->twiceDailyAt(1, 13, 15); Jalankan tugas setiap hari pada 1:15 & 13:15
    ->weekly(); Jalankan tugas setiap hari Minggu pukul 00:00
    ->weeklyOn(1, '8:00'); Jalankan tugas setiap minggu pada hari Senin pukul 8:00
    ->monthly(); Jalankan tugas pada hari pertama setiap bulan pukul 00:00
    ->monthlyOn(4, '15:00'); Jalankan tugas setiap bulan pada tanggal 4 pukul 15:00
    ->twiceMonthly(1, 16, '13:00'); Jalankan tugas setiap bulan pada tanggal 1 dan 16 pukul 13:00
    ->lastDayOfMonth('15:00'); Jalankan tugas pada hari terakhir bulan itu pukul 15:00
    ->quarterly(); Jalankan tugas pada hari pertama setiap kuartal pukul 00:00
    ->quarterlyOn(4, '14:00'); Jalankan tugas setiap kuartal pada tanggal 4 pukul 14:00
    ->yearly(); Jalankan tugas pada hari pertama setiap tahun pukul 00:00
    ->yearlyOn(6, 1, '17:00'); Jalankan tugas setiap tahun pada tanggal 1 Juni pukul 17:00
    ->timezone('America/New_York'); Tetapkan zona waktu untuk tugas tersebut

    Metode di atas itu semua dari dokumentasi yang sudah saya artikan ke bahasa.

    Hati - Hati

    Pastikan untuk command schedule nya sudah cocok dengan crontab nya. Jika kalian menggunakan model 1/2 jam seperti misalnya menjalankan 04:30, maka pastikan Anda juga sudah yakin bahwa crontab nya jalan setiap 30 menit.

    $schedule->command('backup:run --only-db')->daily()->at('04.30');
    

    Karena jika crontab jalan setiap 1 jam, maka schedule di atas tidak akan pernah jalan. Jadi pastikan Anda telah membuat sinkronisasi yang pasti antara crontab dan juga perintah schedule nya.

    Penting Untuk Dipahami

    Bayangkan jika kalian mempunyai beberapa tugas schedule, yang dari salah satunya itu mempunyai tugas yang sangat banyak dan lama.

    By default, schedule akan menjalankan tergantung letak dari tugas yang di definisikan. Jadi jika kita punya tugas A,B,C dalam satu waktu, maka dia akan di order dari yang pertama.

    Bayangkan jika B mempunyai tugas yang sangat lama, maka C akan terus menggu hingga B selesai, nah untuk itu, kita bisa menambahkan metode runInBackground setelah waktu yang di tentukan seperti:

    $schedule->command('analytics:report')
             ->daily()
             ->runInBackground();
    

    Dengan begitu, C tidak akan menunggu B kelar, baru jalan.

    Produksi

    Jika kalian menggunakan Laravel forge, maka hal tersebut akan sangat mudah untuk di lakukan pada menu scheduler.

    Laravel forge

    Dan jika kalian tidak menggunakan Laravel forge, harusnya kalian bisa menggunakan Crontab untuk menjalankan sechedule itu.

    Semoga artikel ini dapat bermanfaat untuk kita semua ya. Jika ada salah kata saya mohon maaf. Saya Irsyad, dan sampai jumpa lagi.

    Karteil
    Destinasi Utama Belajar Online dengan Format Tulisan yang Elegan
    Kunjungi Sekarang

    Irsyad A. Panjaitan

    Let's start living like no one can help us in any event, so that when we are helped in certain times, it becomes a plus in itself.

    Follow me on
    Support me
    SaweriaGithub

    Newsletter

    Bergabunglah dengan 23.000+ lainnya dan jangan pernah ketinggalan screencast, tips, tutorial, dan lainnya.