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_amountcurrent_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.

Laravel — P44: Form Single Image Upload (CMP)

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.

Laravel — P46: Controller Destroy (CMP)

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.

Leave a Reply