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:
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:
<?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:
<?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:
<?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:
<?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.
<?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:
<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:
<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:
- Form Method Spoofing (Laravel Documentation)
- Database: Seeding (Laravel Documentation)
This entry is part 1 of 9 in the series Working With Forms
- Sending a Form to a PUT Route in Laravel (With Example)
- Sending a Form to a DELETE Route in Laravel (With Example)
- Why Use Laravel Form Request and How? (BIG Code improvement)
- Making a File Upload Form in Laravel: a Step-by-Step Guide
- How to Insert Form Array Values Into the Database in Laravel
- Using Form Checkboxes in Laravel Blade (keep old value)
- Using Form Select in Laravel Blade (keep old value)
- Using Form Radio Buttons in Laravel Blade (keep old value)
- Using Text and Textarea in Laravel Blade (keep old value)