Jumat, 24 May 2024

Membuat Kode Laravel Lebih Bersih: Memindahkan Kode dari Controller

Pada artikel ini kita akan belajar membuat kode project Laravel yang kita buat lebih bersih, kita pindahkan beberapa potongan kode yang ada di Controller.

Laravel

Laravel menawarkan berbagai metode untuk menjaga agar controller tetap ramping. Pertanyaan umum yang muncul adalah, “Di mana lagi saya bisa menempatkan logika saya?” Berikut adalah beberapa alternatif tempat untuk memindahkan logika dari Controller kamu:

  1. Form Requests: Menyederhanakan validasi data.
  2. Eloquent Mutators: Mengatur data selama interaksi dengan database.
  3. Eloquent Observers: Menanggapi peristiwa model tertentu.
  4. Service Classes: Mengelompokkan logika bisnis.
  5. Action Classes: Menangani tugas individu.
  6. Jobs: Mengelola tugas latar belakang.
  7. Events and Listeners: Mengkoordinasikan peristiwa aplikasi dan reaksi.
  8. Global Helpers: Fungsi praktis yang tersedia di mana saja.
  9. Traits: Cuplikan kode yang dapat digunakan di beberapa kelas.
  10. Base Controllers: Controller dengan fungsionalitas bersama.
  11. Repository Classes: Memusatkan interaksi dengan database.

Mari kita mulai dengan menyederhanakan kode Controller kamu.

Menyederhanakan Kode Controller

Pertama, mari kita lihat kode awal kamu:

public function store(Request $request)
{
    $this->authorize('user_create');
    $userData = $request->validate([
        'name' => 'required',
        'email' => 'required|unique:users',
        'password' => 'required',
    ]);
    $userData['start_at'] = Carbon::createFromFormat('m/d/Y', $request->start_at)->format('Y-m-d');
    $userData['password'] = bcrypt($request->password);
    $user = User::create($userData);
    $user->roles()->sync($request->input('roles', []));
    Project::create(['user_id' => $user->id, 'name' => 'Demo project 1']);
    Category::create(['user_id' => $user->id, 'name' => 'Demo category 1']);
    Category::create(['user_id' => $user->id, 'name' => 'Demo category 2']);
    MonthlyReport::where('month', now()->format('Y-m'))->increment('users_count');
    $user->sendEmailVerificationNotification();
    $admins = User::where('is_admin', 1)->get();
    Notification::send($admins, new AdminNewUserNotification($user));
    return response()->json(['result' => 'success', 'data' => $user], 200);
}

Menyederhanakan Validasi Laravel dengan Form Request

Validasi adalah bagian penting dari setiap aplikasi Laravel. Terutama saat menangani banyak bidang, validasi dapat membuat kode kamu berantakan. Mari kita sederhanakan ini.

Pertama, kita akan memindahkan aturan validasi ke dalam Form Request. Ini tidak hanya berlaku untuk validasi data tetapi juga untuk izin.

Untuk membuat Form Request, jalankan perintah berikut:

Terminal
php artisan make:request StoreUserRequest

Ini akan membuat file baru app\Http\Requests\StoreUserRequest.php dengan dua metode penting: authorize() untuk izin dan rules() untuk validasi data.

Form Request kamu seharusnya terlihat seperti ini:

StoreUserRequest.php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;

class StoreUserRequest extends FormRequest
{
    public function authorize()
    {
        return Gate::allows('user_create');
    }

    public function rules()
    {
        return [
            'name' => 'required',
            'email' => 'required|unique:users',
            'password' => 'required',
        ];
    }
}

Dalam Controller, gunakan StoreUserRequest alih-alih Request default. Kamu bisa mengakses data yang sudah divalidasi langsung dengan memanggil metode validated() dari Request.

Misalnya, Controller kamu mungkin terlihat seperti ini:

public function store(StoreUserRequest $request)
{
    $userData = $request->validated();
    $userData['start_at'] = Carbon::createFromFormat('m/d/Y', $request->start_at)->format('Y-m-d');
    $userData['password'] = bcrypt($request->password);
    $user = User::create($userData);
    $user->roles()->sync($request->input('roles', []));
    // ...
}

Perubahan kecil ini secara signifikan mengurangi kerumitan Controller, membuat kode kamu lebih terorganisir dan mudah dikelola.

Menyederhanakan Modifikasi Data dengan Eloquent: Mutators vs. Observers

