Author: vandyahmad24

  • Notification di Laravel 12: Email, Database, dan Channel Kustom

    Hampir semua aplikasi web butuh notifikasi: konfirmasi order, reset password, atau pemberitahuan saat ada komentar baru. Laravel punya sistem notifikasi yang cukup fleksibel: satu kelas bisa kirim ke email, database, Slack, dan channel lain sekaligus.

    Artikel ini membahas cara kerja notification di Laravel 12, mulai dari membuat kelas notifikasi, kirim via email dan database, sampai membuat channel kustom.

    Membuat Kelas Notifikasi

    php artisan make:notification OrderConfirmed

    File dibuat di app/Notifications/OrderConfirmed.php:

    <?php
    
    namespace AppNotifications;
    
    use AppModelsOrder;
    use IlluminateBusQueueable;
    use IlluminateNotificationsNotification;
    use IlluminateNotificationsMessagesMailMessage;
    use IlluminateContractsQueueShouldQueue;
    
    class OrderConfirmed extends Notification implements ShouldQueue
    {
        use Queueable;
    
        public function __construct(
            private Order $order
        ) {}
    
        public function via(object $notifiable): array
        {
            return ['mail', 'database'];
        }
    
        public function toMail(object $notifiable): MailMessage
        {
            return (new MailMessage)
                ->subject("Order #{$this->order->id} Dikonfirmasi")
                ->greeting("Halo, {$notifiable->name}!")
                ->line("Order Anda telah kami terima dan sedang diproses.")
                ->action('Lihat Detail Order', route('orders.show', $this->order))
                ->line("Terima kasih sudah berbelanja.");
        }
    
        public function toDatabase(object $notifiable): array
        {
            return [
                'order_id' => $this->order->id,
                'message'  => "Order #{$this->order->id} dikonfirmasi",
                'url'      => route('orders.show', $this->order),
            ];
        }
    }

    Notifiable Trait

    Model yang ingin menerima notifikasi harus menggunakan trait Notifiable. Model User bawaan Laravel sudah menyertakan ini:

    use IlluminateNotificationsNotifiable;
    
    class User extends Authenticatable
    {
        use Notifiable;
    }

    Untuk model lain — misalnya Admin atau Customer — tambahkan trait yang sama.

    Mengirim Notifikasi

    // Lewat instance model
    $user->notify(new OrderConfirmed($order));
    
    // Lewat Notification facade (tanpa model spesifik)
    Notification::send($users, new OrderConfirmed($order));
    
    // On-demand — kirim ke email tanpa model User
    Notification::route('mail', 'admin@example.com')
                ->notify(new OrderConfirmed($order));

    Karena kelas ini implement ShouldQueue, notifikasi akan diproses di background queue, tidak memperlambat response HTTP.

    Notifikasi Email: Kustomisasi Template

    Untuk kontrol penuh atas tampilan email, publish template bawaan Laravel:

    php artisan vendor:publish --tag=laravel-mail

    Template disimpan di resources/views/vendor/mail/. Edit sesuai kebutuhan brand Anda.

    Kalau mau pakai Markdown template yang lebih kaya:

    php artisan make:notification OrderShipped --markdown=mail.order.shipped

    Template dibuat di resources/views/mail/order/shipped.blade.php:

    @component('mail::message')
    # Order Anda Dalam Perjalanan
    
    Pesanan **#{{ $order->id }}** sedang dikirim ke alamat Anda.
    
    @component('mail::button', ['url' => $trackingUrl])
    Lacak Pengiriman
    @endcomponent
    
    Estimasi tiba: **{{ $order->estimated_arrival }}**
    
    Salam,
    {{ config('app.name') }}
    @endcomponent

    Notifikasi Database

    Untuk simpan notifikasi di database (biasanya untuk fitur notifikasi in-app), buat tabel dulu:

    php artisan make:notifications-table
    php artisan migrate

    Ambil notifikasi yang belum dibaca:

    // Semua notifikasi
    $notifications = auth()->user()->notifications;
    
    // Hanya yang belum dibaca
    $unread = auth()->user()->unreadNotifications;
    
    // Tandai sudah dibaca
    auth()->user()->unreadNotifications->markAsRead();
    
    // Tandai satu notifikasi sudah dibaca
    $notification->markAsRead();

    Channel Kustom

    Kalau perlu kirim via WhatsApp, Telegram, atau sistem internal, buat channel kustom:

    <?php
    
    namespace AppNotificationsChannels;
    
    use IlluminateNotificationsNotification;
    
    class WhatsAppChannel
    {
        public function send(object $notifiable, Notification $notification): void
        {
            $message = $notification->toWhatsApp($notifiable);
    
            // Kirim via WhatsApp API
            app(WhatsAppService::class)->send(
                $notifiable->phone,
                $message
            );
        }
    }

    Daftarkan di method via():

    public function via(object $notifiable): array
    {
        return [WhatsAppChannel::class, 'database'];
    }
    
    public function toWhatsApp(object $notifiable): string
    {
        return "Order #{$this->order->id} Anda sudah dikonfirmasi.";
    }

    Baca Juga

    Butuh tim yang bantu implementasi sistem notifikasi di aplikasi Laravel Anda? Lihat layanan pengembangan aplikasi kami.

  • Keunggulan Laravel Volt: Mengapa Single-File Component Lebih Efisien

    Setelah cukup banyak pakai Livewire biasa, saya coba beralih ke Volt di salah satu proyek internal. Hasilnya memang ada perbedaan yang terasa, terutama saat nambah fitur baru atau onboarding developer junior ke proyek.

    Artikel ini membahas keunggulan nyata Laravel Volt dibanding cara penulisan Livewire sebelumnya, lengkap dengan contoh kode perbandingan.

    1. Single-File Component: Logika dan Template Dalam Satu Tempat

    Livewire biasa butuh dua file per komponen. Volt hanya butuh satu.

    Livewire biasa:

    // app/Livewire/SearchBox.php
    class SearchBox extends Component
    {
        public string $query = '';
    
        public function updatedQuery(): void
        {
            // filter results
        }
    
        public function render()
        {
            return view('livewire.search-box');
        }
    }
    
    {{-- resources/views/livewire/search-box.blade.php --}}
    <div>
        <input wire:model.live="query" />
    </div>

    Volt (satu file):

    <?php
    
    use function LivewireVolt{state};
    
    state(['query' => '']);
    
    $updatedQuery = fn() => /* filter results */;
    
    ?>
    
    <div>
        <input wire:model.live="query" />
    </div>

    Lebih sedikit file = lebih mudah navigasi, lebih mudah review PR.

    2. Computed Properties yang Lebih Eksplisit

    Livewire v2 punya computed properties tapi agak tersembunyi. Volt mengekspos ini dengan cara yang lebih jelas:

    <?php
    
    use AppModelsProduct;
    use function LivewireVolt{state, computed};
    
    state(['category' => 'all']);
    
    $products = computed(function () {
        return $this->category === 'all'
            ? Product::paginate(12)
            : Product::where('category', $this->category)->paginate(12);
    });
    
    ?>
    
    <div>
        <select wire:model.live="category">
            <option value="all">Semua</option>
            <option value="elektronik">Elektronik</option>
        </select>
    
        @foreach ($this->products as $product)
            <p>{{ $product->name }}</p>
        @endforeach
    
        {{ $this->products->links() }}
    </div>

    Computed property otomatis di-cache selama satu request, tidak dihitung ulang setiap setiap kali template render.

    3. Lifecycle Hooks yang Lebih Bersih

    Volt mendukung lifecycle hooks Livewire dengan cara yang lebih ringkas:

    <?php
    
    use AppModelsCart;
    use function LivewireVolt{state, mount, updated};
    
    state(['quantity' => 1, 'productId' => null]);
    
    mount(function (int $productId) {
        $this->productId = $productId;
    });
    
    updated('quantity', function () {
        if ($this->quantity < 1) $this->quantity = 1;
        if ($this->quantity > 99) $this->quantity = 99;
    });
    
    $addToCart = function () {
        Cart::addItem($this->productId, $this->quantity);
        $this->dispatch('cart-updated');
    };
    
    ?>
    
    <div>
        <input wire:model="quantity" type="number" />
        <button wire:click="addToCart">Tambah ke Keranjang</button>
    </div>

    4. Form Handling yang Lebih Bersih

    Volt mendukung Form Objects dari Livewire v3:

    <?php
    
    use AppLivewireFormsContactForm;
    use function LivewireVolt{form};
    
    form(ContactForm::class, 'contact');
    
    $submit = function () {
        $this->contact->submit();
        $this->reset('contact');
    };
    
    ?>
    
    <form wire:submit="submit">
        <input wire:model="contact.name" />
        <input wire:model="contact.email" />
        <textarea wire:model="contact.message"></textarea>
        <button type="submit">Kirim</button>
    </form>

    5. Lebih Mudah Dipahami oleh Developer Baru

    Ini mungkin yang paling terasa di tim. Dengan Volt, developer yang baru bergabung cukup buka satu file untuk memahami apa yang dilakukan sebuah komponen. Tidak perlu loncat antara dua file untuk trace logika.

    Baca Juga

    Tertarik membangun aplikasi dengan Laravel + Livewire + Volt? Lihat layanan pengembangan aplikasi kami.

  • Cara Instalasi Laravel Volt di Laravel 12: Via Breeze dan Livewire Langsung

    Laravel Volt sudah termasuk dalam Livewire v3, jadi cara installnya lebih mudah dari yang Anda bayangkan. Artikel ini membahas dua jalur instalasi: lewat Laravel Breeze, dan langsung lewat Livewire.

    Prasyarat

    • PHP 8.1 atau lebih baru
    • Laravel 10 ke atas (disarankan Laravel 12)
    • Composer

    Jalur 1: Install via Laravel Breeze

    Ini jalur paling umum. Breeze menyediakan scaffolding autentikasi lengkap sekaligus menginstall Volt.

    # Buat project Laravel baru
    composer create-project laravel/laravel nama-project
    cd nama-project
    
    # Install Breeze
    composer require laravel/breeze --dev
    
    # Install dengan opsi Livewire (Volt sudah termasuk)
    php artisan breeze:install livewire
    
    # Install dependencies Node.js dan build assets
    npm install && npm run dev
    
    # Jalankan migration
    php artisan migrate

    Setelah ini, Anda langsung punya halaman login, register, dan dashboard yang sudah jalan dengan Livewire + Volt.

    Jalur 2: Install Livewire + Volt Langsung

    Kalau tidak butuh scaffolding autentikasi dari Breeze:

    # Di project Laravel yang sudah ada
    composer require livewire/livewire
    
    # Volt sudah termasuk di dalam package livewire/livewire v3
    # Aktifkan Volt dengan publish config
    php artisan livewire:publish --config

    Tambahkan direktif Livewire di layout utama Anda (resources/views/layouts/app.blade.php):

    <html>
    <head>
        ...
        @livewireStyles
    </head>
    <body>
        {{ $slot }}
        @livewireScripts
    </body>
    </html>

    Konfigurasi Path Komponen Volt

    Secara default, komponen Volt disimpan di resources/views/livewire/. Anda bisa konfigurasi lewat app/Providers/AppServiceProvider.php:

    <?php
    
    namespace App\Providers;
    
    use Illuminate\Support\ServiceProvider;
    use Livewire\Volt\Volt;
    
    class AppServiceProvider extends ServiceProvider
    {
        public function boot(): void
        {
            // Tambah folder komponen Volt
            Volt::mount([
                resource_path('views/livewire'),
                resource_path('views/pages'),  // tambahan
            ]);
        }
    }

    Membuat Komponen Volt Pertama

    php artisan make:volt search-box

    File dibuat di resources/views/livewire/search-box.blade.php:

    <?php
    
    use function Livewire\Volt\{state};
    
    state(['query' => '']);
    
    ?>
    
    <div>
        <input
            wire:model.live.debounce.300ms="query"
            placeholder="Cari..."
            class="border rounded px-3 py-2"
        />
    
        @if($query)
            <p>Mencari: {{ $query }}</p>
        @endif
    </div>

    Gunakan di halaman Blade mana saja:

    <livewire:search-box />

    Verifikasi Instalasi

    # Pastikan Livewire terinstall
    composer show livewire/livewire
    
    # Jalankan server dan cek halaman yang ada komponen Volt
    php artisan serve

    Kalau komponen muncul dan reaktif (bisa klik/input tanpa reload halaman), instalasi berhasil.

    Baca Juga

    Mau membangun aplikasi web interaktif dengan Laravel + Livewire? Lihat layanan pengembangan aplikasi kami.

  • Perbedaan Laravel Volt dan Laravel Breeze: Kapan Pakai Yang Mana?

    Saat mulai proyek Laravel baru, salah satu pertanyaan yang sering muncul adalah: pakai Breeze atau Volt? Keduanya dari ekosistem Laravel, tapi perannya sangat berbeda.

    Artikel ini menjelaskan perbedaan Laravel Volt dan Laravel Breeze, bukan untuk memilih yang “lebih baik”, tapi untuk memahami kapan masing-masing dipakai.

    Laravel Breeze: Starter Kit Autentikasi

    Laravel Breeze adalah starter kit yang menyediakan scaffolding autentikasi: login, register, reset password, verifikasi email, dan konfirmasi password.

    Breeze bisa diinstall dengan beberapa pilihan frontend:

    • Blade (default) — tanpa JavaScript framework
    • Livewire — menggunakan Livewire untuk interaktivitas
    • Inertia + Vue
    • Inertia + React
    • API (untuk SPA atau mobile)

    Ketika Anda pilih opsi Livewire saat install Breeze, Volt sudah termasuk di dalamnya.

    composer require laravel/breeze --dev
    php artisan breeze:install livewire

    Laravel Volt: Cara Tulis Komponen Livewire

    Volt bukan starter kit. Volt adalah API untuk menulis komponen Livewire dalam format single-file.

    Perbedaannya dengan Livewire biasa: tidak perlu file class PHP terpisah. Logika dan template ada dalam satu file Blade:

    <?php
    use function LivewireVolt{state};
    
    state(['search' => '']);
    
    $results = computed(function () {
        return Article::where('title', 'like', "%{$this->search}%")->get();
    });
    ?>
    
    <div>
        <input wire:model.live="search" placeholder="Cari artikel..." />
        @foreach ($this->results as $article)
            <p>{{ $article->title }}</p>
        @endforeach
    </div>

    Perbandingan Langsung

    Aspek Breeze Volt
    Fungsi utama Scaffolding autentikasi API tulis komponen Livewire
    Diinstall terpisah? Ya, via Composer Sudah termasuk di Livewire v3
    Menghasilkan file? Ya (routes, views, controllers) Tidak — hanya mengubah cara penulisan
    Bisa dipakai tanpa yang lain? Ya Ya (tapi butuh Livewire v3)
    Wajib? Tidak Tidak

    Hubungan Keduanya

    Breeze dan Volt bukan saingan. Keduanya bisa dipakai bersamaan. Skenario yang paling umum:

    1. Install Breeze dengan opsi Livewire → Volt otomatis terinstall
    2. Gunakan Volt untuk menulis komponen-komponen interaktif di aplikasi Anda
    3. Halaman autentikasi (login, register) sudah di-generate Breeze

    Kalau Anda tidak butuh autentikasi siap pakai, bisa install Livewire dan Volt langsung tanpa Breeze.

    Kapan Tidak Perlu Breeze?

    • Proyek API-only (tidak ada view)
    • Autentikasi sudah diimplementasi custom
    • Menggunakan Jetstream (alternatif Breeze yang lebih lengkap)

    Baca Juga

    Butuh tim yang bantu setup Laravel + Livewire untuk proyek Anda? Lihat layanan pengembangan aplikasi kami.

  • Apa Itu Laravel Volt dan Bagaimana Cara Kerjanya

    Kalau Anda pernah pakai Livewire di Laravel, mungkin sudah tahu betapa nyamannya bikin komponen reaktif tanpa harus tulis JavaScript. Laravel Volt membawa pengalaman itu selangkah lebih jauh dengan sintaks single-file component yang lebih bersih.

    Artikel ini menjelaskan apa itu Laravel Volt, bagaimana cara kerjanya, dan di mana Volt cocok dipakai.

    Apa Itu Laravel Volt?

    Laravel Volt adalah API baru untuk Livewire v3 yang memungkinkan Anda menulis komponen Livewire dalam satu file Blade, tanpa class PHP terpisah.

    Sebelum Volt, sebuah komponen Livewire butuh dua file:

    1. app/Livewire/Counter.php — class dengan logika
    2. resources/views/livewire/counter.blade.php — template

    Dengan Volt, keduanya bisa dalam satu file counter.blade.php:

    <?php
    
    use function LivewireVolt{state, computed};
    
    state(['count' => 0]);
    
    $increment = fn() => $this->count++;
    $decrement = fn() => $this->count--;
    
    ?>
    
    <div>
        <h1>{{ $count }}</h1>
        <button wire:click="increment">+</button>
        <button wire:click="decrement">-</button>
    </div>

    Lebih ringkas, dan semua yang perlu dibaca ada dalam satu file.

    Perbedaan Volt Class API vs Functional API

    Volt mendukung dua cara penulisan. Functional API (contoh di atas) lebih ringkas untuk komponen sederhana. Class API cocok untuk komponen yang lebih kompleks karena lebih terstruktur:

    <?php
    
    use LivewireVoltComponent;
    
    new class extends Component {
        public int $count = 0;
    
        public function increment(): void
        {
            $this->count++;
        }
    
        public function decrement(): void
        {
            $this->count--;
        }
    } ?>
    
    <div>
        <h1>{{ $count }}</h1>
        <button wire:click="increment">+</button>
        <button wire:click="decrement">-</button>
    </div>

    Apa Yang Volt Sederhanakan?

    Volt bukan framework baru, melainkan layer di atas Livewire v3. Yang berubah:

    • Satu file per komponen: tidak perlu buka dua file sekaligus saat edit
    • Co-location: logika dan template ada di satu tempat, lebih mudah dipahami konteksnya
    • Lebih sedikit boilerplate: tidak perlu bikin class, register namespace, dll.

    Yang tetap sama: cara Livewire bekerja (wire:click, wire:model, $refresh, dll.) tidak berubah sama sekali.

    Kapan Pakai Volt vs Livewire Biasa?

    Volt cocok untuk komponen yang:

    • Logikanya tidak terlalu besar (kurang dari ~100 baris)
    • Tidak perlu di-extend atau di-inherit komponen lain
    • Berdiri sendiri dan tidak terlalu banyak dependency

    Tetap pakai Livewire class biasa kalau:

    • Komponen butuh extend dari base class kustom
    • Logikanya sudah terlalu panjang dan butuh dipisah ke service
    • Tim lebih familiar dengan pola class-based

    Baca Juga

    Tertarik membangun aplikasi dengan stack Laravel + Livewire? Lihat layanan pengembangan aplikasi kami.

  • Migration di Laravel: Panduan Lengkap dengan Contoh Kode

    Setiap developer Laravel pasti sudah kenal migration. Tapi banyak yang belum tahu fitur-fitur migration yang lebih dalam: cara rollback aman, modifikasi kolom tanpa data hilang, atau cara buat index yang tepat.

    Artikel ini membahas migration di Laravel dari dasar sampai teknik yang dipakai di proyek production.

    Apa Itu Migration?

    Migration adalah cara Laravel mengelola struktur database lewat kode PHP, bukan lewat SQL manual. Setiap migration adalah “version control” untuk database Anda.

    Keuntungannya: semua developer di tim punya struktur database yang sama, dan perubahan terlacak di git.

    Membuat Migration

    # Buat tabel baru
    php artisan make:migration create_articles_table
    
    # Modifikasi tabel yang sudah ada
    php artisan make:migration add_published_at_to_articles_table
    
    # Migration sekalian dengan model dan factory
    php artisan make:model Article -m

    File migration dibuat di folder database/migrations/ dengan timestamp sebagai prefix.

    Struktur Migration

    Migration punya dua method: up() untuk apply, down() untuk rollback.

    <?php
    
    use IlluminateDatabaseMigrationsMigration;
    use IlluminateDatabaseSchemaBlueprint;
    use IlluminateSupportFacadesSchema;
    
    return new class extends Migration
    {
        public function up(): void
        {
            Schema::create('articles', function (Blueprint $table) {
                $table->id();
                $table->foreignId('user_id')->constrained()->cascadeOnDelete();
                $table->string('title');
                $table->string('slug')->unique();
                $table->text('content');
                $table->enum('status', ['draft', 'published'])->default('draft');
                $table->timestamp('published_at')->nullable();
                $table->timestamps();
                $table->softDeletes();
            });
        }
    
        public function down(): void
        {
            Schema::dropIfExists('articles');
        }
    };

    Tipe Kolom yang Paling Sering Dipakai

    $table->id();                    // bigint unsigned auto-increment primary key
    $table->string('name');          // VARCHAR(255)
    $table->string('code', 10);      // VARCHAR(10)
    $table->text('body');            // TEXT
    $table->longText('content');     // LONGTEXT
    $table->integer('qty');          // INT
    $table->bigInteger('views');     // BIGINT
    $table->decimal('price', 10, 2); // DECIMAL(10,2)
    $table->boolean('is_active');    // TINYINT(1)
    $table->date('birth_date');      // DATE
    $table->timestamp('sent_at');    // TIMESTAMP
    $table->timestamps();            // created_at + updated_at
    $table->softDeletes();           // deleted_at
    
    // Relasi
    $table->foreignId('user_id')->constrained(); // FK ke tabel users
    $table->foreignId('category_id')
          ->nullable()
          ->constrained()
          ->nullOnDelete();

    Modifikasi Kolom yang Sudah Ada

    Gunakan change() untuk modifikasi kolom tanpa hapus tabel:

    public function up(): void
    {
        Schema::table('articles', function (Blueprint $table) {
            // Perluas panjang kolom
            $table->string('title', 500)->change();
    
            // Jadikan nullable
            $table->text('summary')->nullable()->change();
    
            // Tambah kolom baru
            $table->string('meta_title')->nullable()->after('title');
    
            // Hapus kolom
            $table->dropColumn('old_field');
    
            // Rename kolom
            $table->renameColumn('body', 'content');
        });
    }
    
    public function down(): void
    {
        Schema::table('articles', function (Blueprint $table) {
            $table->string('title', 255)->change();
            $table->dropColumn('meta_title');
            $table->renameColumn('content', 'body');
        });
    }

    Index untuk Performa Query

    Index yang tepat bisa membuat query jauh lebih cepat:

    Schema::create('orders', function (Blueprint $table) {
        $table->id();
        $table->foreignId('user_id')->constrained();
        $table->string('status');
        $table->timestamp('ordered_at');
        $table->timestamps();
    
        // Index tunggal
        $table->index('status');
    
        // Composite index — sangat berguna untuk filter gabungan
        $table->index(['user_id', 'status']);
        $table->index(['status', 'ordered_at']);
    
        // Unique constraint
        $table->unique(['user_id', 'order_number']);
    });

    Kapan tambah index: Kolom yang sering dipakai di WHERE, ORDER BY, atau JOIN. Jangan tambah index di semua kolom. Terlalu banyak index justru memperlambat operasi insert/update.

    Menjalankan Migration

    # Jalankan migration yang belum dijalankan
    php artisan migrate
    
    # Lihat status migration
    php artisan migrate:status
    
    # Rollback migration terakhir
    php artisan migrate:rollback
    
    # Rollback beberapa batch
    php artisan migrate:rollback --step=3
    
    # Reset semua migration
    php artisan migrate:reset
    
    # Drop semua tabel lalu migrate ulang (pakai di development)
    php artisan migrate:fresh --seed

    Migration di Production: Aturan Aman

    Beberapa hal yang penting saat deploy migration ke production:

    1. Jangan hapus kolom langsung: kalau kode masih pakai kolom tersebut, bisa error. Deprecate dulu, hapus di deploy berikutnya.
    2. Tambah kolom nullable atau dengan default: kolom NOT NULL tanpa default di tabel besar bisa lock tabel saat migration berjalan.
    3. Test rollback sebelum deploy — pastikan method down() bisa dijalankan tanpa error.
    4. Backup dulu — sebelum migration yang modifikasi struktur tabel penting.

    Baca Juga

    Kalau Anda butuh tim untuk bangun aplikasi Laravel dari awal sampai production, lihat layanan pengembangan aplikasi kami.

  • Tutorial PostgreSQL di Laravel: Setup, JSONB, dan Full-Text Search

    Laravel secara default menggunakan MySQL. Tapi kalau proyek Anda butuh fitur seperti JSON columns yang lebih canggih, full-text search bawaan, atau JSONB, PostgreSQL adalah pilihan yang solid.

    Artikel ini membahas cara setup PostgreSQL di Laravel, termasuk konfigurasi, perbedaan dengan MySQL, dan fitur-fitur PostgreSQL yang bisa dimanfaatkan langsung dari Eloquent.

    Instalasi dan Konfigurasi

    Pastikan extension PHP untuk PostgreSQL sudah aktif:

    # Ubuntu/Debian
    sudo apt install php-pgsql
    
    # Cek apakah sudah aktif
    php -m | grep pdo_pgsql

    Edit file .env:

    DB_CONNECTION=pgsql
    DB_HOST=127.0.0.1
    DB_PORT=5432
    DB_DATABASE=nama_database
    DB_USERNAME=postgres
    DB_PASSWORD=password_anda

    Tidak perlu ubah konfigurasi lain. Laravel sudah punya driver PostgreSQL bawaan.

    Perbedaan Sintaks Migration

    Sebagian besar sintaks migration sama antara MySQL dan PostgreSQL. Perbedaan utama ada di beberapa tipe data:

    Schema::create('products', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->decimal('price', 10, 2);
    
        // PostgreSQL: pakai jsonb, bukan json biasa
        $table->jsonb('metadata')->nullable();
    
        // PostgreSQL: UUID native
        $table->uuid('external_id')->unique();
    
        // Enum di PostgreSQL butuh cast berbeda
        $table->string('status')->default('active');
    
        $table->timestamps();
    });

    Tips: Di PostgreSQL, pakai jsonb daripada json. JSONB disimpan dalam format binary yang lebih efisien untuk query dan indexing.

    Query JSON dengan PostgreSQL

    PostgreSQL punya operator JSON yang sangat powerful. Di Laravel, Anda bisa pakai operator -> dan ->> langsung di Eloquent:

    // Cari produk berdasarkan field di dalam JSON
    $products = Product::where('metadata->category', 'elektronik')->get();
    
    // Atau dengan whereJsonContains
    $products = Product::whereJsonContains('metadata->tags', 'promo')->get();
    
    // Query nested JSON
    $products = Product::where('metadata->specs->weight', '>', 500)->get();
    

    Contoh migration dengan index JSONB:

    Schema::create('products', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->jsonb('metadata')->nullable();
        $table->timestamps();
    });
    
    // Tambah index GIN untuk performa query JSONB
    DB::statement('CREATE INDEX products_metadata_gin ON products USING GIN (metadata jsonb_path_ops)');
    

    Full-Text Search

    PostgreSQL punya full-text search bawaan yang bisa langsung dipakai tanpa plugin tambahan:

    // Migration: tambah kolom tsvector
    $table->tsvector('search_vector')->nullable();
    
    // Isi kolom tsvector saat insert/update
    DB::statement("
        UPDATE articles
        SET search_vector = to_tsvector('indonesian', coalesce(title, '') || ' ' || coalesce(content, ''))
    ");
    
    // Query full-text search
    $results = Article::whereRaw(
        "search_vector @@ plainto_tsquery('indonesian', ?)",
        [$keyword]
    )->get();

    Untuk proyek besar, buat trigger PostgreSQL agar search_vector otomatis diperbarui saat data berubah.

    UUID sebagai Primary Key

    PostgreSQL mendukung UUID natively. Cara pakai di Laravel 12:

    Schema::create('orders', function (Blueprint $table) {
        $table->uuid('id')->primary()->default(DB::raw('gen_random_uuid()'));
        $table->string('status');
        $table->timestamps();
    });

    Di model:

    <?php
    
    namespace AppModels;
    
    use IlluminateDatabaseEloquentModel;
    use IlluminateDatabaseEloquentConcernsHasUuids;
    
    class Order extends Model
    {
        use HasUuids;
    }

    Transaksi dan Savepoint

    PostgreSQL mendukung nested transaction lewat savepoint. Di Laravel ini otomatis dipakai saat Anda nested DB::transaction():

    DB::transaction(function () {
        $order = Order::create([...]);
    
        DB::transaction(function () use ($order) {
            // Ini pakai savepoint di PostgreSQL
            // Kalau gagal, hanya bagian ini yang di-rollback
            foreach ($order->items as $item) {
                Inventory::decrement($item->product_id, $item->qty);
            }
        });
    
        $order->update(['status' => 'confirmed']);
    });

    Koneksi Multiple Database

    Kalau Anda pakai MySQL dan PostgreSQL bersamaan, tambahkan konfigurasi di config/database.php:

    'connections' => [
        'mysql' => [
            'driver' => 'mysql',
            // ...
        ],
        'pgsql' => [
            'driver'   => 'pgsql',
            'host'     => env('PGSQL_HOST', '127.0.0.1'),
            'port'     => env('PGSQL_PORT', '5432'),
            'database' => env('PGSQL_DATABASE'),
            'username' => env('PGSQL_USERNAME'),
            'password' => env('PGSQL_PASSWORD'),
        ],
    ],

    Di model, tentukan koneksi yang dipakai:

    class AnalyticsEvent extends Model
    {
        protected $connection = 'pgsql';
    }

    Baca Juga

    Butuh tim yang berpengalaman setup aplikasi Laravel dengan PostgreSQL di production? Lihat layanan pengembangan aplikasi kami.

  • Command Artisan di Laravel yang Paling Sering Dipakai

    Kalau Anda sudah cukup lama pakai Laravel, pasti sering jalankan perintah seperti php artisan migrate atau php artisan serve. Tapi banyak developer yang belum tahu ada puluhan command Artisan lain yang bisa menghemat banyak waktu.

    Artikel ini berisi daftar command Artisan yang paling sering dipakai dalam proyek Laravel sehari-hari, beserta kapan dan bagaimana menggunakannya.

    Apa Itu Artisan?

    Artisan adalah command-line interface (CLI) bawaan Laravel. Dengan Artisan, Anda bisa menjalankan berbagai tugas: generate file, jalankan migration, clear cache, hingga membuat custom command sendiri.

    Untuk lihat semua command yang tersedia:

    php artisan list

    Untuk bantuan command tertentu:

    php artisan help migrate

    Command untuk Generate File

    Ini yang paling sering dipakai saat awal development:

    # Buat controller
    php artisan make:controller ArticleController
    php artisan make:controller ArticleController --resource  # dengan CRUD methods
    php artisan make:controller ArticleController --api       # CRUD tanpa create/edit
    
    # Buat model (+ migration + factory + seeder sekaligus)
    php artisan make:model Article
    php artisan make:model Article -mfs  # migration + factory + seeder
    
    # Buat migration
    php artisan make:migration create_articles_table
    php artisan make:migration add_slug_to_articles_table
    
    # Buat seeder
    php artisan make:seeder ArticleSeeder
    
    # Buat Form Request
    php artisan make:request StoreArticleRequest
    
    # Buat middleware
    php artisan make:middleware EnsureEmailIsVerified
    
    # Buat service provider
    php artisan make:provider ReportServiceProvider

    Command Database

    Untuk mengelola migration dan seeder:

    # Jalankan migration baru
    php artisan migrate
    
    # Rollback migration terakhir
    php artisan migrate:rollback
    
    # Rollback semua, lalu migrate ulang
    php artisan migrate:fresh
    
    # Migrate + seed sekaligus
    php artisan migrate:fresh --seed
    
    # Lihat status semua migration
    php artisan migrate:status
    
    # Jalankan seeder
    php artisan db:seed
    php artisan db:seed --class=ArticleSeeder

    Perhatian: migrate:fresh akan menghapus semua tabel dan data. Jangan jalankan di production.

    Command Cache

    Sangat berguna setelah mengubah konfigurasi atau saat debugging:

    # Clear semua cache
    php artisan cache:clear
    
    # Clear config cache
    php artisan config:clear
    
    # Clear route cache
    php artisan route:clear
    
    # Clear view cache
    php artisan view:clear
    
    # Cache config (untuk production — mempercepat load)
    php artisan config:cache
    
    # Cache routes (untuk production)
    php artisan route:cache

    Kalau perubahan config atau route tidak terbaca, biasanya cukup jalankan dua command pertama.

    Command untuk Development Server

    # Jalankan development server
    php artisan serve
    
    # Jalankan di port tertentu
    php artisan serve --port=8080
    
    # Jalankan queue worker
    php artisan queue:work
    
    # Jalankan queue worker dengan restart otomatis saat ada perubahan kode
    php artisan queue:work --tries=3
    
    # Lihat list route
    php artisan route:list
    
    # Filter route berdasarkan nama
    php artisan route:list --name=article

    Command Tinker

    Tinker adalah REPL (interactive shell) untuk Laravel. Sangat berguna untuk coba query Eloquent atau test logika tanpa harus buat endpoint:

    php artisan tinker

    Di dalam Tinker:

    >>> AppModelsArticle::count()
    >>> AppModelsArticle::latest()->first()
    >>> AppModelsUser::factory()->create(['name' => 'Budi'])

    Command Maintenance Mode

    Kalau perlu deploy atau maintenance tanpa memutus koneksi database:

    # Aktifkan maintenance mode
    php artisan down
    
    # Aktifkan dengan pesan dan redirect
    php artisan down --message="Sedang maintenance" --retry=60
    
    # Aktifkan dengan bypass secret (Anda masih bisa akses site)
    php artisan down --secret="rahasia123"
    
    # Matikan maintenance mode
    php artisan up

    Command Berguna Lainnya

    # Generate app key (wajib setelah clone project)
    php artisan key:generate
    
    # Lihat semua event yang terdaftar
    php artisan event:list
    
    # Jalankan scheduler (biasanya di cron)
    php artisan schedule:run
    
    # Optimize semua (cache config + route + views)
    php artisan optimize
    
    # Clear semua cache (untuk development)
    php artisan optimize:clear

    Baca Juga

    Kalau Anda ingin tim yang bisa bantu bangun dan maintain aplikasi Laravel Anda, lihat layanan pengembangan aplikasi kami.

  • Membuat Controller di Laravel 12: Resource, API, dan Best Practice

    Membuat Controller di Laravel 12: Resource, API, dan Best Practice

    Controller adalah salah satu komponen paling sering ditulis di Laravel. Hampir setiap fitur butuh controller: dari menampilkan halaman, menyimpan data form, sampai mengembalikan JSON untuk API.

    Artikel ini membahas cara membuat controller di Laravel 12: dari controller dasar, resource controller, sampai best practice yang dipakai tim profesional.

    Membuat Controller Baru

    Gunakan perintah Artisan:

    php artisan make:controller ArticleController

    File akan dibuat di app/Http/Controllers/ArticleController.php:

    <?php
    
    namespace AppHttpControllers;
    
    use IlluminateHttpRequest;
    
    class ArticleController extends Controller
    {
        //
    }

    Tambahkan method sesuai kebutuhan. Misalnya untuk menampilkan daftar artikel:

    public function index()
    {
        $articles = Article::latest()->paginate(10);
        return view('articles.index', compact('articles'));
    }
    
    public function show(Article $article)
    {
        return view('articles.show', compact('article'));
    }

    Resource Controller

    Kalau Anda butuh semua operasi CRUD sekaligus, pakai flag --resource:

    php artisan make:controller ArticleController --resource

    Laravel akan generate 7 method sekaligus: index, create, store, show, edit, update, destroy.

    Daftarkan ke route dengan satu baris:

    Route::resource('articles', ArticleController::class);

    Ini otomatis membuat route untuk semua aksi CRUD, tidak perlu daftar satu per satu.

    API Resource Controller

    Untuk API yang tidak butuh halaman form (create/edit), pakai flag --api:

    php artisan make:controller Api/ArticleController --api

    Hasilnya hanya 5 method: index, store, show, update, destroy. Tanpa create dan edit.

    Route::apiResource('articles', ApiArticleController::class);

    Dependency Injection di Controller

    Laravel mendukung dependency injection lewat constructor. Ini cara yang direkomendasikan untuk inject service:

    <?php
    
    namespace AppHttpControllers;
    
    use AppServicesArticleService;
    use IlluminateHttpRequest;
    
    class ArticleController extends Controller
    {
        public function __construct(
            private ArticleService $articleService
        ) {}
    
        public function store(Request $request)
        {
            $validated = $request->validate([
                'title'   => 'required|string|max:255',
                'content' => 'required|string',
                'status'  => 'required|in:draft,published',
            ]);
    
            $article = $this->articleService->create($validated);
    
            return redirect()->route('articles.show', $article)
                             ->with('success', 'Artikel berhasil dibuat.');
        }
    }

    Middleware di Controller

    Di Laravel 12, cara attach middleware ke controller berubah. Tidak lagi lewat constructor, tapi lewat method middleware() di route atau lewat static method di controller:

    // Di routes/web.php
    Route::resource('articles', ArticleController::class)
         ->middleware('auth');
    
    // Atau partial — hanya method tertentu
    Route::resource('articles', ArticleController::class)
         ->only(['store', 'update', 'destroy'])
         ->middleware('auth');

    Kalau ingin lebih granular per-method, pakai route biasa:

    Route::get('/articles', [ArticleController::class, 'index']);
    Route::post('/articles', [ArticleController::class, 'store'])->middleware('auth');
    Route::get('/articles/{article}', [ArticleController::class, 'show']);

    Form Request untuk Validasi

    Untuk controller yang lebih bersih, pindahkan validasi ke Form Request:

    php artisan make:request StoreArticleRequest
    <?php
    
    namespace AppHttpRequests;
    
    use IlluminateFoundationHttpFormRequest;
    
    class StoreArticleRequest extends FormRequest
    {
        public function authorize(): bool
        {
            return auth()->check();
        }
    
        public function rules(): array
        {
            return [
                'title'   => 'required|string|max:255',
                'content' => 'required|string|min:100',
                'status'  => 'required|in:draft,published',
            ];
        }
    }

    Gunakan di controller:

    public function store(StoreArticleRequest $request)
    {
        $article = Article::create($request->validated());
    
        return redirect()->route('articles.show', $article)
                         ->with('success', 'Artikel disimpan.');
    }

    Controller jadi lebih ringkas karena validasi sudah diurus Form Request.

    Nested Resource Controller

    Kalau ada relasi hierarkis, misalnya komentar yang dimiliki artikel, gunakan nested resource:

    Route::resource('articles.comments', CommentController::class);

    URL yang dihasilkan: /articles/{article}/comments/{comment}

    Controller-nya otomatis menerima kedua model sebagai parameter:

    public function store(Request $request, Article $article)
    {
        $comment = $article->comments()->create([
            'body'    => $request->validated('body'),
            'user_id' => auth()->id(),
        ]);
    
        return redirect()->route('articles.show', $article);
    }

    Baca Juga

    Butuh tim untuk membangun aplikasi web berbasis Laravel dari awal? Lihat layanan pengembangan aplikasi kami.

  • Membuat Controller di Laravel 11

    Membuat Controller di Laravel 11

    Pada artikel ini kita akan membahas cara membuat controller di laravel 11 secara praktis, dari persiapan lingkungan hingga implementasi praktis dan best practice. Laravel sebagai framework PHP modern memudahkan pengelolaan arsitektur aplikasi dengan konsep controller, routing, dan dependency injection. Dalam konteks proyek nyata, memahami cara membangun controller yang bersih, terstruktur, dan maintainable adalah pondasi utama. Panduan ini dirancang untuk pemula hingga developer berpengalaman, dengan contoh kode yang bisa langsung dicoba di lingkungan lokal. Kami juga akan membahas bagaimana menjaga kode tetap scalable, mudah dites, dan mudah dipelihara seiring bertambahnya kompleksitas proyek. Kami menekankan bahwa praktik yang tepat saat membuat controller di laravel 11 akan mempercepat pengembangan, mengurangi bug, serta meningkatkan kemudahan kolaborasi antar tim.

    Persiapan Lingkungan Laravel 11

    Untuk memulai, pastikan lingkungan pengembangan sudah siap. Anda memerlukan PHP >= 8.1, Composer, dan Laravel 11. Instalasi bisa dilakukan melalui Composer create-project atau laravel/installer. Pastikan ekstensi yang diperlukan seperti PDO, BCMath, OpenSSL tersedia. Setelah itu, buat project baru:

    composer create-project laravel/laravel blog-laravel11 "11.x"

    Langkah ini menyiapkan fondasi MVC standar, sehingga Anda bisa mulai membangun membuat controller di laravel 11 dengan perintah Artisan. Bila Anda membutuhkan referensi langkah demi langkah, lihat Layanan Arrazy Inovasi untuk solusi terkait pengembangan web.

    Selain itu, sebaiknya Anda merujuk juga pada Dokumentasi Laravel Resmi dan Dokumentasi PHP untuk memahami opsi konfigurasi serta best practice penerapan arsitektur pada proyek nyata.

    Membuat Controller Pertama dengan Artisan

    Setelah proyek siap, langkah selanjutnya adalah membuat controller pertama. Gunakan Artisan untuk meng-generate controller secara cepat:

    php artisan make:controller HelloWorldController

    Untuk sebuah API atau halaman berita, Anda juga bisa membuat membuat controller di laravel 11 dengan opsi resource:

    php artisan make:controller ArticleController --resource

    Berikut contoh struktur dasar controller yang bisa Anda adaptasi:

    namespace App\Http\Controllers;
    
    use App\Http\Controllers\Controller;
    use Illuminate\Http\Request;
    
    class HelloWorldController extends Controller
    {
    public function index()
    {
    return view('hello.index');
    }
    }

    Pelajari bagaimana alur request menuju controller bisa divalidasi sederhana dengan Request type hinting, sehingga membuat controller di laravel 11 menjadi lebih elegan dan mudah di-test. Untuk referensi praktis, kunjungi Layanan Arrazy Inovasi.

    Routing dan Pemanfaatan Controller

    Setelah controller dibuat, ikuti langkah berikut untuk menghubungkannya dengan routing. Untuk halaman biasa:

    // routes/web.php
    use App\Http\Controllers\HelloWorldController;
    Route::get('/hello', [HelloWorldController::class, 'index']);

    Untuk struktur CRUD dengan resource controller:

    Route::resource('articles', ArticleController::class);

    Dengan routing yang terdefinisi, membuat controller di laravel 11 akan segera bisa diakses melalui URL. Jika Anda ingin solusi end-to-end, lihat Layanan Arrazy Inovasi Teknologi.

    Resource Controller untuk CRUD

    Resource controller memudahkan implementasi operasi Create, Read, Update, Delete. Dengan satu deklarasi, Laravel akan mengerti semua rute konvensional. Berikut contoh metode inti yang umum dipakai:

    public function index() { ... }
    public function show($id) { ... }
    public function store(Request $request) { ... }
    public function update(Request $request, $id) { ... }
    public function destroy($id) { ... }

    Contoh Struktur File Controller

    Berikut contoh file Controller lengkap untuk Article dengan beberapa metode:

    namespace App\\Http\\Controllers;
    
    use App\\Http\\Controllers\\Controller;
    use Illuminate\\Http\\Request;
    use App\\Models\\Article;
    
    class ArticleController extends Controller
    {
    public function index()
    {
    $articles = Article::all();
    return view('articles.index', compact('articles'));
    }
    
    public function create()
    {
    return view('articles.create');
    }
    
    public function store(Request $request)
    {
    $data = $request->validate(['title' => 'required|string', 'content' => 'required']);
    Article::create($data);
    return redirect()->route('articles.index');
    }
    
    public function show(Article $article)
    {
    return view('articles.show', compact('article'));
    }
    
    public function edit(Article $article)
    {
    return view('articles.edit', compact('article'));
    }
    
    public function update(Request $request, Article $article)
    {
    $data = $request->validate(['title' => 'required|string', 'content' => 'required']);
    $article->update($data);
    return redirect()->route('articles.index');
    }
    
    public function destroy(Article $article)
    {
    $article->delete();
    return redirect()->route('articles.index');
    }
    }

    Dengan struktur seperti ini, membuat controller di laravel 11 tidak lagi menjadi pekerjaan yang menakutkan. Untuk mengecek integrasi dengan view dan model, bagian berikutnya akan membantu Anda memahami pola penggunaan view dan dependency injection.

    Integrasi dengan View dan Dependency Injection

    Controller di Laravel biasanya bekerja bersama View (Blade) dan model. Anda bisa mengembalikan data ke view dengan compact, atau menggunakan resource responses untuk API. Dependency Injection memudahkan pengambilan layanan (misalnya repository) tanpa membuat instance secara manual. Contoh sederhana:

    public function index()
    {
    $items = Item::all();
    return view('items.index', compact('items'));
    }

    Menyiapkan layanan khusus dapat dilakukan melalui constructor injection, sehingga logika bisnis tidak tertumpuk di controller. Jika Anda ingin solusi end-to-end, telusuri lebih lanjut di Layanan Arrazy Inovasi untuk memahami bagaimana arsitektur layanan dapat meningkatkan kualitas proyek Anda.

    Praktik Terbaik dan Keamanan

    Beberapa praktik terbaik saat membuat controller di laravel 11 meliputi:

    – Memisahkan logika presentasi dari logika bisnis dengan menggunakan service class atau repository.

    – Menggunakan Form Request untuk validasi agar controller tetap bersih.

    – Mengunci akses dengan middleware sesuai kebutuhan (auth, role-based access, dll).

    – Menjaga ukuran controller tetap kecil dengan membagi fungsi ke dalam kelas yang jelas.

    – Menambahkan komentar yang relevan dan dokumentasi singkat di tiap metode penting. Pelajari bagaimana Laravel menangani request lifecycle melalui Dokumentasi Laravel Resmi untuk pemahaman yang lebih dalam.

    Kalau Anda butuh dukungan implementasi nyata, Layanan Arrazy Inovasi siap membantu Anda—mulai dari pengembangan website hingga solusi integrasi ERP berbasis Laravel.

    Di tengah tutorial ini, Anda juga bisa meninjau beberapa layanan utama yang relevan, seperti Jasa Pembuatan Website, Jasa Pembuatan Aplikasi Mobile, Jasa SEO, Jasa Pembuatan Chatbot, dan Jasa Website Sekolah untuk kebutuhan edukasi digital Anda.

    Selain itu, untuk memperkaya referensi teknis Anda, lihat juga dokumentasi terbaru pada Dokumentasi PHP dan Dokumentasi Laravel Resmi sebagai pedoman standar industri.

    Penutup yang Menginspirasi

    Dengan memahami cara membuat controller di laravel 11 melalui pendekatan yang terstruktur, Anda akan lebih percaya diri mengelola proyek Laravel berukuran kecil maupun besar. Kunci utamanya adalah memulai dari konsep sederhana, lalu secara bertahap menambah kompleksitas dengan pola desain yang telah teruji. Jika Anda ingin mempercepat waktu implementasi, Layanan Arrazy Inovasi siap membantu Anda menyusun arsitektur aplikasi yang kuat bersama tim ahli kami.

    Terima kasih telah mengikuti panduan ini. Semoga langkah demi langkah dalam tutorial ini membantu Anda memahami bagaimana membuat controller di laravel 11 secara praktis dan profesional. Jangan ragu untuk menjangkau tim kami untuk bantuan lebih lanjut.