06. Menghapus Chirp
Terkadang mengedit saja tidak cukup untuk memperbaiki sebuah pesan, jadi mari kita beri kemampuan kepada pengguna untuk menghapus Chirp mereka.
Semoga Anda sudah mulai terbiasa sekarang. Kami yakin Anda akan terkesan betapa cepatnya kita bisa menambahkan fitur ini.
Routing
Kita akan mulai lagi dengan memperbarui rute kita untuk mengaktifkan rute chirps.destroy:
<?php
use App\Http\Controllers\ChirpController; use App\Http\Controllers\ProfileController; use Illuminate\Foundation\Application; use Illuminate\Support\Facades\Route; use Inertia\Inertia; Route::get('/', function () { return Inertia::render('Welcome', [ 'canLogin' => Route::has('login'), 'canRegister' => Route::has('register'), 'laravelVersion' => Application::VERSION, 'phpVersion' => PHP_VERSION, ]); }); Route::get('/dashboard', function () { return Inertia::render('Dashboard'); })->middleware(['auth', 'verified'])->name('dashboard'); Route::middleware('auth')->group(function () { Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit'); Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update'); Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy'); }); Route::resource('chirps', ChirpController::class)- ->only(['index', 'store', 'update'])+ ->only(['index', 'store', 'update', 'destroy']) ->middleware(['auth', 'verified']);
require __DIR__.'/auth.php';
Tabel rute untuk controller ini sekarang terlihat seperti ini:
| Verb | URI | Action | Nama Rute |
|---|---|---|---|
| GET | /chirps |
index | chirps.index |
| POST | /chirps |
store | chirps.store |
| PUT/PATCH | /chirps/{chirp} |
update | chirps.update |
| DELETE | /chirps/{chirp} |
destroy | chirps.destroy |
Memperbarui controller
Sekarang kita dapat memperbarui metode destroy pada kelas ChirpController untuk melakukan penghapusan dan kembali ke indeks Chirp:
<?php
namespace App\Http\Controllers; use App\Models\Chirp; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Gate; use Inertia\Inertia; use Inertia\Response; class ChirpController extends Controller {
/** * Menampilkan daftar resource. */ public function index(): Response { return Inertia::render('Chirps/Index', [ 'chirps' => Chirp::with('user:id,name')->latest()->get(), ]); } /** * Menampilkan formulir untuk membuat resource baru. */ public function create() { // } /** * Menyimpan resource yang baru dibuat ke dalam penyimpanan. */ public function store(Request $request): RedirectResponse { $validated = $request->validate([ 'message' => 'required|string|max:255', ]); $request->user()->chirps()->create($validated); return redirect(route('chirps.index')); } /** * Menampilkan resource yang dipilih/spesifik. */ public function show(Chirp $chirp) { // } /** * Menampilkan formulir untuk mengedit resource yang dipilih/spesifik. */ public function edit(Chirp $chirp) { // } /** * Memperbarui resource yang dipilih/spesifik di dalam penyimpanan. */ public function update(Request $request, Chirp $chirp): RedirectResponse { Gate::authorize('update', $chirp); $validated = $request->validate([ 'message' => 'required|string|max:255', ]); $chirp->update($validated); return redirect(route('chirps.index')); } /** * Menghapus resource yang dipilih/spesifik dari penyimpanan. */- public function destroy(Chirp $chirp)+ public function destroy(Chirp $chirp): RedirectResponse {- //+ Gate::authorize('delete', $chirp);+ + $chirp->delete();+ + return redirect(route('chirps.index')); } }
Otorisasi
Sama seperti saat mengedit, kita hanya ingin penulis Chirp yang dapat menghapus Chirp mereka, jadi mari perbarui metode delete pada kelas ChirpPolicy kita:
<?php
namespace App\Policies; use App\Models\Chirp; use App\Models\User; use Illuminate\Auth\Access\HandlesAuthorization; class ChirpPolicy {
use HandlesAuthorization; /** * Tentukan apakah pengguna dapat melihat model apa saja. */ public function viewAny(User $user): bool { // } /** * Tentukan apakah pengguna dapat melihat model yang ditentukan. */ public function view(User $user, Chirp $chirp): bool { // } /** * Tentukan apakah pengguna dapat membuat model. */ public function create(User $user): bool { // } /** * Menentukan apakah pengguna dapat memperbarui model. */ public function update(User $user, Chirp $chirp): bool { return $chirp->user()->is($user); } /** * Menentukan apakah pengguna dapat menghapus model. */ public function delete(User $user, Chirp $chirp): bool {- //+ return $this->update($user, $chirp); }
/** * Menentukan apakah pengguna dapat merestorasi model. */ public function restore(User $user, Chirp $chirp): bool { // } /** * Menentukan apakah pengguna dapat menghapus model secara permanen. */ public function forceDelete(User $user, Chirp $chirp): bool { // } }
Alih-alih mengulang logika dari metode update, kita dapat menentukan logika yang sama dengan memanggil metode update dari metode delete kita. Siapa pun yang berwenang untuk memperbarui Chirp sekarang juga akan berwenang untuk menghapusnya.
Memperbarui komponen
Terakhir, kita dapat menambahkan tombol hapus ke menu dropdown yang kita buat sebelumnya di komponen Chirp kita:
<script setup> import Dropdown from '@/Components/Dropdown.vue';+import DropdownLink from '@/Components/DropdownLink.vue'; import InputError from '@/Components/InputError.vue'; import PrimaryButton from '@/Components/PrimaryButton.vue'; import dayjs from 'dayjs'; import relativeTime from 'dayjs/plugin/relativeTime'; import { useForm } from '@inertiajs/vue3'; import { ref } from 'vue';
dayjs.extend(relativeTime); const props = defineProps(['chirp']); const form = useForm({ message: props.chirp.message, }); const editing = ref(false); </script> <template> <div class="p-6 flex space-x-2"> <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-gray-600 -scale-x-100" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"> <path stroke-linecap="round" stroke-linejoin="round" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" /> </svg> <div class="flex-1"> <div class="flex justify-between items-center"> <div> <span class="text-gray-800">{{ chirp.user.name }}</span> <small class="ml-2 text-sm text-gray-600">{{ dayjs(chirp.created_at).fromNow() }}</small> <small v-if="chirp.created_at !== chirp.updated_at" class="text-sm text-gray-600"> · edited</small> </div> <Dropdown v-if="chirp.user.id === $page.props.auth.user.id"> <template #trigger> <button> <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-gray-400" viewBox="0 0 20 20" fill="currentColor"> <path d="M6 10a2 2 0 11-4 0 2 2 0 014 0zM12 10a2 2 0 11-4 0 2 2 0 014 0zM16 12a2 2 0 100-4 2 2 0 000 4z" /> </svg> </button> </template> <template #content> <button class="block w-full px-4 py-2 text-left text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:bg-gray-100 transition duration-150 ease-in-out" @click="editing = true"> Edit </button>+ <DropdownLink as="button" :href="route('chirps.destroy', chirp.id)" method="delete">+ Delete+ </DropdownLink> </template> </Dropdown> </div> <form v-if="editing" @submit.prevent="form.put(route('chirps.update', chirp.id), { onSuccess: () => editing = false })"> <textarea v-model="form.message" class="mt-4 w-full text-gray-900 border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 rounded-md shadow-sm"></textarea> <InputError :message="form.errors.message" class="mt-2" /> <div class="space-x-2"> <PrimaryButton class="mt-4">Save</PrimaryButton> <button class="mt-4" @click="editing = false; form.reset(); form.clearErrors()">Cancel</button> </div> </form> <p v-else class="mt-4 text-lg text-gray-900">{{ chirp.message }}</p> </div> </div> </template>
import React, { useState } from 'react'; import Dropdown from '@/Components/Dropdown'; import InputError from '@/Components/InputError'; import PrimaryButton from '@/Components/PrimaryButton'; import dayjs from 'dayjs'; import relativeTime from 'dayjs/plugin/relativeTime'; import { useForm, usePage } from '@inertiajs/react'; dayjs.extend(relativeTime); export default function Chirp({ chirp }) { const { auth } = usePage().props; const [editing, setEditing] = useState(false); const { data, setData, patch, processing, reset, errors } = useForm({ message: chirp.message, }); const submit = (e) => { e.preventDefault(); patch(route('chirps.update', chirp.id), { onSuccess: () => setEditing(false) }); }; return ( <div className="p-6 flex space-x-2"> <svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6 text-gray-600 -scale-x-100" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2"> <path strokeLinecap="round" strokeLinejoin="round" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" /> </svg> <div className="flex-1"> <div className="flex justify-between items-center"> <div> <span className="text-gray-800">{chirp.user.name}</span> <small className="ml-2 text-sm text-gray-600">{dayjs(chirp.created_at).fromNow()}</small> { chirp.created_at !== chirp.updated_at && <small className="text-sm text-gray-600"> · edited</small>} </div> {chirp.user.id === auth.user.id && <Dropdown> <Dropdown.Trigger> <button> <svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 text-gray-400" viewBox="0 0 20 20" fill="currentColor"> <path d="M6 10a2 2 0 11-4 0 2 2 0 014 0zM12 10a2 2 0 11-4 0 2 2 0 014 0zM16 12a2 2 0 100-4 2 2 0 000 4z" /> </svg> </button> </Dropdown.Trigger> <Dropdown.Content> <button className="block w-full px-4 py-2 text-left text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:bg-gray-100 transition duration-150 ease-in-out" onClick={() => setEditing(true)}> Edit </button>+ <Dropdown.Link as="button" href={route('chirps.destroy', chirp.id)} method="delete">+ Delete+ </Dropdown.Link> </Dropdown.Content> </Dropdown> } </div> {editing ? <form onSubmit={submit}> <textarea value={data.message} onChange={e => setData('message', e.target.value)} className="mt-4 w-full text-gray-900 border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 rounded-md shadow-sm"></textarea> <InputError message={errors.message} className="mt-2" /> <div className="space-x-2"> <PrimaryButton className="mt-4">Save</PrimaryButton> <button className="mt-4" onClick={() => setEditing(false) && reset()}>Cancel</button> </div> </form> : <p className="mt-4 text-lg text-gray-900">{chirp.message}</p> } </div> </div> ) }
Coba sekarang
Jika Anda membuat Chirp yang tidak Anda sukai, cobalah untuk menghapusnya!