Hey, there! 👋🏻 This Bootcamp is still under construction. I'm sharing it publicly because I think it's already in a good enough shape, but here be dragons. If you find anything not working, please let me know and I'll fix it. I feel like the web part is complete, but the Turbo Native side wasn't reviewed yet. Enjoy!

05. Deleting Chirps

Introduction

Sometimes no amount of editing can fix a message, so let's give our users the ability to delete their Chirps.

Hopefully you're starting to get the hang of things now. We think you'll be impressed how quickly we can add this feature.

Routing

Let's update our routes/web.php file to add the new destroy action in our resource definition:

routes/web.php
<?php
 ...
use App\Http\Controllers\ChirpController;
use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;
 
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
 
Route::get('/', function () {
return view('welcome');
});
 
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');
 
Route::resource('chirps', ChirpController::class)
->only(['index', 'create', 'store', 'edit', 'update'])
->only(['index', 'create', 'store', 'edit', 'update', 'destroy'])
->middleware(['auth', 'verified']);
 ...
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');
});
 
require __DIR__.'/auth.php';

However, at this point we can get rid of the ->only() method call. By default, Laravel will register all those resource routes when we're using the resource() route method. The ->only() method is useful when you want to limite to only a few of those routes:

routes/web.php
<?php
 ...
use App\Http\Controllers\ChirpController;
use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;
 
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
 
Route::get('/', function () {
return view('welcome');
});
 
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');
 
Route::resource('chirps', ChirpController::class)
->only(['index', 'create', 'store', 'edit', 'update', 'destroy'])
->middleware(['auth', 'verified']);
 ...
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');
});
 
require __DIR__.'/auth.php';

Our route table for this controller now looks like this:

Verb URI Action Route Name
GET /chirps index chirps.index
GET /chirps/create create chirps.create
POST /chirps store chirps.store
GET /chirps/{chirp}/edit edit chirps.edit
PUT/PATCH /chirps/{chirp} update chirps.update
DELETE /chirps/{chirp} destroy chirps.destroy

Updating the Controller

Now we can update the destroy action on our ChirpController class to perform the deletion and return to the Chirp index:

app/Http/Controllers/ChirpController.php
<?php
 ...
namespace App\Http\Controllers;
 
use App\Models\Chirp;
use Illuminate\Http\Request;
 
class ChirpController extends Controller
{
 ...
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
return view('chirps.index', [
'chirps' => Chirp::with('user:id,name')->latest()->get(),
]);
}
 
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
return view('chirps.create');
}
 
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$validated = $request->validate([
'message' => ['required', 'string', 'max:255'],
]);
 
$request->user()->chirps()->create($validated);
 
return redirect()
->route('chirps.index')
->with('status', __('Chirp created.'));
}
 
/**
* Display the specified resource.
*
* @param \App\Models\Chirp $chirp
* @return \Illuminate\Http\Response
*/
public function show(Chirp $chirp)
{
//
}
 
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Chirp $chirp
* @return \Illuminate\Http\Response
*/
public function edit(Chirp $chirp)
{
//
$this->authorize('update', $chirp);
 
return view('chirps.edit', [
'chirp' => $chirp,
]);
}
 
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param \App\Models\Chirp $chirp
* @return \Illuminate\Http\Response
*/
public function update(Request $request, Chirp $chirp)
{
$this->authorize('update', $chirp);
 
$validated = $request->validate([
'message' => ['required', 'string', 'max:255'],
]);
 
$chirp->update($validated);
 
return redirect()
->route('chirps.index')
->with('status', __('Chirp updated.'));
}
 
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Chirp $chirp
* @return \Illuminate\Http\Response
*/
public function destroy(Chirp $chirp)
{
//
$this->authorize('delete', $chirp);
 
$chirp->delete();
 
return redirect()
->route('chirps.index')
->with('status', __('Chirp deleted.'));
}
 
}

Authorization

As with editing, we only want our Chirp authors to be able to delete their Chirps, so let's update the delete method our ChirpPolicy class:

app/Policies/ChirpPolicy.php
<?php
 ...
namespace App\Policies;
 
use App\Models\Chirp;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
 
class ChirpPolicy
{
 ...
use HandlesAuthorization;
 
/**
* Determine whether the user can view any models.
*
* @param \App\Models\User $user
* @return \Illuminate\Auth\Access\Response|bool
*/
public function viewAny(User $user)
{
//
}
 
/**
* Determine whether the user can view the model.
*
* @param \App\Models\User $user
* @param \App\Models\Chirp $chirp
* @return \Illuminate\Auth\Access\Response|bool
*/
public function view(User $user, Chirp $chirp)
{
//
}
 
/**
* Determine whether the user can create models.
*
* @param \App\Models\User $user
* @return \Illuminate\Auth\Access\Response|bool
*/
public function create(User $user)
{
//
}
 
/**
* Determine whether the user can update the model.
*
* @param \App\Models\User $user
* @param \App\Models\Chirp $chirp
* @return \Illuminate\Auth\Access\Response|bool
*/
public function update(User $user, Chirp $chirp)
{
return $user->is($chirp->user);
}
 
/**
* Determine whether the user can delete the model.
*
* @param \App\Models\User $user
* @param \App\Models\Chirp $chirp
* @return \Illuminate\Auth\Access\Response|bool
*/
public function delete(User $user, Chirp $chirp)
{
//
return $user->is($chirp->user);
}
 ...
/**
* Determine whether the user can restore the model.
*
* @param \App\Models\User $user
* @param \App\Models\Chirp $chirp
* @return \Illuminate\Auth\Access\Response|bool
*/
public function restore(User $user, Chirp $chirp)
{
//
}
 
/**
* Determine whether the user can permanently delete the model.
*
* @param \App\Models\User $user
* @param \App\Models\Chirp $chirp
* @return \Illuminate\Auth\Access\Response|bool
*/
public function forceDelete(User $user, Chirp $chirp)
{
//
}
 
}

Although the logic of authorizing users to update or delete Chirps is pretty much the same for this demo app, chances are you may have different authorization policies in a real app. For that reason, we're leaving them separate.

Updating our Chirp partial

Finally, we can add a delete button to the dropdown menu we created earlier in our chirps._chirp Blade partial:

resources/views/chirps/_chirp.blade.php
<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">
<x-relative-time :date="$chirp->created_at" />
</small>
@unless ($chirp->created_at->eq($chirp->updated_at))
<small class="text-sm text-gray-600"> &middot; edited</small>
@endunless
</div>
 
@if (Auth::id() === $chirp->user->id)
<x-dropdown align="right" width="48">
<x-slot name="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>
</x-slot>
 
<x-slot name="content">
<a href="{{ route('chirps.edit', $chirp) }}" 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">App\
Edit
</a>
<form action="{{ route('chirps.destroy', $chirp) }}" method="POST">
@method('DELETE')
 
<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">
Delete
</button>
</form>
</x-slot>
</x-dropdown>
@endif
</div>
<p class="mt-4 text-lg text-gray-900">{{ $chirp->message }}</p>
</div>
</div>

Testing it out

If you Chirped anything you weren't happy with, try deleting it!

Deleting Chirps

Continue to Hotwiring everything...