Tutorial CRUD Laravel 9 dengan Jetstream Livewire

Tutorial CRUD Laravel 9 dengan Jetstream Livewire

id13 min read • 6699 views

CRUD Jetstream Livewire - Hi, Dev 👨‍💻 👋 Pada artikel ini, saya akan share tutorial membuat content management system sederhana yang terdiri dari fitur basic CRUD atau Create, Read, Update dan Delete menggunakan laravel 9 dan jetstream livewire. Laravel yang akan saya gunakan pada kali ini yaitu laravel versi 9. Namun hal itu tidak akan terlalu berdampak, karena disini nanti kita akan lebih banyak bekerja dengan livewire. Akan ada 7 step yang akan saya jelaskan secara berurutan, antara lain;

  1. Install laravel via composer
  2. Membuat database dan set up environment
  3. Install jetstream livewire
  4. Generate model dan migration
  5. Set up model dan migration
  6. Generate livewire component
  7. Set up livewire component.

Baiklah, mari kita langsung saja ke step-stepnya 🚀

Step 1: Install Laravel

composer create-project laravel/laravel crud-livewire

Langkah pertama yang harus kita lakukan tentu saja menginstall laravel. Kali ini saya akan memberikan contoh untuk menginstall laravel via composer. Jalankan perintah seperti di atas pada terminal kalian. Jika kamu sebelumnya sudah menggunakan laravel installer, kamu bisa langsung saja menginstall laravel menggunakan laravel new crud-livewire.

Step 2: Set up Database

Langkah selanjutnya yaitu membuat database. Silahkan buat database baru untuk menampung data-data kita nantinya.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=crud-livewire
DB_USERNAME=root
DB_PASSWORD=

Jika sudah selesai membuat database, jangan lupa untuk menyesuaikannya pada file .env seperti contoh di atas.

Step 3: Install Jetstream

composer require laravel/jetstream
php artisan jetstream:install livewire

Kemudian pada langkah ketiga, kita perlu menginstall jetstream sekaligus livewire. Silahkan jalankan perintah-perintah seperti di atas secara berurutan.

npm install && npm run dev

Jika proses install jetstream dan jetsream livewire sudah selesai, silahkan jalankan perintah seperti di atas untuk meng-compile asset-asset kita.

Step 4: Generate Model & Migration

php artisan make:model Post -m

Lanjut ke step ke-empat ini kita akan melakukan generate Post model dan migration. Jalankan perintah seperti di atas untuk membuat Post model sekaligus migrationnya.

public function up()
{
    Schema::create('posts', function (Blueprint $table) {
        $table->id();
        $table->foreignId('user_id')->constrained();
        $table->string('title');
        $table->string('slug');
        $table->longText('body');
        $table->timestamps();
    });
}

Sudah selesai menjalankan perintah sebelumnya? jika sudah, lanjutkan untuk melakukan set up post migration. Buka file post migration yang terletak di database/migrations/{timestamp} _create_posts_table.php dan tambahkan kode pada method up seperti code di atas.

php artisan migrate

Selanjutnya jika sudah selesai melakukan set up post migration, lanjutkan dengan menjalankan perintah php artisan migrate untuk memigrasi file-file migration ke database.

Step 5: Set up Model

Models/User.php

public function posts()
{
    return $this->hasMany(Post::class);
}

Oke, sekarang waktunya kita untuk bekerja pada model. Pada step ini kita akan menghubungkan antara Post dengan User Model. Jika kamu perhatikan, pada post migration tadi, kita membuat foreign key untuk user_id. Hal ini bertujuan agar memudahkan kita untuk memanggil data relasi. Jadi skenarionya, saat kita membuat post baru, ada yang juga terinput meskipun kita tidak menginputknya. data tersebut yaitu user_id, yang mengambil dari id user yang melakukan aktivitas create data tersebut.

Silahkan buka file User.php, kemudian tambahkan code seperti di atas.