Bayangkan kamu perlu mengatur beberapa data sebelum disimpan di database, seperti format tanggal atau enkripsi kata sandi. Alih-alih melakukan ini di dalam Controller, Laravel’s Eloquent menyediakan alat yang efisien: Mutators dan Observers. Mari kita lihat cara kerjanya.

Mutators

Mutators memungkinkan kamu untuk menangani nilai atribut model sebelum menyimpannya ke database. Ada dua pendekatan utama untuk mendefinisikan Mutators berdasarkan versi Laravel kamu:

Untuk Laravel 9 dan lebih lama:

public function setStartAtAttribute($value)
{
    $this->attributes['start_at'] = Carbon::createFromFormat('m/d/Y', $value)->format('Y-m-d');
}

public function setPasswordAttribute($value)
{
    $this->attributes['password'] = bcrypt($value);
}

Dari Laravel 9 dan seterusnya:

protected function startAt(): Attribute
{
    return Attribute::make(
        set: fn ($value) => Carbon::createFromFormat('m/d/Y', $value)->format('Y-m-d')
    );
}

protected function password(): Attribute
{
    return Attribute::make(
        set: fn ($value) => bcrypt($value)
    );
}

Observers

Observers memberikan kamu metode yang dipicu selama siklus hidup model, seperti saat entri dibuat atau diperbarui.

Untuk menghasilkan Observer untuk model User, gunakan:

Terminal
php artisan make:observer UserObserver --model=User

Berikut adalah contoh cara mengatur Observer untuk mengubah data sebelum catatan dibuat:

UserObserver.php
namespace App\Observers;

use App\Models\User;
use Carbon\Carbon;

class UserObserver
{
    public function creating(User $user)
    {
        $user->start_at = Carbon::createFromFormat('m/d/Y', $user->start_at)->format('Y-m-d');
        $user->password = bcrypt($user->password);
    }
}

Catatan bahwa beberapa metode, seperti creating(), mungkin tidak didokumentasikan secara resmi, menunjukkan bahwa mereka tidak selalu menjadi pendekatan yang direkomendasikan.

Implementasi Kode yang Lebih Bersih

Sekarang, dengan modifikasi data yang ditangani di luar Controller, kode menjadi lebih bersih:

public function store(StoreUserRequest $request)
{
    $user = User::create($request->validated());
    $user->roles()->sync($request->input('roles', []));
    // ...
}

Ini menunjukkan bagaimana dengan alat yang tepat, Laravel membantu dalam menghasilkan kode yang rapi dan dapat dikelola.

Menyederhanakan Controller dengan Service Classes di Laravel

Selanjutnya, dalam perjalanan kita untuk merapikan Controller, kita akan menyelami logika inti yang bertanggung jawab untuk menyimpan data di database. Kelas Service khusus akan menjadi sekutu kita untuk tugas ini.

Kelas Service tidak memiliki perintah artisan khusus untuk pembuatannya, jadi kita akan membuatnya secara manual. Mari kita kembangkan file UserService.php dalam direktori app/Services, yang berisi logika untuk membuat pengguna:

UserService.php
namespace App\Services;

use App\Models\User;

class UserService
{
    public function create(array $userData): User
    {
        $user = User::create($userData);
        $user->roles()->sync($userData['roles']);
        
        return $user;
    }
}

Dalam UserService, kita memperkenalkan metode create() yang mengambil data yang sudah divalidasi sebagai inputnya, mengatur pembuatan pengguna, sinkronisasi peran, dan mengembalikan pengguna baru.

Sekarang, untuk menggunakan layanan ini dalam Controller kita, ada beberapa jalur yang bisa kita tempuh:

  1. Langsung menginisiasi layanan di dalam Controller, dan mengoper data yang sudah divalidasi ke metode create().

    $user = (new UserService())->create($request->validated());
    
  2. Menggunakan dependency injection untuk memperkenalkan layanan ke dalam metode Controller, memungkinkan cara yang rapi dan efisien untuk mengakses fungsi layanan kita.

    public function store(StoreUserRequest $request, UserService $userService)
    {
        $user = $userService->create($request->validated());
        // ...
    }
    

Menggunakan pendekatan kelas layanan meningkatkan organisasi kode kita, mendorong keterbacaan dan pemeliharaan dengan menjaga Controller tetap ramping dan fokus.

