CRUD (Create, Read, Update, Delete) is the foundation of almost every web application. Once you master it in Laravel, building any feature — from a point-of-sale app to an inventory system — becomes far easier because the pattern repeats. This tutorial builds a complete product CRUD in Laravel 11 from scratch, with code you can copy and understand.
1. Prepare the Table with a Migration
Instead of creating the table by hand in phpMyAdmin, use a migration so the database structure is tracked in code. Create the model and its migration together:
php artisan make:model Product -m
Open the migration in database/migrations/ and define the columns:
public function up(): void
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->integer('price');
$table->integer('stock')->default(0);
$table->text('description')->nullable();
$table->timestamps();
});
}
php artisan migrate
2. Set Up the Model
To mass-assign data via create() and update(), list the fillable columns in the $fillable property. This protects you from mass-assignment vulnerabilities:
class Product extends Model
{
protected $fillable = ['name', 'price', 'stock', 'description'];
}
3. Create a Resource Controller
Laravel has a controller type that scaffolds all seven CRUD methods at once. Create it with the --resource flag:
php artisan make:controller ProductController --resource
Register a single route that maps all of them in routes/web.php:
use App\Http\Controllers\ProductController;
Route::resource('products', ProductController::class);
Run php artisan route:list to see the index, create, store, edit, update, and destroy routes created automatically.
4. Display and Store Data
public function index()
{
$products = Product::latest()->paginate(10);
return view('products.index', compact('products'));
}
public function store(StoreProductRequest $request)
{
Product::create($request->validated());
return redirect()->route('products.index')
->with('success', 'Product created.');
}
5. Clean Validation with Form Requests
Note the StoreProductRequest parameter above. Rather than piling validation rules in the controller, move them to a dedicated class so the controller stays clean:
php artisan make:request StoreProductRequest
public function rules(): array
{
return [
'name' => 'required|string|max:255',
'price' => 'required|integer|min:0',
'stock' => 'required|integer|min:0',
];
}
When validation fails, Laravel automatically redirects the user back to the form with the error messages — no extra code needed.
6. A Simple Blade View
@foreach ($products as $product)
<tr>
<td>{{ $product->name }}</td>
<td>{{ number_format($product->price) }}</td>
<td>{{ $product->stock }}</td>
</tr>
@endforeach
{{ $products->links() }}
The {{ $products->links() }} line renders pagination navigation automatically.
7. Update and Delete
Thanks to route model binding, Laravel finds the record by the ID in the URL for you:
public function update(StoreProductRequest $request, Product $product)
{
$product->update($request->validated());
return redirect()->route('products.index')
->with('success', 'Product updated.');
}
public function destroy(Product $product)
{
$product->delete();
return back()->with('success', 'Product deleted.');
}
Adding Search & Pagination
Real apps need search. Use when() so the filter only applies when a keyword is present:
$products = Product::query()
->when($request->q, fn($query) =>
$query->where('name', 'like', '%'.$request->q.'%'))
->latest()
->paginate(10)
->withQueryString();
withQueryString() keeps the search term when moving between pages.
Common Mistakes to Avoid
- Forgetting
$fillable: causes a MassAssignmentException oncreate(). - Skipping validation: dirty data reaches the database and opens security holes.
- Putting all logic in the controller: use Form Requests and, for complex logic, a Service class.
With these seven steps you have a complete, safe, and clean CRUD. Repeat the same pattern for other entities such as customers, transactions, or categories — which is exactly why mastering CRUD makes every future build feel much faster.