Models/Post.php

protected $guarded = [];

public function user()
{
    return $this->belongsTo(User::class);
}

Kemudian buka file Post.php, tambahkan method protected $guarded = [] agar data-data yang kita inputkan nantinya dapat diterima dan ditambahkan pada table posts di database. Selain itu, kita juga perlu menambahkan relasi ke class User.

Step 6: Generate Livewire Component

php artisan make:livewire Posts

Nah, pada step ke-enam ini, kita akan bekerja dengan livewire. Pertama, kita akan membuat livewire component yang akan kita kasih nama Posts. Silahkan jalankan perintah seperti di atas, untuk membuat livewire component tersebut.

use App\Http\Livewire\Posts;
....
....
....
Route::middleware(['auth:sanctum', 'verified'])->group(function(){
    Route::get('/dashboard', function () {
        return view('dashboard');
    })->name('dashboard');
    Route::get('posts',Posts::class)->name('posts');
});

Selanjutnya, kita tambahkan route baru di dalam middleware auth seperti contoh code di atas. Dan jangan lupa untuk menambahkan lokasi Posts class.

Step 7: Set up Livewire Component

<?php

namespace App\Http\Livewire;

use App\Models\Post;
use Livewire\Component;

class Posts extends Component
{
    public $title, $body, $post_id;
    
    public $isModalOpen = false;

    public function create()
    {
        $this->resetCreateForm();
        $this->openModal();
    }

    public function openModal()
    {
        $this->isModalOpen = true;
    }

    public function closeModal()
    {
        $this->isModalOpen = false;
    }

    private function resetCreateForm(){
        $this->title = '';
        $this->body  = '';
    }
    
    public function save()
    {
        $this->validate([
            'title' => 'required',
            'body'  => 'required',
        ]);

        Post::updateOrCreate(['id' => $this->post_id], [
            'user_id'   => auth()->user()->id,
            'title'     => $this->title,
            'slug'      => str($this->title)->slug(),
            'body'      => $this->body,
        ]);

        session()->flash('message', $this->post_id ? 'Data updated successfully.' : 'Data added successfully.');

        $this->closeModal();

        $this->resetCreateForm();
    }

    public function edit($id)
    {
        $post           = Post::findOrFail($id);
        $this->post_id  = $id;
        $this->title    = $post->title;
        $this->body     = $post->body;
    
        $this->openModal();
    }
    
    public function delete($id)
    {
        Post::find($id)->delete();
        session()->flash('message', 'Data deleted successfully.');
    }

    public function render()
    {
        $posts = Post::latest()->get();
        return view('livewire.posts', compact('posts'));
    }
}

Lanjut ke Posts livewire component. Disini kita akan membuat semua logic yang kita buat pada artikel ini, antara lain: menampilkan modal, menutup modal, create data, edit data dan delete data. Buka file Posts.php yang terletak pada direktori app/Http/Livewire. Kemudian sesuaikan kode yang ada dengan kode seperti di atas. Pada code di atas, kita menambahkan beberapa method dengan fungsinya masing-masing, antara lain:

  • create(), ketika method ini dipanggil, maka akan menjalankan method resetCreateForm() atau mengosongkan semua input field dan memanggil method openModal() atau mengubah value dari variable $isModalOpen dari false menjadi true.
  • save(), merupakan method yang berfungsi untuk menyimpan data baru atau memperbarui data yang sudah ada. Di dalam method ini, untuk data slug, saya menggunakan string helper terbaru dari laravel 9 untuk meng-convert request title menjadi slug.
  • edit($id), merupakan method dengan fungsi untuk menampilkan modal beserta data yang diambil dari table posts dengan parameter post id.
  • delete($id), yang sudah sangat jelas fungsinya yaitu untuk menghapus data dari table posts dengan parameter post id.
  • render(), untuk merender ke file posts.blade.php (livewire component) beserta variabel $posts yang berisikan semua data dari table posts.
