Sending a Form to a PUT Route in Laravel (With Example)

Routes that receive PUT requests that contain data are commonly used for updating. This originates from RESTful API best practices but can be applied to any web application.

In Laravel defining a route that uses the PUT method is straightforward, for example:

routes/web.php
Route::put('/products/{products}', 'ProductController@update')->name('products.update');

A PUT route is also added when using a resource controller like:

Route::resource('products', ProductController::class);

In both cases, you might be wondering what the form code would need to look like in order to support a route that accepts only the PUT method. After all HTML forms in browsers don’t natively support using the PUT method.

Fortunately, Laravel provides an easy solution for this!

By using @method('PUT') within the form, Laravel will know to handle incoming form submissions as PUT requests:

<form action="{{ route('products.update', $product) }}" method="POST">
    @method('PUT')
    @csrf
    <input name="name" value="" />
    <!-- Additional form elements -->
    <input type="submit" />
</form>

That’s it! Behind the scenes, Laravel automatically replaces the @method directive with a hidden field. By sending this hidden field Laravel will know that the incoming form data should be handled as if it were sent as a PUT (this is called “Method spoofing”). You can see this hidden input when viewing the page source code:

<input type="hidden" name="_method" value="PUT">

In this guide, we’ll build a full example that shows a list of products and provides a form that updates a product using a PUT-based route.

Let’s get started!

Step 1: Create a Laravel Project

Begin by creating a new Laravel project if you haven’t already. Open your terminal and run:

composer create-project laravel/laravel productsapp
cd productsapp 

Step 2: Creating a Model and Migration

Generate a ‘Product’ model and a migration file to create a ‘products’ table by running the following artisan command:

php artisan make:model Product -m

This adds a model in app/Models/Product.php in which we’ll add the following code to allow us to easily mass-update all columns in one go:

app/Models/Product.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    protected $fillable = [
        'name',
        'price',
        'category'
    ];
}

Let’s also edit the migration file in the folder database/migrations. In that file, we will define the database columns we will need by adding:

database/migrations/2023_11_22_204217_create_products_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('category');
            $table->decimal('price', 8, 2);
            // Add other product fields here
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('products');
    }
};

Step 3: Create a Seeder

Now we’ll create and run a seeder to fill the products table with some test data.

Create a seeder called ProductSeeder by running:

php artisan make:seeder ProductSeeder

Now open the generated ProductSeeder.php file located in the database/seeders directory and add:

database/seeders/ProductSeeder.php
<?php

namespace Database\Seeders;

use App\Models\Product;
use Illuminate\Database\Seeder;

class ProductSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        Product::create([
            'name' => 'The Lord of the Rings: The Fellowship of the Ring',
            'price' => 9.95,
            'category' => 'Fantasy Movies',
        ]);

        Product::create([
            'name' => 'Pirates of the Caribbean: The Curse of the Black Pearl',
            'price' => 9.95,
            'category' => 'Fantasy Movies',
        ]);

        Product::create([
            'name' => 'Pan\'s Labyrinth',
            'price' => 9.95,
             'category' => 'Fantasy Movies',
        ]);
    }
}

Now run the seeder using Artisan:

php artisan db:seed --class=ProductSeeder

Step 4: Create a ProductController

Create a controller for managing products:

php artisan make:controller ProductController

In the next step, we’ll add the necessary code.

Step 5: Add Code to ProductController

Next, add the following methods to the ProductController: ‘index’ to display a list of products, ‘edit’ to render the edit form, and ‘update’ to validate form fields and perform an update:

app/Http/Controllers/ProductController.php
<?php

namespace App\Http\Controllers;

use App\Models\Product;
use Illuminate\Http\Request;

class ProductController extends Controller
{
    public function index()
    {
        $products = Product::all();

        return view('products.index', compact('products'));
    }

    public function edit(Product $product)
    {
        return view('products.edit', compact('product'));
    }

    public function update(Request $request, Product $product)
    {
        $validated = $request->validate([
            'name' => 'required|string|max:255',
            'price' => 'required|numeric',
            'category' => 'required|string',
        ]);

        $product->update($validated);

        return redirect()->route('products.index');
    }
}

Step 6: Setting Up Routes

Now let’s add routes to routes/web.php to show all products (index), edit a product, and update a product.

routes/web.php
<?php

use App\Http\Controllers\ProductController;
use Illuminate\Support\Facades\Route;

