In the previous post, we created the database structure and models for categories, products, and orders. Now it’s time to make the application interactive and dynamic. This step focuses on CRUD operations (Create, Read, Update, Delete) for products and categories, and displaying products dynamically on the homepage.
By the end of this post, you’ll be able to manage products from an admin panel and show them live to users.
Step 1: Setting Up Routes for CRUD
First, we need routes to manage products and categories. Open routes/web.php and add:
use App\Http\Controllers\ProductController;
use App\Http\Controllers\CategoryController;
Route::resource('categories', CategoryController::class);
Route::resource('products', ProductController::class);
Tip: Using
Route::resourceautomatically creates routes for all CRUD operations (index, create, store, show, edit, update, destroy). This saves a lot of boilerplate.
Step 2: Creating Controllers
Next, create controllers for products and categories:
php artisan make:controller ProductController --resource
php artisan make:controller CategoryController --resource
This generates controller files with empty methods for CRUD operations.
Step 3: Implementing Category CRUD
In app/Http/Controllers/CategoryController.php, implement CRUD logic:
use App\Models\Category;
use Illuminate\Http\Request;
class CategoryController extends Controller
{
public function index()
{
$categories = Category::all();
return view('categories.index', compact('categories'));
}
public function create()
{
return view('categories.create');
}
public function store(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
'description' => 'nullable|string',
]);
Category::create($request->all());
return redirect()->route('categories.index')->with('success', 'Category created successfully.');
}
public function edit(Category $category)
{
return view('categories.edit', compact('category'));
}
public function update(Request $request, Category $category)
{
$request->validate([
'name' => 'required|string|max:255',
'description' => 'nullable|string',
]);
$category->update($request->all());
return redirect()->route('categories.index')->with('success', 'Category updated successfully.');
}
public function destroy(Category $category)
{
$category->delete();
return redirect()->route('categories.index')->with('success', 'Category deleted successfully.');
}
}
Human tip: Always validate user input to prevent errors or malicious data.
Step 4: Implementing Product CRUD
In ProductController.php:
use App\Models\Product;
use App\Models\Category;
use Illuminate\Http\Request;
class ProductController extends Controller
{
public function index()
{
$products = Product::with('category')->get();
return view('products.index', compact('products'));
}
public function create()
{
$categories = Category::all();
return view('products.create', compact('categories'));
}
public function store(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
'description' => 'required|string',
'price' => 'required|numeric',
'stock' => 'required|integer',
'category_id' => 'required|exists:categories,id',
'image' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048',
]);
$data = $request->all();
if ($request->hasFile('image')) {
$data['image'] = $request->file('image')->store('products', 'public');
}
Product::create($data);
return redirect()->route('products.index')->with('success', 'Product created successfully.');
}
public function edit(Product $product)
{
$categories = Category::all();
return view('products.edit', compact('product', 'categories'));
}
public function update(Request $request, Product $product)
{
$request->validate([
'name' => 'required|string|max:255',
'description' => 'required|string',
'price' => 'required|numeric',
'stock' => 'required|integer',
'category_id' => 'required|exists:categories,id',
'image' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048',
]);
$data = $request->all();
if ($request->hasFile('image')) {
$data['image'] = $request->file('image')->store('products', 'public');
}
$product->update($data);
return redirect()->route('products.index')->with('success', 'Product updated successfully.');
}
public function destroy(Product $product)
{
$product->delete();
return redirect()->route('products.index')->with('success', 'Product deleted successfully.');
}
}
Step 5: Creating Views
Create simple Blade templates for listing products (products/index.blade.php) and creating/editing products (products/create.blade.php, products/edit.blade.php).
Example products/index.blade.php:
@extends('layouts.app')
@section('content')
<div class="container mx-auto p-6">
<h1 class="text-2xl font-bold mb-4">Products</h1>
<a href="{{ route('products.create') }}" class="bg-green-500 text-white px-4 py-2 rounded mb-4 inline-block">Add Product</a>
<table class="min-w-full bg-white border">
<thead>
<tr>
<th class="px-4 py-2 border">Name</th>
<th class="px-4 py-2 border">Category</th>
<th class="px-4 py-2 border">Price</th>
<th class="px-4 py-2 border">Stock</th>
<th class="px-4 py-2 border">Actions</th>
</tr>
</thead>
<tbody>
@foreach($products as $product)
<tr>
<td class="px-4 py-2 border">{{ $product->name }}</td>
<td class="px-4 py-2 border">{{ $product->category->name }}</td>
<td class="px-4 py-2 border">${{ $product->price }}</td>
<td class="px-4 py-2 border">{{ $product->stock }}</td>
<td class="px-4 py-2 border">
<a href="{{ route('products.edit', $product->id) }}" class="bg-blue-500 text-white px-2 py-1 rounded">Edit</a>
<form action="{{ route('products.destroy', $product->id) }}" method="POST" class="inline-block">
@csrf
@method('DELETE')
<button type="submit" class="bg-red-500 text-white px-2 py-1 rounded">Delete</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endsection
Pro tip: Tailwind CSS makes tables and buttons look professional without custom CSS.
Step 6: Displaying Products Dynamically on the Homepage
Now, let’s replace our mock data homepage with dynamic products from the database.
In routes/web.php:
use App\Models\Product;
Route::get('/', function () {
$products = Product::with('category')->get();
return view('homepage', compact('products'));
});
In homepage.blade.php:
@foreach($products as $product)
<div class="bg-white p-4 rounded shadow hover:shadow-lg transition">
<h2 class="font-semibold text-xl">{{ $product->name }}</h2>
<p class="text-gray-600 mt-2">{{ $product->description }}</p>
<p class="text-green-600 mt-2 font-bold">${{ $product->price }}</p>
<p class="text-gray-500 mt-1">Category: {{ $product->category->name }}</p>
<button class="bg-blue-500 text-white px-4 py-2 mt-4 rounded hover:bg-blue-600">Add to Cart</button>
</div>
@endforeach
Human touch: You can now see real database products on the homepage instead of placeholders. This makes your e-commerce site feel alive.
Step 7: Key Takeaways
- CRUD operations allow admins to manage products and categories easily.
- Blade templates + Tailwind make the interface clean and responsive.
- Dynamic homepage connects directly to the database, eliminating static mock data.
- Always validate input and handle file uploads securely.
Personal Note
When I built my first dynamic e-commerce site, I was amazed at how quickly Laravel connects the database to the frontend. The moment I saw products appear on the homepage without manual coding, it felt like magic.
Next Steps
In the next post, we will:
- Implement a shopping cart and checkout system.
- Learn how users can add products to the cart and place orders.
- Add order status tracking to complete our e-commerce flow.