<x-slot name="header">
    <h2 class="text-center">Tutorial CRUD Laravel dengan Jetstream Livewire</h2>
</x-slot>
<div class="py-12">
    <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
        <div class="bg-white overflow-hidden shadow-xl sm:rounded-lg px-4 py-4">
            @if (session()->has('message'))
            <div class="py-3">
                <p class="text-sm">{{ session('message') }}</p>
            </div>
            @endif
            <button wire:click="create()" class="bg-indigo-500 text-white font-bold py-2 px-4 rounded my-3">Create Post</button>
            @if($isModalOpen)
            <x-post.form/>
            @endif
            <table class="table-fixed w-full mt-5">
                <thead>
                    <tr class="bg-gray-100">
                        <th class="px-4 py-2 w-20">No.</th>
                        <th class="px-4 py-2">Title</th>
                        <th class="px-4 py-2">Desc</th>
                        <th class="px-4 py-2">Author</th>
                        <th class="px-4 py-2">Action</th>
                    </tr>
                </thead>
                <tbody>
                    @php
                    $no = 0;
                    @endphp
                    @forelse ($posts as $post)
                    <tr>
                        <td class="border px-4 py-2">{{ ++$no }}</td>
                        <td class="border px-4 py-2">{{ $post->title }}</td>
                        <td class="border px-4 py-2">{{ Str::limit($post->body,70) }}</td>
                        <td class="border px-4 py-2">{{ $post->user->name }}</td>
                        <td class="border px-4 py-2">
                            <button wire:click="edit({{ $post->id }})"
                                class="bg-indigo-600 text-white font-bold py-2 px-4 rounded">Edit</button>
                            <button class="bg-red-600 hover:bg-red-700 text-white font-bold py-2 px-4 rounded" wire:click="delete({{ $post->id }})">Delete</button>
                        </td>
                    </tr>
                    @empty
                    <tr>
                        <td colspan="4">
                            <p class="text-center">No Data</p>
                        </td>
                    </tr>
                    @endforelse
                </tbody>
            </table>
        </div>
    </div>
</div>

Lanjut ke layout. Silahkan buka file posts.blade.php yang terletak pada direktori resources/views/livewire, kemudian tambahkan code seperti di atas pada file tersebut. Code di atas merupakan code basic yang dibuat menggunakan tailwindcss untuk membuat table. Di dalam table tersebut, kita menambahkan perulangan menggunakan forelse untuk menampilkan data-data yang diambil dari table posts.

Pada code tersebut, kita juga menambahkan 3 button dengan property wire:model="create()", wire:model="edit($id)" dan wire:model="delete($id)". Ketiga button tersebut memiliki fungsi yang akan dijelaskan seperti di bawah ini:

  1. wire:model="create()": Ketika button create ini diklik, maka akan memanggil method create() di Posts.php dan akan menampilkan modal.
  2. wire:model="edit({{$post->id}})": Merupakan button edit dengan parameter id dari post tersebut. Jika button edit ini diklik, maka akan memanggil method edit() dan akan menampilkan modal serta data dari post id tersebut. 
  3. wire:model="delete({{$post->id}})": 
php artisan make:component Post/Form

Jika kamu perhatikan pada code posts.blade.php, kita juga menyertakan form component, yang mana sebenarnya kita belum mempunyai component tersebut. Untuk itu, mari kita buat form component tersebut dengan menjalankan perintah artisan seperti di atas. Setelah perintah tersebut dijalankan, laravel akan membuatkan kita form component yang terletak di app/View/Components/Post/Form.php dan resources/views/components/post/form.blade.php.

Modal Component