Route::get('/products', [ProductController::class, 'index'])->name('products.index');
Route::get('/products/{product}', [ProductController::class, 'edit'])->name('products.edit');
Route::put('/products/{product}', [ProductController::class, 'update'])->name('products.update');

Step 7: Create a View to Update A Product

Now let’s create a file at resources/views/products/edit.blade.php to contain the form code needed to edit a product.

Note that this is where we’ll need to add the @method(‘PUT’) to ensure it works nicely with the put-based route we’ve defined in the previous step:

resources/views/products/edit.blade.php
<html>
<head>
    <!-- Include Bootstrap to make it the example look better -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
          integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
            integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
            crossorigin="anonymous"></script>
</head>
<body>
<div class="container">
    <!-- Edit Product Form -->
    <div class="row">
        <div class="col-md-6 mt-3">
            <div class="card">
                <div class="card-body">
                    <h5 class="card-title">Edit Product</h5>
                    <form action="{{ route('products.update', $product) }}" method="POST">
                        @method('PUT')
                        @csrf

                        @error('name')
                        <div class="invalid-feedback d-block">{{ $message }}</div>
                        @enderror

                        <div class="form-group">
                            <label for="name">Product name</label>
                            <input name="name" type="text" class="form-control" value="{{ $product->name }}" />
                        </div>

                        @error('price')
                        <div class="invalid-feedback d-block">{{ $message }}</div>
                        @enderror

                        <div class="form-group">
                            <label for="price">Product price</label>
                            <input name="price" type="text" class="form-control" value="{{ $product->price }}" />
                        </div>

                        @error('category')
                        <div class="invalid-feedback d-block">{{ $message }}</div>
                        @enderror

                        <div class="form-group">
                            <label for="category">Product category</label>
                            <input name="category" type="text" class="form-control" value="{{ $product->category }}" />
                        </div>

                        <input type="submit" class="btn btn-primary mt-3" />
                        <a href="{{ route('products.index') }}" class="btn btn-secondary mt-3">Cancel</a>

                    </form>
                </div>
            </div>
        </div>
    </div>

    <!-- Footer -->
    <footer class="mt-5 text-center">
        <p>Created with ♥ by Laracoding</p>
    </footer>
</div>
</body>
</html>

In case you forget to add @method('PUT') to your form you will receive this error:

“The POST method is not supported for route products/1. Supported methods: GET, HEAD, PUT.”

To fix this simply add @method('PUT') and try again

Step 8: Creating a View to Show Products

Now let’s create a file at products/index.blade.php with blade code to display a list of all products along with a link to go to the edit page:

resources/products/index.blade.php
<html>
<head>
    <!-- Include Bootstrap to make it the example look better -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
          integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
            integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
            crossorigin="anonymous"></script>
</head>
<body>
<div class="container">

    <h1 class="mt-4 mb-4">All Products</h1>

    <!-- Product List -->
    <div class="row">
        @foreach($products as $product)
            <div class="col-md-4 mb-3">
                <div class="card">
                    <div class="card-body">
                        <h5 class="card-title">{{ $product->name }}</h5>
                        <p class="card-text">
                            Price: ${{ $product->price }}<br>
                            Category: {{ $product->category }}
                        </p>
                        <a href="{{ route('products.edit', $product) }}">
                            Edit
                        </a>
                    </div>
                </div>
            </div>
        @endforeach
    </div>

    <!-- Footer -->
    <footer class="mt-5 text-center">
        <p>Created with ♥ by Laracoding</p>
    </footer>
</div>
</body>
</html>

Step 9: Test the Application

Launch the application by running:

php artisan serve

Now navigate to http://127.0.0.1:8000/products in your browser. You should see the products along with a link to go to our edit form:

When clicking on the edit we’ll open our edit form as shown below. As a test let’s update the price:

Now after storing our changes, the application will redirect us back to the index page where we can see the updated price:

Conclusion

Congratulations on learning how to send a PUT request using a form in Laravel! Using simple blade directives like @method is part of what makes the developer experience with Laravel so great.

Now go ahead and make something cool using this technique. Happy coding!

References:

Johan van den Broek

Johan is the creator of laracoding.com. As a child, he began tinkering with various programming languages, many of which have been long forgotten today. Currently, he works exclusively with PHP and Laravel, and his passion for programming remains to this day.

Leave a Reply

Your email address will not be published. Required fields are marked *

Recent Posts