Cara Membuat Scheduler dan Job Queues di Laravel
Laravel menyediakan fasilitas untuk beberapa arsitektur dan struktur aplikasi yang kurang umum maka kita akan bahasa salah satunya yaitu Jobs dan Queue. Dalam tulisan ini kita akan membahas salah satu fitur Laravel untuk mengimplementasikan sebuah data antrian, proses pekerjaan yang mengantri, event(acara), dan publishing(penerbitan) WebSocket dan juga akan membahas Laravel penjadwal, yang membuat jadwal cron.
Untuk memahami apa itu Queue(antrian), pikirkan saja gagasan “mengantri” dalam antrian di bank. Bahkan jika ada beberapa baris—antrian—hanya satu orang yang dilayani pada satu waktu dari setiap antrian, dan setiap orang pada akhirnya akan mencapai bagian depan loket untuk dilayani.Di beberapa bank, ini adalah jenis kebijakan first-in-first-out FIFO yang ketat, tetapi di bank lain, tidak ada jaminan pasti bahwa seseorang tidak akan memotong antrian anda di beberapa titik.
Pada dasarnya, seseorang dapat ditambahkan ke antrian, dihapus dari antrian sebelum waktunya, atau berhasil “diproses” dan kemudian dihapus. Seseorang bahkan mungkin mencapai bagian depan antrian, tidak dapat dilayani dengan benar, kembali ke antrian untuk sementara waktu, dan kemudian diproses lagi.
Antrian dalam pemrograman sangat mirip. Aplikasi anda menambahkan “pekerjaan” ke antrian, yang merupakan sebuah potongan pengkode yang memberi tahu aplikasi cara melakukan perilaku tertentu Kemudian beberapa struktur aplikasi terpisah lainnya, biasanya Job Worker(pekerja antrian) mengambil tanggung jawab untuk pekerjaan dari antrian satu per satu dan melakukan tugas sesuai perintah. Pekerja antrian dapat menghapus pekerjaan, mengembalikannya ke antrian dengan penundaan, atau menandainya sebagai berhasil diproses.
Laravel memudahkan untuk layanan fitur antrian bisa menggunakan Redis, beanstalkd, Amazon Simple Queue Service (SQS), atau tabel database kalian juga dapat memilih driver sinkronisasi agar pekerjaan berjalan langsung di aplikasi anda tanpa benar-benar diantrekan.
di studicase ini saya menggunakan sebuah project cara memebuat api di laravel yang digunakan seblumnya di postingan Tutorial Laravel: Membuat REST API CRUD dengan Laravel serta JWT
Menyiapkan Database
Membuat Migration
disini menggunakan migrate laravel untuk keperluan membuat table dan kolom di database, berikut ini kode dan artisan command yang digunakan:
menjalankan generator migrate laravel
php artisan make:migration task
php artisan make:migration task_scheduler
isi kode migrate yang digunakan untuk task
:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class Task extends Migration
{
public function up()
{
Schema::create('task', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('description');
$table->integer('user_id');
$table->enum('status', ['WAIT', 'DONE'])->default('WAIT');
$table->timestamps();
});
}
public function down()
{
Schema::drop('task');
}
}
isi kode migrate yang digunakan untuk task_scheduler
:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class TaskScheduler extends Migration
{
public function up()
{
Schema::create('task_scheduler', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('days');
$table->time('time');
$table->string('description');
$table->integer('user_id');
$table->text('log_executed');
$table->timestamps();
});
}
public function down()
{
Schema::drop('task_scheduler');
}
}
Penjelasan :
Schema::create('task_scheduler', function (Blueprint $table)
: membuat sebuah tabel baru ketika command migrate dijalankan$table->id();
: membuat kolom id dengan auto increment$table->string('name');
: kolom name dengan tipeVARCHAR
dan akan menciptakan sebuah tipe text di database$table->string('days');
: sama seperti name akan membuat tipe menjadiVARCHAR
di database mysql$table->time('time');
: membuat kolom dengan name time dan memiliki tipe dataTIME
$table->string('description');
: sama seperti kolom name dan days akan menciptakan tipeVARCHAR
dan name kolom description$table->integer('user_id');
: membuat kolom dengan name user_id dan memiliki tipeINT
$table->timestamps();
: disini untuk menggenerate sebuah kolomcreate_at
danupdate_at
secara otomatis dengan data typeTIMESTAMP
setelah file migrate dirasasa sudah sesuai dengan kebutuhan maka lanjut untuk menjalankan command berikut:
php artisan migrate
Membuat Model
lanjut membuat model laravel untuk mapping data yang bisa di modif atau data yang akan digunakan di model laravel, maka kita buat sebuah model dengan nama TaskScheduler.php
yang ada di ...\app\Models\TaskScheduler.php
, dengan isi kode berikut ini :
- Model Task
kita gunakan artisan untuk mengenerate model
php artisan make:model Task
- edit file seperti berikut yang berada di
\app\Models\Task.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Task extends Model
{
use HasFactory;
protected $table = 'task';
protected $fillable = [
'name','description','user_id','status'
];
}
- Model Task Scheduler kita gunakan artisan untuk mengenerate model
php artisan make:model TaskScheduler
- edit file seperti berikut yang berada di
\app\Models\TaskScheduler.php
// TaskScheduler.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class TaskScheduler extends Model
{
use HasFactory;
protected $table = 'task_scheduler';
protected $fillable = [
'name','days','time','description','user_id','log_executed',
];
}
Membuat Repository
Dan disini menggunakan Repository Pattern yang artinya untuk mengakses ke model harus melalui repository terlebih dahulu, mari kita buat sebuah repository dengan nama TaskScheduleRepository.php
di folder path ...\app\Repositories\TaskScheduleRepository.php
, berikut isi pengkodean repository untuk TaskScheduleRepository.php
dan TaskRepository.php
:
Task Repository
- membuat file repo yang berada di
..\app\Repositories\TaskRepository.php
// TaskRepository.php
<?php
namespace App\Repositories;
use App\Models\Task;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
class TaskRepository
{
public function create($input)
{
try {
$task = Task::create($input);
if ($task->save()) {
$result['status'] = true;
$result['code'] = 201;
$result['message'] = "Berhasil Membuat Task Scheduler baru";
return $result;
} else {
$result['status'] = false;
$result['code'] = 400;
$result['message'] = "Gagal Membuat Task Scheduler baru";
$result['task'] = (object)[];
return $result;
}
} catch (\Exception $e) {
$result['status'] = false;
$result['code'] = 500;
$result['message'] = "Gagal Membuat Task Scheduler baru";
$result['task'] = (object)[];
$result['error'] = [
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line_of_code' => $e->getLine(),
'code' => $e->getCode(),
];
return $result;
}
}
}
Task Schedule Repository
- membuat file repo yang berada di
..\app\Repositories\TaskScheduleRepository.php
// TaskScheduleRepository.php
<?php
namespace App\Repositories;
use App\Models\TaskScheduler;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
class TaskScheduleRepository
{
private $user;
public function __construct()
{
$this->user = Auth::user();
}
public function getById($id)
{
try {
$repoTask = TaskScheduler::where('user_id', $this->user->id)->where('id', $id)->get();
$result['code'] = 200;
$result['status'] = true;
$result['message'] = "Berhasil Memuat task";
$result['task'] = $repoTask;
return $result;
} catch (\Exception $e) {
$result['status'] = false;
$result['code'] = 500;
$result['message'] = "Gagal Memuat semua task";
$result['user'] = (object)[];
$result['error'] = [
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line_of_code' => $e->getLine(),
'code' => $e->getCode(),
];
return $result;
}
}
public function getAll()
{
try {
$repoTask = TaskScheduler::where('user_id', $this->user->id)->get();
$result['code'] = 200;
$result['status'] = true;
$result['message'] = "Berhasil Memuat task";
$result['task'] = $repoTask;
return $result;
} catch (\Exception $e) {
$result['status'] = false;
$result['code'] = 500;
$result['message'] = "Gagal Memuat semua task";
$result['user'] = (object)[];
$result['error'] = [
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line_of_code' => $e->getLine(),
'code' => $e->getCode(),
];
return $result;
}
}
public function create($input)
{
try {
$input['days'] = implode(',', $input['days']);
$input['user_id'] = $this->user->id;
$task = TaskScheduler::create($input);
if ($task->save()) {
$result['status'] = true;
$result['code'] = 201;
$result['message'] = "Berhasil Membuat Task Scheduler baru";
return $result;
} else {
$result['status'] = false;
$result['code'] = 400;
$result['message'] = "Gagal Membuat Task Scheduler baru";
$result['user'] = (object)[];
return $result;
}
} catch (\Exception $e) {
$result['status'] = false;
$result['code'] = 500;
$result['message'] = "Gagal Membuat Task Scheduler baru";
$result['user'] = (object)[];
$result['error'] = [
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line_of_code' => $e->getLine(),
'code' => $e->getCode(),
];
return $result;
}
}
public function update($input = [])
{
$task = TaskScheduler::find($input['id']);
try {
$input['days'] = implode(',', $input['days']);
if (!is_null($task) && $task->update($input)) {
$result['code'] = 200;
$result['status'] = true;
$result['message'] = "Berhasil Update task";
$result['task'] = $task;
} else {
$result['code'] = 400;
$result['status'] = false;
$result['message'] = "Gagal Update task";
$result['task'] = (object)[];
}
return $result;
} catch (\Exception $e) {
$result['status'] = false;
$result['code'] = 500;
$result['message'] = "Gagal Update task";
$result['task'] = (object)[];
$result['error'] = [
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line_of_code' => $e->getLine(),
'code' => $e->getCode(),
];
return $result;
}
}
public function delete($id)
{
$task = TaskScheduler::find($id);
try {
if ($task->delete($id)) {
$result['code'] = 200;
$result['status'] = true;
$result['message'] = "Berhasil Delete task";
}
return $result;
} catch (\Exception $e) {
$result['status'] = false;
$result['code'] = 500;
$result['message'] = "Gagal Delete task";
$result['user'] = (object)[];
$result['error'] = [
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line_of_code' => $e->getLine(),
'code' => $e->getCode(),
];
return $result;
}
}
}
Membuat Jobs di Laravel
berikut ini perintah Artisan untuk membuat sebuah Job:
php artisan make:job TaskSchedulerJob
Penjelasan :
- perintah diatas untuk membuat sebuah Job bernama
TaskScheduler
yang dimana nantinya akan menciptakan sebuah file phpTaskSchedulerJob.php
yang bertempat di...\app\Jobs\TaskSchedulerJob.php
, tujuannya kita akan membuat sebuah task todo di jam tertentu secara otomatis.
Membuat Endpoint API di Laravel
Menggenerate Controller
setelah selesai menyiapkan untuk kebutuhan database maka lanjut untuk membuat sebuah endpoint yang dimana membutuhkan sebuah controller dan route. mari kit buat controller dengan command php artisan laravel seperti berikut :
php artisan make:controller TaskScheduleController --resource
Penjelasan :
- dengan imbuhan
--resource
diakhir command menandakan bahwa controller akan di create dengan isian function methode indexcreate
,store
,show
,edit
,update
, dandestroy
akan tetapi tidak semua akan kita gunakan yang digunakan hanyaindex
,create
,update
, dandestroy
.
Pengkodean Controller
berikut ini kode controller yang digunakan untuk membuat API di laravel :
- pengkodengan untuk controller
TaskScheduleController
yang berada di file\app\Http\Controllers\TaskScheduleController.php
// TaskScheduleController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use App\Repositories\TaskScheduleRepository;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Log;
use DateTimeZone;
use App\Models\TaskScheduler;
use App\Jobs\TaskSchedulerJob;
use PhpParser\Node\Stmt\Echo_;
use Symfony\Component\Console\Input\Input;
class TaskScheduleController extends Controller
{
public function index()
{
$repoTask = new TaskScheduleRepository;
$response = $repoTask->getAll();
$statusCode = $response['code'];
unset($response['code']);
return response()->json($response, $statusCode);
}
public function create(Request $request)
{
$inputRequest = $request->input();
$validator = Validator::make($inputRequest, [
'name' => 'required|string',
'days' => 'required|array|in:Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday',
'time' => 'required|string|date_format:H:i',
'description' => 'required|string',
'user_id' => 'required|string',
]);
if ($validator->fails()) {
$error = $validator->messages()->first();
$response['status'] = false;
$response['message'] = $error;
return response()->json($response, 400);
}
$repoTask = new TaskScheduleRepository;
$response = $repoTask->create($inputRequest);
$statusCode = $response['code'];
unset($response['code']);
return response()->json($response, $statusCode);
}
public function store(Request $request)
{
//
}
public function show($id)
{
$repoTask = new TaskScheduleRepository;
$response = $repoTask->getById($id);
$statusCode = $response['code'];
unset($response['code']);
return response()->json($response, $statusCode);
}
public function edit($id)
{
//
}
public function update(Request $request, $id)
{
$inputRequest = $request->input();
$inputRequest['id'] = $id ?? null;
$validator = Validator::make($inputRequest, [
'id' => 'required|string',
'name' => 'required|string',
'days' => 'required|array|in:Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday',
'time' => 'required|string|date_format:H:i',
'description' => 'required|string',
'user_id' => 'required|string',
]);
if ($validator->fails()) {
$error = $validator->messages()->first();
$response['status'] = false;
$response['message'] = $error;
return response()->json($response, 400);
}
$repoTask = new TaskScheduleRepository;
$response = $repoTask->update($inputRequest);
$statusCode = $response['code'];
unset($response['code']);
return response()->json($response, $statusCode);
}
public function destroy($id)
{
//
}
public function handleJob()
{
\Log::info(Carbon::now());
$day = ucwords(\Carbon\Carbon::now()->Format('l'));
$time = \Carbon\Carbon::now()->setTimezone(new DateTimeZone('+7'))->format('H:i');
$response = TaskScheduler::where('days', 'like', '%'.$day.'%')
->where('time', '=', $time)
->get();
$response = $response->toArray();
$executed_schedulers = [];
if (count($response) > 0) {
foreach ($response as $key => $data) {
$_taskScheduleId = $data['id'];
$_taskUserId = $data['user_id'];
$_taskData = [
'name' => $data['name'],
'description' => $data['description'],
'user_id' => $data['user_id'],
];
/* proses queue terjadi disini */
TaskSchedulerJob::dispatch($_taskScheduleId, $_taskUserId, $_taskData)->onQueue('task_scheduler');
/* end queue */
$executed_schedulers[] = $_taskScheduleId;
}
}
$total_executed_scheduler = count($executed_schedulers);
if ($total_executed_scheduler > 0) {
$result['total_executed_scheduler'] = $total_executed_scheduler;
$result['executed_idSchedulers'] = $executed_schedulers;
$result['status'] = 'success';
} else {
$result['status'] = 'empty';
}
return $result;
}
}
Penjelasan :
- method
handleJob
di controller tujuannya untuk mentrigger dari cron ketika dipanggil, contohnya seperti berikut yang ada di...\app\Console\Kernel.php
:
...
protected function schedule(Schedule $schedule)
{
$schedule->call('\App\Http\Controllers\TaskScheduleController@handleJob')->everyMinute();
}
...
- yang dimana pengkodean di atas akan melakukan exsekusi setiap menitnya oleh sistem cron di sistem operasi kalian.
- dan selain itu kalian bisa mentriger tanpa harus menunggu cron, misalnya kesulitan untuk mencoba di environment testing atau di local kalian. kalian bisa gunakan di route dan panggil dari sana seperti memanggil controller pada umumnya.
Menambahkan route di api.php
Langsung saja pengkodeannya membuat sebuah API di Laravel untuk keperluan sebuah data task sebagai berikut:
setelah selesai pengkodean controller mari kita definisikan sebuah route endpoint yang akan kita gunakan, kita modifikasi file route\api.php
seperti berikut:
use App\Http\Controllers\TaskScheduleController;
Route::name('taskSchedule')->get('task-shcedule', [TaskScheduleController::class, 'index']);
Route::name('taskSchedule.create')->post('task-shcedule', [TaskScheduleController::class, 'create']);
Route::name('taskSchedule.detail')->get('task-shcedule/{id}', [TaskScheduleController::class, 'show']);
Route::name('taskSchedule.update')->put('task-shcedule/{id}', [TaskScheduleController::class, 'update']);
Route::name('taskSchedule.patch')->patch('task-shcedule/{id}', [TaskScheduleController::class, 'patch']);
Route::name('taskSchedule.delete')->delete('task-shcedule/{id}', [TaskScheduleController::class, 'destroy']);
// untuk memanggil job tanpa menunggu cron jalan vie endpoint task-shcedule/handleJob
Route::name('taskSchedule.handleJob')->delete('task-shcedule/handleJob', [TaskScheduleController::class, 'handleJob']);
Menjalankan Jobs Queue Laravel
sebelum menjalankan kita siapkan command scheduler dengan cara command berikut :
php artisan queue:work --queue=task_scheduler redis --tries=3 --timeout=0
Penjelasan :
queue:work
: perintah untuk menjalankan queue--queue=task_scheduler redis
: disini nama queuetask_scheduler
dengan menggunakan database redis--tries=3 --timeout=0
: disini mencoba untuk 3 kali jiga mengalami gagal dan timeout 0
Penutup
Perjalanan tutorial masih jauh dari kata sempurna harus dibenahi ok sampai sini masih belum menjalankan jobs schedule nya bisa menghubungi saya atau tunggu di up file projectnya ke github. sampai ketemu lagi terimakasih.
Penjelasan :