<div class="fixed z-10 inset-0 overflow-y-auto ease-out duration-400">
    <div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
        <div class="fixed inset-0 transition-opacity">
            <div class="absolute inset-0 bg-gray-500 opacity-75"></div>
        </div>
        <span class="hidden sm:inline-block sm:align-middle sm:h-screen"></span>
        <div class="mx-auto inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"
            role="dialog" aria-modal="true" aria-labelledby="modal-headline">
            <form>
                <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
                    <div class="">
                        <div class="mb-4">
                            <label for="title"
                                class="block text-gray-700 text-sm font-bold mb-2">Title</label>
                            <input type="text"
                                class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
                                id="title" placeholder="Enter Title" wire:model="title">
                            @error('title') <span class="text-red-500">{{ $message }}</span>@enderror
                        </div>
                        <div class="mb-4">
                            <label for="body"
                                class="block text-gray-700 text-sm font-bold mb-2">Body</label>
                            <textarea
                                class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
                                id="body" wire:model="body"
                                placeholder="Description"></textarea>
                            @error('body') <span class="text-red-500">{{ $message }}</span>@enderror
                        </div>
                    </div>
                </div>
                <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
                    <span class="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto">
                    <button wire:click.prevent="save()" type="button"
                        class="inline-flex justify-center w-full rounded-md border border-transparent px-4 py-2 text-base leading-6 font-bold text-white shadow-sm hover:bg-red-700 focus:outline-none bg-indigo-500 hover:bg-indigo-600 focus:border-indigo-700 focus:shadow-outline-indigo transition ease-in-out duration-150 sm:text-sm sm:leading-5 mr-2">
                    Save
                    </button>
                    </span>
                    <span class="mt-3 flex w-full rounded-md shadow-sm sm:mt-0 sm:w-auto">
                    <button wire:click="closeModal()" type="button"
                        class="inline-flex justify-center w-full rounded-md border border-gray-300 px-4 py-2 bg-white text-base leading-6 font-bold text-gray-700 shadow-sm hover:text-gray-700 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue transition ease-in-out duration-150 sm:text-sm sm:leading-5">
                    Close
                    </button>
                    </span>
                </div>
            </form>
        </div>
    </div>
</div>

Kita sudah mempunyai form component file, sekarang tugas kita adalah menambahkan code untuk membuat create/edit form modal. Buka file form.blade.php, yang terletak di direktori resources/views/components/post. Kemudian, tambahkan code di atas. Code di atas merupakan code untuk membuat tampilan form basic yang terdiri dari input field title dan textarea body.

Perhatikan code di atas. Pada code tersebut, terdapat property wire:model="title" dan wire:model="body". Property tersebut digunakan untuk data binding atau menyinkronkan antara front end dan back end. Jadi, di livewire kita tidak perlu lagi memerlukan property name dan value. Kemudian, terdapat pula property wire:click pada button. Jika button tersebut diklik, maka akan memanggil method (seperti pada property tersebut) ke class Posts seperti save() dan closeModal().

Testing

CRUD laravel jetstream livewire

Setelah melakukan langkah-langkah di atas, sekarang waktunya kita untuk mengujinya. Silahkan jalankan laravel project dengan menjalankan perintah php artisan serve, kemudian buka browser dengan URL 127.0.0.1:{port} atau crud-livewire.test. Kemudian coba register akun dan buka halaman crud-livewire.test/posts. Selanjutnya coba lakukan untuk tambah data, edit data dan hapus data. Hasil akhir dari apa yang kita lakukan pada artikel ini, bisa dilihat seperti pada gambar di atas. Sampai disini, kita telah berhasil membuat content management system sederhana yang terdiri dari fitur basic CRUD atau Create, Read, Update dan Delete menggunakan livewire.

Saya rasa cukup sekian artikel kali ini. Jika kamu punya kritik, saran, masukan atau apapun itu yang ingin ditanyakan, silahkan tulis komentar kalian pada form komentar di bawah ini. Selamat mencoba, semoga artikel ini dapat membantu dan sampai jumpa pada artikel berikutnya.

Happy coding gaess 👋

Published on February 26, 2022
Last updated on June 03, 2026

If you like this post and want to support us, you can support us via buymeacoffee or trakteer.