Contoh Penggunaan Contract di Laravel 12: Implementasi dan Binding

Kalau Anda sudah membaca artikel tentang apa itu Contract di Laravel 12, artikel ini melanjutkannya dengan contoh penggunaan nyata: bagaimana membuat implementasi Contract sendiri dan kapan ini berguna dalam proyek.

Menggunakan Contract Bawaan Laravel

Contract bawaan Laravel ada di namespace Illuminate\Contracts\*. Contoh yang paling sering dipakai adalah type-hinting di constructor untuk decoupling:

<?php

namespace App\Services;

use Illuminate\Contracts\Cache\Repository as CacheContract;
use Illuminate\Contracts\Mail\Mailer as MailerContract;

class NotificationService
{
    public function __construct(
        private CacheContract  $cache,
        private MailerContract $mailer,
    ) {}

    public function sendIfNotSent(string $userId, string $template): void
    {
        $cacheKey = "notification:{$userId}:{$template}";

        if ($this->cache->has($cacheKey)) {
            return; // Sudah dikirim
        }

        $this->mailer->send($template, [], fn ($m) => $m->to($userId));
        $this->cache->put($cacheKey, true, now()->addDay());
    }
}

Service ini bisa ditest dengan mudah — tinggal inject mock CacheContract dan MailerContract.

Membuat Contract Sendiri

Studi kasus: aplikasi yang butuh fitur kirim notifikasi ke berbagai channel (email, SMS, WhatsApp). Dengan Contract, kita bisa ganti implementasi tanpa mengubah kode yang memakainya.

Buat Contract interface di app/Contracts/NotificationChannel.php:

<?php

namespace App\Contracts;

interface NotificationChannel
{
    public function send(string $recipient, string $message): bool;
    public function isAvailable(): bool;
}

Buat beberapa implementasi:

<?php

namespace App\Services\Notifications;

use App\Contracts\NotificationChannel;
use Illuminate\Support\Facades\Mail;

class EmailChannel implements NotificationChannel
{
    public function send(string $recipient, string $message): bool
    {
        try {
            Mail::raw($message, fn ($m) => $m->to($recipient));
            return true;
        } catch (\Exception $e) {
            return false;
        }
    }

    public function isAvailable(): bool
    {
        return config('mail.default') !== null;
    }
}
<?php

namespace App\Services\Notifications;

use App\Contracts\NotificationChannel;

class WhatsAppChannel implements NotificationChannel
{
    public function __construct(
        private WhatsAppApiClient $client
    ) {}

    public function send(string $recipient, string $message): bool
    {
        return $this->client->sendMessage($recipient, $message);
    }

    public function isAvailable(): bool
    {
        return !empty(config('services.whatsapp.token'));
    }
}

Bind Contract ke Implementasi di Service Provider

<?php

namespace App\Providers;

use App\Contracts\NotificationChannel;
use App\Services\Notifications\WhatsAppChannel;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // Bind default implementation
        $this->app->bind(NotificationChannel::class, WhatsAppChannel::class);
    }
}

Sekarang di mana saja Anda type-hint NotificationChannel, Laravel otomatis inject WhatsAppChannel:

<?php

namespace App\Http\Controllers;

use App\Contracts\NotificationChannel;

class AlertController extends Controller
{
    public function __construct(
        private NotificationChannel $channel
    ) {}

    public function send(Request $request): Response
    {
        $sent = $this->channel->send(
            $request->recipient,
            $request->message
        );

        return response()->json(['success' => $sent]);
    }
}

Manfaat: Mudah Ganti Implementasi

Kalau besok ingin switch dari WhatsApp ke SMS, cukup ganti binding di service provider:

$this->app->bind(NotificationChannel::class, SmsChannel::class);

Controller dan semua kode yang pakai NotificationChannel tidak perlu diubah sama sekali.

Contoh: Kontekstual Binding

Kalau perlu inject implementasi berbeda di class berbeda:

$this->app->when(AlertController::class)
          ->needs(NotificationChannel::class)
          ->give(WhatsAppChannel::class);

$this->app->when(ReportController::class)
          ->needs(NotificationChannel::class)
          ->give(EmailChannel::class);

Baca Juga

Mau tim yang bantu bangun arsitektur aplikasi Laravel yang scalable? Lihat layanan pengembangan aplikasi kami.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *