
Part: 2 Tutorial Laravel UI - Lanjutan CRUD
Langkah-Langkah Lanjutan Tutorial Laravel UI - CRUD Users
Langkah 12: Update UserController dengan Method CRUD
Edit Admin/UserController.php:
<?php namespace App\Http\Controllers\Admin; use App\Http\Controllers\Controller; use App\Models\Role; use App\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; use Illuminate\Validation\Rule; class UserController extends Controller { public function index() { $users = User::with('role')->paginate(10); return view('users.index', compact('users')); } public function create() { $roles = Role::all(); return view('users.create', compact('roles')); } public function store(Request $request) { $request->validate([ 'name' => 'required|string|max:255', 'email' => 'required|string|email|max:255|unique:users', 'password' => 'required|string|min:8|confirmed', 'role_id' => 'required|exists:roles,id', ], [ 'name.required' => 'Nama wajib diisi.', 'name.max' => 'Nama maksimal 255 karakter.', 'email.required' => 'Email wajib diisi.', 'email.email' => 'Format email tidak valid.', 'email.unique' => 'Email sudah digunakan.', 'password.required' => 'Password wajib diisi.', 'password.min' => 'Password minimal 8 karakter.', 'password.confirmed' => 'Konfirmasi password tidak cocok.', 'role_id.required' => 'Role wajib dipilih.', 'role_id.exists' => 'Role yang dipilih tidak valid.', ]); User::create([ 'name' => $request->name, 'email' => $request->email, 'password' => Hash::make($request->password), 'role_id' => $request->role_id, ]); return redirect()->route('users.index') ->with('success', 'User berhasil ditambahkan.'); } public function show(User $user) { return view('users.show', compact('user')); } public function edit(User $user) { $roles = Role::all(); return view('users.edit', compact('user', 'roles')); } public function update(Request $request, User $user) { $request->validate([ 'name' => 'required|string|max:255', 'email' => [ 'required', 'string', 'email', 'max:255', Rule::unique('users')->ignore($user->id), ], 'password' => 'nullable|string|min:8|confirmed', 'role_id' => 'required|exists:roles,id', ], [ 'name.required' => 'Nama wajib diisi.', 'name.max' => 'Nama maksimal 255 karakter.', 'email.required' => 'Email wajib diisi.', 'email.email' => 'Format email tidak valid.', 'email.unique' => 'Email sudah digunakan.', 'password.min' => 'Password minimal 8 karakter.', 'password.confirmed' => 'Konfirmasi password tidak cocok.', 'role_id.required' => 'Role wajib dipilih.', 'role_id.exists' => 'Role yang dipilih tidak valid.', ]); $userData = [ 'name' => $request->name, 'email' => $request->email, 'role_id' => $request->role_id, ]; // Update password hanya jika diisi if ($request->filled('password')) { $userData['password'] = Hash::make($request->password); } $user->update($userData); return redirect()->route('users.index') ->with('success', 'User berhasil diperbarui.'); } public function destroy(User $user) { $user->delete(); return redirect()->route('users.index')->with('success', 'User berhasil dihapus'); } }
Langkah 13: Update View Users Index dengan Tombol Tambah
Edit resources/views/users/index.blade.php:
@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-12"> <div class="card"> <div class="card-header d-flex justify-content-between align-items-center"> <h4 class="mb-0">Manajemen Users</h4> <a href="{{ route('users.create') }}" class="btn btn-primary"> <i class="fas fa-plus"></i> Tambah User </a> </div> <div class="card-body"> @if(session('success')) <div class="alert alert-success alert-dismissible fade show" role="alert"> {{ session('success') }} <button type="button" class="btn-close" data-bs-dismiss="alert"></button> </div> @endif @if(session('error')) <div class="alert alert-danger alert-dismissible fade show" role="alert"> {{ session('error') }} <button type="button" class="btn-close" data-bs-dismiss="alert"></button> </div> @endif <div class="table-responsive"> <table class="table table-striped table-hover"> <thead class="table-dark"> <tr> <th>ID</th> <th>Nama</th> <th>Email</th> <th>Role</th> <th>Tanggal Daftar</th> <th>Aksi</th> </tr> </thead> <tbody> @forelse($users as $user) <tr> <td>{{ $user->id }}</td> <td>{{ $user->name }}</td> <td>{{ $user->email }}</td> <td> <span class="badge bg-{{ $user->isAdmin() ? 'danger' : 'primary' }}"> {{ $user->role->name ?? 'No Role' }} </span> </td> <td>{{ $user->created_at->format('d/m/Y H:i') }}</td> <td> <div class="btn-group" role="group"> <a href="{{ route('users.show', $user) }}" class="btn btn-sm btn-info"> <i class="fas fa-eye"></i> Detail </a> <a href="{{ route('users.edit', $user) }}" class="btn btn-sm btn-warning"> <i class="fas fa-edit"></i> Edit </a> @if($user->id !== auth()->id()) <form action="{{ route('users.destroy', $user) }}" method="POST" class="d-inline"> @csrf @method('DELETE') <button type="submit" class="btn btn-sm btn-danger" onclick="return confirm('Yakin ingin menghapus user {{ $user->name }}?')"> <i class="fas fa-trash"></i> Hapus </button> </form> @endif </div> </td> </tr> @empty <tr> <td colspan="6" class="text-center">Tidak ada data user.</td> </tr> @endforelse </tbody> </table> </div> <div class="d-flex justify-content-center"> {{ $users->links() }} </div> </div> </div> </div> </div> </div> @endsection
Buat file resources/views/users/create.blade.php:
php artisan make:view users.create
@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header"> <h4 class="mb-0">Tambah User Baru</h4> </div> <div class="card-body"> <form method="POST" action="{{ route('users.store') }}"> @csrf <div class="row mb-3"> <label for="name" class="col-md-4 col-form-label text-md-end">{{ __('Nama') }}</label> <div class="col-md-6"> <input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus> @error('name') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="row mb-3"> <label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email') }}</label> <div class="col-md-6"> <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email"> @error('email') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="row mb-3"> <label for="role_id" class="col-md-4 col-form-label text-md-end">{{ __('Role') }}</label> <div class="col-md-6"> <select name="role_id" id="role_id" class="form-select @error('role_id') is-invalid @enderror" required> <option value="">Pilih Role</option> @foreach($roles as $role) <option value="{{ $role->id }}" {{ old('role_id') == $role->id ? 'selected' : '' }}> {{ $role->name }} </option> @endforeach </select> @error('role_id') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="row mb-3"> <label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label> <div class="col-md-6"> <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password"> @error('password') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="row mb-3"> <label for="password-confirm" class="col-md-4 col-form-label text-md-end">{{ __('Konfirmasi Password') }}</label> <div class="col-md-6"> <input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password"> </div> </div> <div class="row mb-0"> <div class="col-md-6 offset-md-4"> <button type="submit" class="btn btn-primary"> <i class="fas fa-save"></i> {{ __('Simpan') }} </button> <a href="{{ route('users.index') }}" class="btn btn-secondary"> <i class="fas fa-arrow-left"></i> {{ __('Kembali') }} </a> </div> </div> </form> </div> </div> </div> </div> </div> @endsection
Langkah 15: Buat View Edit User
Buat file resources/views/users/edit.blade.php:
php artisan make:view users.edit
@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header"> <h4 class="mb-0">Edit User: {{ $user->name }}</h4> </div> <div class="card-body"> <form method="POST" action="{{ route('users.update', $user) }}"> @csrf @method('PUT') <div class="row mb-3"> <label for="name" class="col-md-4 col-form-label text-md-end">{{ __('Nama') }}</label> <div class="col-md-6"> <input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name', $user->name) }}" required autocomplete="name" autofocus> @error('name') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="row mb-3"> <label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email') }}</label> <div class="col-md-6"> <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email', $user->email) }}" required autocomplete="email"> @error('email') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="row mb-3"> <label for="role_id" class="col-md-4 col-form-label text-md-end">{{ __('Role') }}</label> <div class="col-md-6"> <select name="role_id" id="role_id" class="form-select @error('role_id') is-invalid @enderror" required> <option value="">Pilih Role</option> @foreach($roles as $role) <option value="{{ $role->id }}" {{ old('role_id', $user->role_id) == $role->id ? 'selected' : '' }}> {{ $role->name }} </option> @endforeach </select> @error('role_id') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="row mb-3"> <label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password Baru') }}</label> <div class="col-md-6"> <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" autocomplete="new-password"> <small class="form-text text-muted">Kosongkan jika tidak ingin mengubah password</small> @error('password') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="row mb-3"> <label for="password-confirm" class="col-md-4 col-form-label text-md-end">{{ __('Konfirmasi Password') }}</label> <div class="col-md-6"> <input id="password-confirm" type="password" class="form-control" name="password_confirmation" autocomplete="new-password"> </div> </div> <div class="row mb-0"> <div class="col-md-6 offset-md-4"> <button type="submit" class="btn btn-primary"> <i class="fas fa-save"></i> {{ __('Update') }} </button> <a href="{{ route('users.index') }}" class="btn btn-secondary"> <i class="fas fa-arrow-left"></i> {{ __('Kembali') }} </a> </div> </div> </form> </div> </div> </div> </div> </div> @endsection
Langkah 16: Buat View Show User (Detail)
Buat file resources/views/users/show.blade.php:
php artisan make:view users.show
@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header d-flex justify-content-between align-items-center"> <h4 class="mb-0">Detail User</h4> <div> <a href="{{ route('users.edit', $user) }}" class="btn btn-warning btn-sm"> <i class="fas fa-edit"></i> Edit </a> <a href="{{ route('users.index') }}" class="btn btn-secondary btn-sm"> <i class="fas fa-arrow-left"></i> Kembali </a> </div> </div> <div class="card-body"> <div class="row mb-3"> <div class="col-md-3"><strong>ID:</strong></div> <div class="col-md-9">{{ $user->id }}</div> </div> <div class="row mb-3"> <div class="col-md-3"><strong>Nama:</strong></div> <div class="col-md-9">{{ $user->name }}</div> </div> <div class="row mb-3"> <div class="col-md-3"><strong>Email:</strong></div> <div class="col-md-9">{{ $user->email }}</div> </div> <div class="row mb-3"> <div class="col-md-3"><strong>Role:</strong></div> <div class="col-md-9"> <span class="badge bg-{{ $user->isAdmin() ? 'danger' : 'primary' }} fs-6"> {{ $user->role->name ?? 'No Role' }} </span> </div> </div> <div class="row mb-3"> <div class="col-md-3"><strong>Tanggal Daftar:</strong></div> <div class="col-md-9">{{ $user->created_at->format('d F Y, H:i') }} WIB</div> </div> <div class="row mb-3"> <div class="col-md-3"><strong>Terakhir Update:</strong></div> <div class="col-md-9">{{ $user->updated_at->format('d F Y, H:i') }} WIB</div> </div> <div class="row mb-3"> <div class="col-md-3"><strong>Email Verified:</strong></div> <div class="col-md-9"> @if($user->email_verified_at) <span class="badge bg-success">Terverifikasi</span> <small class="text-muted">({{ $user->email_verified_at->format('d/m/Y H:i') }})</small> @else <span class="badge bg-warning">Belum Terverifikasi</span> @endif </div> </div> </div> </div> </div> </div> </div> @endsection
Langkah 17: Tambahkan Font Awesome (Opsional)
Untuk menampilkan icon, tambahkan Font Awesome di resources/views/layouts/app.blade.php:
<!-- Fonts --> <link rel="dns-prefetch" href="//fonts.bunny.net"> <link href="https://fonts.bunny.net/css?family=Nunito" rel="stylesheet"> <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet"> <!-- Scripts --> @vite(['resources/sass/app.scss', 'resources/js/app.js'])
Langkah 18: Testing Aplikasi
-
Jalankan Server:
php artisan serve -
Test Login sebagai Admin:
- Login dengan akun admin
- Akses
/adminuntuk melihat dashboard admin - Klik "Kelola User" untuk masuk ke halaman manajemen user
-
Test CRUD Operations:
- Create: Klik tombol "Tambah User" dan isi form
- Read: Lihat daftar users di halaman index
- Update: Klik tombol "Edit" pada user tertentu
- Delete: Klik tombol "Hapus" pada user (kecuali diri sendiri)
- Show: Klik tombol "Detail" untuk melihat detail user
-
Test Validasi:
- Coba submit form kosong
- Coba gunakan email yang sudah ada
- Coba password kurang dari 8 karakter
- Coba password confirmation yang tidak cocok
Penutup
Sekarang Anda telah berhasil membuat sistem manajemen user dengan CRUD lengkap yang memiliki:
- ✅ Authentication & Authorization
- ✅ Role-based Access Control
- ✅ Form Validation dengan pesan error dalam Bahasa Indonesia
- ✅ CRUD Operations (Create, Read, Update, Delete)
- ✅ UI yang responsive dengan Bootstrap
- ✅ Flash Messages untuk feedback user
- ✅ Pagination untuk daftar user
- ✅ Icon menggunakan Font Awesome
Aplikasi ini siap untuk dikembangkan lebih lanjut dengan fitur-fitur tambahan sesuai kebutuhan proyek Anda.