BAB 31: Edit dan Update Data
Membangun form edit dan menyimpan perubahan data ke database menggunakan Laravel.
Halaman detail di bab sebelumnya sudah memperlihatkan isi catatan secara lengkap, tapi belum ada cara untuk mengubahnya. Begitu catatan tersimpan, isinya terkunci — tidak ada tombol edit, tidak ada form untuk koreksi. Bab ini melengkapi kekurangan itu.
Membangun fitur edit di Laravel mengikuti pola yang sudah familiar dari fitur create: ada dua route (satu untuk menampilkan form, satu untuk memproses pengiriman), dua method di controller, dan satu view baru. Bedanya ada pada bagaimana kita mengisi form dengan data yang sudah ada, dan bagaimana HTTP method PUT digunakan untuk operasi update.
Dua Route Baru
Tambahkan dua route ke routes/web.php, tepat di bawah route show:
// routes/web.php
<?php
use App\Http\Controllers\CatatanController;
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('welcome');
});
Route::get('/catatan', [CatatanController::class, 'index'])->name('catatan.index');
Route::get('/catatan/create', [CatatanController::class, 'create'])->name('catatan.create');
Route::post('/catatan', [CatatanController::class, 'store'])->name('catatan.store');
Route::get('/catatan/{catatan}', [CatatanController::class, 'show'])->name('catatan.show');
Route::get('/catatan/{catatan}/edit', [CatatanController::class, 'edit'])->name('catatan.edit');
Route::put('/catatan/{catatan}', [CatatanController::class, 'update'])->name('catatan.update');
Route edit menggunakan method GET — tugasnya hanya menampilkan form berisi data yang sudah ada. Route update menggunakan method PUT — tugasnya memproses perubahan dan menyimpan ke database.
Secara konvensi REST, PUT digunakan untuk operasi yang menggantikan keseluruhan resource. Browser HTML hanya mendukung GET dan POST, sehingga Laravel menyediakan cara khusus untuk mengirim PUT dari form — ini akan dibahas di bagian view.
Method edit dan update di Controller
Tambahkan dua method baru ke app/Http/Controllers/CatatanController.php:
// app/Http/Controllers/CatatanController.php
<?php
namespace App\Http\Controllers;
use App\Models\Catatan;
use Illuminate\Http\Request;
class CatatanController extends Controller
{
public function index()
{
$catatans = Catatan::latest()->get();
return view('catatan.index', compact('catatans'));
}
public function create()
{
return view('catatan.create');
}
public function store(Request $request)
{
$validated = $request->validate([
'judul' => 'required|string|max:255',
'isi' => 'required|string',
'prioritas' => 'required|in:rendah,sedang,tinggi',
]);
Catatan::create($validated);
return redirect()->route('catatan.index')
->with('success', 'Catatan berhasil disimpan.');
}
public function show(Catatan $catatan)
{
return view('catatan.show', compact('catatan'));
}
public function edit(Catatan $catatan)
{
return view('catatan.edit', compact('catatan'));
}
public function update(Request $request, Catatan $catatan)
{
$validated = $request->validate([
'judul' => 'required|string|max:255',
'isi' => 'required|string',
'prioritas' => 'required|in:rendah,sedang,tinggi',
]);
$catatan->update($validated);
return redirect()->route('catatan.show', $catatan)
->with('success', 'Catatan berhasil diperbarui.');
}
}
Method edit hampir identik dengan show — menerima objek $catatan lewat route model binding, lalu mengirimkannya ke view. Perbedaannya hanya pada view yang dipanggil: catatan.edit bukan catatan.show.
Method update menerima dua parameter: $request untuk data yang dikirim dari form, dan $catatan untuk record yang akan diperbarui. Setelah validasi, $catatan->update($validated) menjalankan query UPDATE ke database dengan nilai-nilai baru. Bedanya dengan create yang menghasilkan record baru, update memodifikasi record yang sudah ada — Laravel tahu record mana yang harus diubah karena $catatan sudah berisi objek dengan ID yang spesifik.
Setelah update berhasil, pengguna diarahkan ke halaman detail catatan yang baru saja diedit, bukan ke halaman listing. Ini pilihan UX yang lebih masuk akal — pengguna bisa langsung memverifikasi bahwa perubahannya tersimpan.
View Form Edit
Buat file baru resources/views/catatan/edit.blade.php:
{{-- resources/views/catatan/edit.blade.php --}}
@extends('layouts.app')
@section('title', 'Edit: ' . $catatan->judul)
@section('content')
<div class="flex items-center justify-between mb-6">
<h1 class="text-2xl font-bold text-gray-900">Edit Catatan</h1>
<a href="{{ route('catatan.show', $catatan) }}" class="text-sm text-gray-500 hover:text-gray-700">
Batal
</a>
</div>
<div class="bg-white rounded border border-gray-200 p-6">
<form action="{{ route('catatan.update', $catatan) }}" method="POST">
@csrf
@method('PUT')
<div class="mb-4">
<label for="judul" class="block text-sm font-medium text-gray-700 mb-1">
Judul
</label>
<input
type="text"
id="judul"
name="judul"
value="{{ old('judul', $catatan->judul) }}"
class="w-full border border-gray-300 rounded px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 @error('judul') border-red-400 @enderror"
placeholder="Judul catatan"
>
@error('judul')
<p class="mt-1 text-xs text-red-600">{{ $message }}</p>
@enderror
</div>
<div class="mb-4">
<label for="isi" class="block text-sm font-medium text-gray-700 mb-1">
Isi
</label>
<textarea
id="isi"
name="isi"
rows="5"
class="w-full border border-gray-300 rounded px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 @error('isi') border-red-400 @enderror"
placeholder="Tulis isi catatan di sini..."
>{{ old('isi', $catatan->isi) }}</textarea>
@error('isi')
<p class="mt-1 text-xs text-red-600">{{ $message }}</p>
@enderror
</div>
<div class="mb-6">
<label for="prioritas" class="block text-sm font-medium text-gray-700 mb-1">
Prioritas
</label>
<select
id="prioritas"
name="prioritas"
class="w-full border border-gray-300 rounded px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 @error('prioritas') border-red-400 @enderror"
>
<option value="">Pilih prioritas</option>
<option value="rendah" {{ old('prioritas', $catatan->prioritas) === 'rendah' ? 'selected' : '' }}>Rendah</option>
<option value="sedang" {{ old('prioritas', $catatan->prioritas) === 'sedang' ? 'selected' : '' }}>Sedang</option>
<option value="tinggi" {{ old('prioritas', $catatan->prioritas) === 'tinggi' ? 'selected' : '' }}>Tinggi</option>
</select>
@error('prioritas')
<p class="mt-1 text-xs text-red-600">{{ $message }}</p>
@enderror
</div>
<button
type="submit"
class="bg-blue-600 text-white px-4 py-2 rounded text-sm font-medium hover:bg-blue-700"
>
Simpan Perubahan
</button>
</form>
</div>
@endsection
Ada tiga hal yang membedakan view ini dari create.blade.php.
Pertama, @method('PUT') di dalam form. Directive ini menghasilkan hidden input <input type="hidden" name="_method" value="PUT"> yang memberitahu Laravel bahwa request ini seharusnya ditangkap oleh route PUT, meskipun browser mengirimnya sebagai POST. Ini adalah teknik standar yang disebut method spoofing.
Kedua, argumen kedua di old() — misalnya old('judul', $catatan->judul). Di form create, old('judul') cukup karena saat pertama kali form dibuka, belum ada nilai yang perlu ditampilkan. Di form edit, form harus sudah terisi dengan data yang ada. Argumen kedua old() adalah fallback: jika tidak ada nilai lama dari sesi (artinya form dibuka pertama kali, bukan setelah validasi gagal), gunakan nilai dari $catatan->judul. Jika ada nilai lama (artinya form baru saja disubmit tapi validasi gagal), gunakan nilai lama itu agar koreksi yang sudah diketik tidak hilang.
Ketiga, pada <select> prioritas, kondisi selected menggunakan old('prioritas', $catatan->prioritas) dengan logika yang sama — fallback ke nilai yang tersimpan di database jika form baru pertama kali dibuka.
old('field', $default) adalah pattern yang sangat berguna di form edit. Selalu sertakan argumen kedua agar form tidak kosong saat pertama kali dibuka.
Tombol Edit di Halaman Detail
Halaman detail juga perlu diperbarui agar ada tombol yang mengarah ke form edit. Buka resources/views/catatan/show.blade.php dan ubah bagian header:
{{-- resources/views/catatan/show.blade.php --}}
@extends('layouts.app')
@section('title', $catatan->judul)
@section('content')
<div class="flex items-center justify-between mb-6">
<h1 class="text-2xl font-bold text-gray-900">{{ $catatan->judul }}</h1>
<div class="flex items-center gap-3">
<a href="{{ route('catatan.edit', $catatan) }}" class="bg-yellow-500 text-white px-3 py-1.5 rounded text-sm font-medium hover:bg-yellow-600">
Edit
</a>
<a href="{{ route('catatan.index') }}" class="text-sm text-gray-500 hover:text-gray-700">
Kembali ke daftar
</a>
</div>
</div>
<div class="bg-white rounded border border-gray-200 p-6">
<div class="mb-4">
<span class="inline-block bg-gray-100 text-gray-700 text-xs font-medium px-2 py-1 rounded capitalize">
Prioritas: {{ $catatan->prioritas }}
</span>
<span class="ml-2 text-xs text-gray-400">
{{ $catatan->created_at->format('d M Y, H:i') }}
</span>
</div>
<div class="text-gray-700 text-sm leading-relaxed whitespace-pre-wrap">
{{ $catatan->isi }}
</div>
</div>
@endsection
Tombol Edit ditempatkan di sebelah kiri link “Kembali ke daftar” dalam satu grup flex. Warna kuning dipilih untuk membedakannya secara visual dari tombol biru yang digunakan untuk aksi create — ini konvensi umum: biru untuk create, kuning/oranye untuk edit.
Menguji Fitur Edit
Buka halaman detail salah satu catatan di http://localhost:8000/catatan/1. Sekarang ada tombol Edit berwarna kuning di pojok kanan atas.
Klik tombol tersebut. Form terbuka di URL /catatan/1/edit dengan field-field yang sudah terisi data catatan — judul, isi, dan prioritasnya sudah ada, tidak perlu diketik ulang dari nol.
Ubah salah satu field, lalu klik “Simpan Perubahan”. Laravel memvalidasi input, menjalankan UPDATE ke database, lalu mengarahkan kembali ke halaman detail dengan pesan sukses.
Jika form edit mengirimkan data tapi tidak ada perubahan yang tersimpan, periksa apakah kolom-kolom yang ingin diupdate sudah ada di array $fillable model Catatan. Kolom yang tidak ada di $fillable akan diabaikan oleh update().
Fitur edit sudah berfungsi. Aplikasi manajemen catatan kini bisa membuat, menampilkan, dan mengubah data. Satu kemampuan yang masih kurang adalah menghapus catatan yang sudah tidak diperlukan — dan itulah yang akan dibangun di bab berikutnya.