How to Use Laravel’s Controller Edit/Update Methods Effectively
We have just a couple of more articles to go before we wrap up this project in its entirety. We’ll move on to more advanced topics after that. In this article, we’ll tackle how to edit a record. We’ll need to display a form with all of our form fields already populated for a particular record and then when we click update, we actually have to update our record.
https://medium.com/geekculture/laravel-p44-form-single-image-upload-cmp-53c519dcd97d
Our Routes
The routes are already created. The edit and update routes will call the edit
and update
methods respectively. The edit
method pulls the record from the table and displays it in a form and the update
method updates the record.
Route::prefix('/personalcars')->group(function() {
Route::get('/{id}/edit', [PersonalCarController::class, 'edit']);
Route::put('/{id}', [PersonalCarController::class, 'update']);
});
Edit Link
In order to get to our edit page, we need to create an edit button (or link). Within our index.blade.php
we’ll add an Edit
link next to our Show
link.
<!-- ... -->
<th scope="col" class="px-6 py-3">
Edit
</th>
<!-- ... -->
<td class="px-6 py-4">
<a href="/personalcars/{{ $car->id }}/edit">
Edit Car
</a>
</td>
<!-- ... -->
The full code for our index.blade.php
file now looks like this.
<x-layouts.app title="{{ $title }}">
<div class="flex bg-white mt-12">
<div class="items-center text-center lg:text-left px-8 md:px-12 lg:w-full">
<div class="relative overflow-x-auto">
@if (session('status'))
<div class="block bg-green-200 p-4">
{{ session('status') }}
</div>
@endif
<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
<tr>
<th scope="col" class="px-6 py-3">
Year
</th>
<th scope="col" class="px-6 py-3">
Make
</th>
<th scope="col" class="px-6 py-3">
Model
</th>
<th scope="col" class="px-6 py-3">
Exterior Color
</th>
<th scope="col" class="px-6 py-3">
Current Value
</th>
<th scope="col" class="px-6 py-3">
View
</th>
<th scope="col" class="px-6 py-3">
Edit
</th>
</tr>
</thead>
<tbody>
@foreach($cars as $car)
<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
<th scope="row" class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
{{ $car->year }}
</th>
<td class="px-6 py-4">
{{ $car->brand->name }}
</td>
<td class="px-6 py-4">
{{ $car->model->name }}
</td>
<td class="px-6 py-4">
{{ $car->exterior_color }}
</td>
<td class="px-6 py-4">
{{ $car->current_value }}
</td>
<td class="px-6 py-4">
<a href="/personalcars/{{ $car->id }}">
Show Car
</a>
</td>
<td class="px-6 py-4">
<a href="/personalcars/{{ $car->id }}/edit">
Edit Car
</a>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
<a href="/personalcars/create">
<button class="block bg-green-400 hover:bg-green-600 text-white uppercase text-lg mx-auto p-4 rounded" type="submit">Add New Car</button>
</a>
</x-layouts.app>
Clicking on our Edit Car
link sends the resource $id
to our edit
method in the PersonalCarController
.
Edit Method
Our edit
method needs to return a pre-filled form. That’s pretty simple. We already know how to get a resource by id
and we already have a form. We can and should use the create.blade.php
form. We’ll just need to make a copy of it and store it in edit.blade.php
.
To get the resource, we’ll use the same PersonalCar::find()
piece of code that we used for our show
method.
<?php
namespace App\Http\Controllers;
use App\Models\Image;
use App\Models\PersonalCar;
use App\Models\PersonalCarBrand;
use App\Models\PersonalCarModel;
use Faker\Provider\Person;
use Illuminate\Http\Request;
class PersonalCarController extends Controller
{
// ...
/**
* Show the form for editing the specified resource.
*
* @param int $id
*/
public function edit($id)
{
$car = PersonalCar::with(['brand', 'model', 'images'])->find($id);
// send data to edit view
// the view injects the data into the form
// the view is returned to the user through the route
}
// ...
}
Next, we need to create our edit.blade.php
file. Remember, we’ll just copy our create.blade.php
form.
<x-layouts.app title="{{ $title }}">
<div class="flex bg-white mt-12">
<div class="flex justify-center items-center w-full">
<div class="w-1/2 bg-white rounded shadow-2xl p-8 m-4">
<h1 class="block w-full text-center text-gray-800 text-2xl font-bold mb-6">Add New Car</h1>
@if ($errors->any())
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
@endif
<form action="/personalcars/" method="post" enctype="multipart/form-data">
@csrf
<div class="flex flex-col mb-4">
<label class="mb-2 font-bold text-lg text-gray-900" for="year">Year</label>
<input class="border py-2 px-3 text-grey-800" type="number" name="year" id="year" min="1920" max="{{ date('Y') + 1 }}" placeholder="2003">
</div>
<div class="flex flex-col mb-4">
<label class="mb-2 font-bold text-lg text-gray-900" for="make">Make</label>
<input class="border py-2 px-3 text-grey-800" type="text" name="make" id="make" placeholder="Chevy">
</div>
<div class="flex flex-col mb-4">
<label class="mb-2 font-bold text-lg text-gray-900" for="model">Model</label>
<input class="border py-2 px-3 text-grey-800" type="text" name="model" id="model" placeholder="Corvette">
</div>
<div class="flex flex-col mb-4">
<label class="mb-2 font-bold text-lg text-gray-900" for="password">Transmission</label>
<div class="flex items-center mb-4">
<input id="is-manual-1" type="radio" name="is_manual" value="1" class="h-4 w-4 border-gray-300 focus:ring-2 focus:ring-blue-300" checked>
<label for="is-manual-1" class="text-sm font-medium text-gray-900 ml-2">
Manual
</label>
</div>
<div class="flex items-center mb-4">
<input id="is-manual-2" type="radio" name="is_manual" value="0" class="h-4 w-4 border-gray-300 focus:ring-2 focus:ring-blue-300">
<label for="is-manual-2" class="text-sm font-medium text-gray-900 ml-2">
Automatic
</label>
</div>
</div>
<div class="flex flex-col mb-4">
<label class="mb-2 font-bold text-lg text-gray-900" for="exterior_color">Exterior Color</label>
<input class="border py-2 px-3 text-grey-800" type="text" name="exterior_color" id="exterior_color" placeholder="Blue">
</div>
<div class="flex flex-col mb-4">
<label class="mb-2 font-bold text-lg text-gray-900" for="purchase_amount">Purchase Amount (in USD)</label>
<input class="border py-2 px-3 text-grey-800" type="text" name="purchase_amount" id="purchase_amount" placeholder="9532.57">
</div>
<div class="flex flex-col mb-4">
<label class="mb-2 font-bold text-lg text-gray-900" for="current_value">Current Value (in USD)</label>
<input class="border py-2 px-3 text-grey-800" type="text" name="current_value" id="current_value" placeholder="95532.57">
</div>
<div class="flex flex-col mb-4">
<label class="mb-2 font-bold text-lg text-gray-900" for="sales_amount">Sales Amount (in USD)</label>
<input class="border py-2 px-3 text-grey-800" type="text" name="sales_amount" id="sales_amount" placeholder="0.00">
</div>
<div class="flex flex-col mb-4">
<label class="mb-2 font-bold text-lg text-gray-900" for="date_purchased">Date Purchased</label>
<input class="border py-2 px-3 text-grey-800" type="date" name="date_purchased" id="date_purchased">
</div>
<div class="flex flex-col mb-4">
<label class="mb-2 font-bold text-lg text-gray-900" for="date_sold">Date Sold</label>
<input class="border py-2 px-3 text-grey-800" type="date" name="date_sold" id="date_sold">
</div>
<div class="flex flex-col mb-4">
<div class="mb-8">
<label class="mb-2 font-bold text-lg text-gray-900 block" for="file">Select Image</label>
<input type="file" name="file" id="file" class="mb-2 text-lg text-gray-900" />
</div>
</div>
<button class="block bg-green-400 hover:bg-green-600 text-white uppercase text-lg mx-auto p-4 rounded" type="submit">Save Car</button>
</form>
</div>
</div>
</div>
</x-layouts.app>
Before we update our edit.blade.php
view, we’ll pass the $car
to it and return it.
public function edit($id)
{
$car = PersonalCar::with(['brand', 'model', 'images'])->find($id);
return view('personalcars/edit', [
'title' => "Edit " . $car->year . " " . $car->brand->name . " " . $car->model->name,
'car' => $car,
]);
}
edit.blade.php
Let’s make the necessary tweaks to our edit.blade.php
file. First, we need to change our title.
<h1 class="block w-full text-center text-gray-800 text-2xl font-bold mb-6">
Edit Car
</h1>
Next, our form action needs to point to our personalcars/{{ $id }}
route.
<form action="/personalcars/{{ $car->id }}" method="post" enctype="multipart/form-data">
If you remember our route, it’s actually accepting a put
request. We can’t change our method
to put
but we can get creative. Blade has a @method
directive that allows us to pass other types of requests, like put
.
<form action="/personalcars/{{ $car->id }}" method="post" enctype="multipart/form-data">
@csrf
@method('put')
Next, we need to populate our fields. This is done by utilizing the value
attribute in each of our fields.
For example, in our year
input field, we’ll add a value with the following code.
<input
class="border py-2 px-3 text-grey-800"
type="number"
name="year"
id="year"
value="{{ $car->year }}"
min="1920"
max="{{ date('Y') + 1 }}"
placeholder="2003"
>
We’ll repeat the process for the rest of our fields.
<x-layouts.app title="{{ $title }}">
<div class="flex bg-white mt-12">
<div class="flex justify-center items-center w-full">
<div class="w-1/2 bg-white rounded shadow-2xl p-8 m-4">
<h1 class="block w-full text-center text-gray-800 text-2xl font-bold mb-6">Edit Car</h1>
@if ($errors->any())
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
@endif
<form action="/personalcars/{{ $car->id }}" method="post" enctype="multipart/form-data">
@csrf
@method('put')
<div class="flex flex-col mb-4">
<label class="mb-2 font-bold text-lg text-gray-900" for="year">Year</label>
<input class="border py-2 px-3 text-grey-800" type="number" name="year" id="year" value="{{ $car->year }}" min="1920" max="{{ date('Y') + 1 }}" placeholder="2003">
</div>
<div class="flex flex-col mb-4">
<label class="mb-2 font-bold text-lg text-gray-900" for="make">Make</label>
<input class="border py-2 px-3 text-grey-800" type="text" name="make" id="make" value="{{ $car->brand->name }}" placeholder="Chevy">
</div>
<div class="flex flex-col mb-4">
<label class="mb-2 font-bold text-lg text-gray-900" for="model">Model</label>
<input class="border py-2 px-3 text-grey-800" type="text" name="model" id="model" value="{{ $car->model->name }}" placeholder="Corvette">
</div>
<div class="flex flex-col mb-4">
<label class="mb-2 font-bold text-lg text-gray-900" for="password">Transmission</label>
<div class="flex items-center mb-4">
<input id="is-manual-1" type="radio" name="is_manual" value="1" class="h-4 w-4 border-gray-300 focus:ring-2 focus:ring-blue-300" {{ $car->is_manual == "Manual" ? "checked" : "" }}>
<label for="is-manual-1" class="text-sm font-medium text-gray-900 ml-2">
Manual
</label>
</div>
<div class="flex items-center mb-4">
<input id="is-manual-2" type="radio" name="is_manual" value="0" class="h-4 w-4 border-gray-300 focus:ring-2 focus:ring-blue-300" {{ $car->is_manual == "Automatic" ? "checked" : "" }}>
<label for="is-manual-2" class="text-sm font-medium text-gray-900 ml-2">
Automatic
</label>
</div>
</div>
<div class="flex flex-col mb-4">
<label class="mb-2 font-bold text-lg text-gray-900" for="exterior_color">Exterior Color</label>
<input class="border py-2 px-3 text-grey-800" type="text" name="exterior_color" id="exterior_color" value="{{ $car->exterior_color }}" placeholder="Blue">
</div>
<div class="flex flex-col mb-4">
<label class="mb-2 font-bold text-lg text-gray-900" for="purchase_amount">Purchase Amount (in USD)</label>
<input class="border py-2 px-3 text-grey-800" type="text" name="purchase_amount" id="purchase_amount" value="{{ $car->purchase_amount }}"}} placeholder="9532.57">
</div>
<div class="flex flex-col mb-4">
<label class="mb-2 font-bold text-lg text-gray-900" for="current_value">Current Value (in USD)</label>
<input class="border py-2 px-3 text-grey-800" type="text" name="current_value" id="current_value" value="{{ $car->current_value }}" placeholder="95532.57">
</div>
<div class="flex flex-col mb-4">
<label class="mb-2 font-bold text-lg text-gray-900" for="sales_amount">Sales Amount (in USD)</label>
<input class="border py-2 px-3 text-grey-800" type="text" name="sales_amount" id="sales_amount" value="{{ $car->sales_amount == "N/A" ? 0 : $car->sales_amount }}" placeholder="0.00">
</div>
<div class="flex flex-col mb-4">
<label class="mb-2 font-bold text-lg text-gray-900" for="date_purchased">Date Purchased</label>
<input class="border py-2 px-3 text-grey-800" type="date" name="date_purchased" id="date_purchased" value="{{ $car->date_purchased }}">
</div>
<div class="flex flex-col mb-4">
<label class="mb-2 font-bold text-lg text-gray-900" for="date_sold">Date Sold</label>
<input class="border py-2 px-3 text-grey-800" type="date" name="date_sold" id="date_sold" value="{{ $car->date_sold }}">
</div>
<div class="flex flex-col mb-4">
@foreach( $car->images as $image )
<div class="w-80 bg-white p-3">
<img class="h-52 w-full object-cover" src="{{ asset( 'storage/' . $image->url ) }}" alt="{{ $image->alt }}" />
</div>
@endforeach
<div class="mb-8">
<label class="mb-2 font-bold text-lg text-gray-900 block" for="file">Add Another Image</label>
<input type="file" name="file" id="file" class="mb-2 text-lg text-gray-900" />
</div>
</div>
<button class="block bg-green-400 hover:bg-green-600 text-white uppercase text-lg mx-auto p-4 rounded" type="submit">Update</button>
</form>
</div>
</div>
</div>
</x-layouts.app>
All of the standard text fields follow the same format as was just discussed. Our radio
button checks to see what our PersonalCar
model returns. It will return either Automatic
or Manual
. Depending on which one matches, the appropriate radio button is checked.
<input
id="is-manual-1"
type="radio"
name="is_manual"
value="1"
class="h-4 w-4 border-gray-300 focus:ring-2 focus:ring-blue-300"
{{ $car->is_manual == "Manual" ? "checked" : "" }}
>
<input
id="is-manual-2"
type="radio"
name="is_manual"
value="0"
class="h-4 w-4 border-gray-300 focus:ring-2 focus:ring-blue-300"
{{ $car->is_manu
We’ll be able to add additional images through our edit
form as well. We’ll display our existing images here.
<div class="flex flex-col mb-4">
@foreach( $car->images as $image )
<div class="w-80 bg-white p-3">
<img class="h-52 w-full object-cover" src="{{ asset( 'storage/' . $image->url ) }}" alt="{{ $image->alt }}" />
</div>
@endforeach
<div class="mb-8">
<label class="mb-2 font-bold text-lg text-gray-900 block" for="file">Add Another Image</label>
<input type="file" name="file" id="file" class="mb-2 text-lg text-gray-900" />
</div>
</div>al == "Automatic" ? "checked" : "" }}
>
Our form is now fully populated.
Update Method
The update
method will be very similar to our store
method. The only difference is that we’ll first find
our resource and update it instead of inserting a new resource.
We start off with the same validation.
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
*/
public function update(Request $request, $id)
{
$validated = $request->validate([
'year' => 'required|integer',
'make' => 'required|max:255',
'model' => 'required|max:255',
'is_manual' => 'required|boolean',
'exterior_color' => 'required|max:255',
'purchase_amount' => 'numeric',
'current_value' => 'numeric',
'sales_amount' => 'numeric|nullable',
'date_purchased' => 'required|date',
'date_sold' => 'date|nullable',
'file' => 'image|mimes:jpg,jpeg,png,gif|max:2048|nullable',
]);
}
If you run the code as is, you’ll see that it already fails validation. That’s because of our formatting for purchase_amount
, current_value
and sales_amount
. We must strip out the non-numeric characters, like $. We’ll just use a simple preg_replace
expression here.
preg_replace("#[^0-9.]#", "", $car->purchase_amount)
<div class="flex flex-col mb-4">
<label class="mb-2 font-bold text-lg text-gray-900" for="purchase_amount">Purchase Amount (in USD)</label>
<input class="border py-2 px-3 text-grey-800" type="text" name="purchase_amount" id="purchase_amount" value="{{ preg_replace("#[^0-9.]#", "", $car->purchase_amount) }}" placeholder="9532.57">
</div>
<div class="flex flex-col mb-4">
<label class="mb-2 font-bold text-lg text-gray-900" for="current_value">Current Value (in USD)</label>
<input class="border py-2 px-3 text-grey-800" type="text" name="current_value" id="current_value" value="{{ preg_replace("#[^0-9.]#", "", $car->current_value) }}" placeholder="95532.57">
</div>
<div class="flex flex-col mb-4">
<label class="mb-2 font-bold text-lg text-gray-900" for="sales_amount">Sales Amount (in USD)</label>
<input class="border py-2 px-3 text-grey-800" type="text" name="sales_amount" id="sales_amount" value="{{ $car->sales_amount == "N/A" ? 0 : preg_replace("#[^0-9.]#", "", $car->sales_amount) }}" placeholder="0.00">
</div>
This will now allow us to pass validation.
Next, we need to find the resource. Since the $id
is passed to our update
method, we can simply use it.
public function update(Request $request, $id)
{
$validated = $request->validate([
'year' => 'required|integer',
'make' => 'required|max:255',
'model' => 'required|max:255',
'is_manual' => 'required|boolean',
'exterior_color' => 'required|max:255',
'purchase_amount' => 'numeric',
'current_value' => 'numeric',
'sales_amount' => 'numeric|nullable',
'date_purchased' => 'required|date',
'date_sold' => 'date|nullable',
'file' => 'image|mimes:jpg,jpeg,png,gif|max:2048|nullable',
]);
$car = PersonalCar::find($id);
}
Finally, we need to update it.
We’ll add the brand and model first, or retrieve them if they exist. Next, we’ll update each of the properties and associate the brand
and model
to it. Finally, if an image was selected (i.e. image != null), then we’ll upload the image and attach
it.
The user should be redirected back to the edit
page with a success message.
public function update(Request $request, $id)
{
$validated = $request->validate([
'year' => 'required|integer',
'make' => 'required|max:255',
'model' => 'required|max:255',
'is_manual' => 'required|boolean',
'exterior_color' => 'required|max:255',
'purchase_amount' => 'numeric',
'current_value' => 'numeric',
'sales_amount' => 'numeric|nullable',
'date_purchased' => 'required|date',
'date_sold' => 'date|nullable',
'file' => 'image|mimes:jpg,jpeg,png,gif|max:2048|nullable',
]);
$car = PersonalCar::find($id);
$brand = PersonalCarBrand::firstOrCreate([
'name' => $request->make,
], [
'slug' => str_replace(" ", "-", strtolower($request->make))
]);
$model = PersonalCarModel::firstOrCreate([
'name' => $request->model,
], [
'slug' => str_replace(" ", "-", strtolower($request->model))
]);
$car->year = $request->year;
$car->brand()->associate($brand);
$car->model()->associate($model);
$car->is_manual = $request->is_manual;
$car->exterior_color = $request->exterior_color;
$car->purchase_amount = $request->purchase_amount;
$car->current_value = $request->current_value;
$car->sales_amount = $request->sales_amount == 0 ? 0 : $request->sales_amount;
$car->date_purchased = $request->date_purchased;
$car->date_sold = $request->date_sold;
$car->save();
if ( $request->file('file') !== null ) {
$image_path = $request->file('file')->store('images', 'public');
$image = Image::create([
'url' => $image_path,
'alt' => $request->year . " " . $brand->name . " " . $model->name,
]);
$car->images()->attach($image);
}
return redirect()->to('/personalcars/' . $id . '/edit')->with('status', 'Your car has been updated.');
}
Our success message needs to be able to be displayed within our edit.blade.php
view. We can add that right under our error check.
<x-layouts.app title="{{ $title }}">
<div class="flex bg-white mt-12">
<div class="flex justify-center items-center w-full">
<div class="w-1/2 bg-white rounded shadow-2xl p-8 m-4">
<h1 class="block w-full text-center text-gray-800 text-2xl font-bold mb-6">Edit Car</h1>
@if ($errors->any())
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
@endif
@if (session('status'))
<div class="block bg-green-200 p-4 mb-4">
{{ session('status') }}
</div>
@endif
<!-- ... -->
Now, if we make a modification, we can click update and our resource will be updated.
In the next article we’ll tackle deleting a resource and we’ll also look at removing specific images.
https://github.com/dinocajic/youtube-laravel
Laravel Series
Continue your Laravel Learning.
Implementing Single Image Upload in Forms
Laravel – P44: CMP – Form Single Image Upload
Learn how to implement single image uploads in Laravel forms. A step-by-step guide to handling and storing images effectively in your application.
How to Use Laravel’s Controller Edit/Update Methods Effectively
Laravel – P45: CMP – Controller Edit/Update
Learn how to effectively implement Edit and Update methods in Laravel controllers to manage data updates smoothly in your applications.
Simplifying Data Removal with Laravel’s Controller Destroy Method
Laravel – P46: CMP – Controller Destroy
Learn how to use the Destroy method in Laravel controllers to efficiently handle data deletions and maintain clean data management.