Kelas Service dan Action di Laravel: Memahami Perbedaannya

Dalam ekosistem Laravel yang luas, baik kelas Service maupun Action menawarkan cara terorganisir untuk mengelola logika. Mereka tampak mirip tetapi digunakan berdasarkan bagaimana kamu lebih suka menyusun logika aplikasi kamu. Mari kita uraikan konsep-konsep ini dengan sederhana.

Kelas Action

Kelas Action bersifat presisi, biasanya didedikasikan untuk satu operasi. Tidak seperti kelas Service, tidak ada perintah Artisan bawaan untuk

membuat kelas Action. Namun, konvensi dalam penamaan kelas Action adalah dengan menggunakan akhiran Action.

Mari kita buat CreateUserAction.php di app/Actions:

CreateUserAction.php
namespace App\Actions;

use App\Models\User;

class CreateUserAction
{
    public function execute(array $data): User
    {
        $user = User::create($data);
        $user->roles()->sync($data['roles']);
        
        return $user;
    }
}

Berikut adalah cara kita menggunakannya dalam Controller kita, menggunakan dependency injection:

public function store(StoreUserRequest $request, CreateUserAction $createUserAction)
{
    $user = $createUserAction->execute($request->validated());
    // ...
}

Seperti yang terlihat, kelas Action juga mendorong logika bisnis yang terpisah, tetapi biasanya lebih terfokus pada tugas spesifik.

Memperbaiki Notifikasi dengan Observer di Laravel

Melanjutkan dengan menyiapkan Observer, kita akan memperkenalkan logika untuk mengirim pemberitahuan admin saat pengguna baru dibuat. Di Laravel, kamu dapat memanfaatkan metode created() dalam Observer untuk mencapai ini.

UserObserver.php
namespace App\Observers;

use App\Models\User;
use App\Notifications\AdminNewUserNotification;
use Illuminate\Support\Facades\Notification;

class UserObserver
{
    public function created(User $user)
    {
        $admins = User::where('is_admin', 1)->get();
        Notification::send($admins, new AdminNewUserNotification($user));
    }
}

Observer membantu kamu mengelola logika pemberitahuan tanpa menambahkan beban ke Controller kamu. Berikut adalah contoh cara mendaftarkan Observer di AppServiceProvider:

AppServiceProvider.php
use App\Models\User;
use App\Observers\UserObserver;

public function boot()
{
    User::observe(UserObserver::class);
}

Dengan ini, Controller kamu tetap bersih dan tanggung jawab pemberitahuan ditangani dengan rapi oleh Observer.

Memperbarui Laporan Bulanan dengan Job di Laravel

Untuk menangani pembaruan laporan bulanan, kamu bisa memanfaatkan pekerjaan latar belakang (Job) di Laravel. Jobs memungkinkan kamu untuk menangani tugas asinkron yang memerlukan waktu lebih lama untuk diproses, memastikan aplikasi kamu tetap responsif.

Untuk membuat Job yang memperbarui laporan bulanan:

Terminal
php artisan make:job UpdateMonthlyReport

Kemudian, di dalam Job, kita dapat mendefinisikan logika untuk memperbarui laporan:

UpdateMonthlyReport.php
namespace App\Jobs;

use App\Models\MonthlyReport;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class UpdateMonthlyReport implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function handle()
    {
        MonthlyReport::where('month', now()->format('Y-m'))->increment('users_count');
    }
}

Kemudian, di dalam Controller kamu, cukup panggil pekerjaan ini:

use App\Jobs\UpdateMonthlyReport;

public function store(StoreUserRequest $request, CreateUserAction $createUserAction)
{
    $user = $createUserAction->execute($request->validated());
    // Mengirimkan pekerjaan ke antrian
    UpdateMonthlyReport::dispatch();
    return response()->json(['result' => 'success', 'data' => $user], 200);
}

Dengan penggunaan Jobs, kamu memastikan tugas berat ditangani secara efisien di latar belakang, memberikan respons cepat kepada pengguna.

Kesimpulan

Dengan menerapkan praktik-praktik ini, kamu akan menemukan bahwa Controller kamu menjadi lebih ramping, logika lebih terorganisir, dan aplikasi lebih dapat dikelola. Laravel memberikan berbagai alat dan metode untuk memisahkan berbagai logika, dan dengan memanfaatkannya, kamu dapat menjaga kode tetap bersih, terstruktur, dan efisien.