Step 7 : Building the Checkout and Order Management System in Laravel
At this point, your Laravel e-commerce app can:
- Show products on the storefront
- Allow users to add items to their cart
- Update quantities or remove products
But without a checkout system, users can’t actually place an order.
This step is crucial because it turns a simple catalog into a real store where customers can submit their purchase information, and you can manage those orders in the admin panel.
We’ll build this carefully with clean logic, proper database structure, and real-world best practices.
🧭 What We’ll Build in This Step
By the end of Step 7, your application will be able to:
- Collect customer shipping and billing details during checkout
- Convert cart items into an order stored in the database
- Create Order and OrderItem models with proper relationships
- Show a confirmation page to the customer
- Display orders list in the admin dashboard
🧩 Step 1: Designing the Orders Database Structure
We need two tables:
orders→ stores general info (customer name, email, total, status)order_items→ stores each product in that order (product_id, quantity, price)
📦 Create Migrations
Run these Artisan commands:
php artisan make:model Order -m
php artisan make:model OrderItem -m
Then open database/migrations/xxxx_create_orders_table.php and add:
public function up()
{
Schema::create('orders', function (Blueprint $table) {
$table->id();
$table->string('customer_name');
$table->string('customer_email');
$table->string('customer_phone')->nullable();
$table->text('customer_address');
$table->decimal('total', 10, 2);
$table->string('status')->default('pending'); // pending, processing, completed
$table->timestamps();
});
}
And xxxx_create_order_items_table.php:
public function up()
{
Schema::create('order_items', function (Blueprint $table) {
$table->id();
$table->foreignId('order_id')->constrained()->onDelete('cascade');
$table->foreignId('product_id')->constrained()->onDelete('cascade');
$table->integer('quantity');
$table->decimal('price', 10, 2);
$table->timestamps();
});
}
Finally run:
php artisan migrate
🧩 Step 2 : Setting Up Model Relationships
Open app/Models/Order.php:
class Order extends Model
{
protected $fillable = [
'customer_name','customer_email','customer_phone',
'customer_address','total','status'
];
public function items()
{
return $this->hasMany(OrderItem::class);
}
}
And app/Models/OrderItem.php:
class OrderItem extends Model
{
protected $fillable = ['order_id','product_id','quantity','price'];
public function order()
{
return $this->belongsTo(Order::class);
}
public function product()
{
return $this->belongsTo(Product::class);
}
}
These relationships let you easily access $order->items and $item->product.
🧩 Step 3 : Building the Checkout Form
Create a CheckoutController:
php artisan make:controller CheckoutController
Open app/Http/Controllers/CheckoutController.php and add:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\Product;
use Illuminate\Support\Facades\Session;
class CheckoutController extends Controller
{
public function showForm()
{
$cart = Session::get('cart', []);
if (empty($cart)) {
return redirect()->route('shop.index')->with('error','Your cart is empty');
}
return view('checkout.form', compact('cart'));
}
public function placeOrder(Request $request)
{
$request->validate([
'customer_name' => 'required',
'customer_email' => 'required|email',
'customer_address' => 'required'
]);
$cart = Session::get('cart', []);
if (empty($cart)) {
return redirect()->route('shop.index');
}
$total = collect($cart)->sum(fn($item) => $item['price'] * $item['quantity']);
$order = Order::create([
'customer_name' => $request->customer_name,
'customer_email' => $request->customer_email,
'customer_phone' => $request->customer_phone,
'customer_address' => $request->customer_address,
'total' => $total,
'status' => 'pending'
]);
foreach ($cart as $id => $item) {
OrderItem::create([
'order_id' => $order->id,
'product_id' => $id,
'quantity' => $item['quantity'],
'price' => $item['price']
]);
}
Session::forget('cart');
return redirect()->route('checkout.thankyou', $order->id);
}
public function thankYou($id)
{
$order = Order::findOrFail($id);
return view('checkout.thankyou', compact('order'));
}
}
🧩 Step 4 : Routes for Checkout
Add these to routes/web.php:
use App\Http\Controllers\CheckoutController;
Route::get('/checkout', [CheckoutController::class, 'showForm'])->name('checkout.form');
Route::post('/checkout', [CheckoutController::class, 'placeOrder'])->name('checkout.place');
Route::get('/thank-you/{id}', [CheckoutController::class, 'thankYou'])->name('checkout.thankyou');
🧩 Step 5 : Checkout Blade Views
resources/views/checkout/form.blade.php
@extends('layouts.app')
@section('content')
<div class="container my-5">
<h2>Checkout</h2>
<form method="POST" action="{{ route('checkout.place') }}">
@csrf
<div class="mb-3">
<label>Name</label>
<input type="text" name="customer_name" class="form-control" required>
</div>
<div class="mb-3">
<label>Email</label>
<input type="email" name="customer_email" class="form-control" required>
</div>
<div class="mb-3">
<label>Phone</label>
<input type="text" name="customer_phone" class="form-control">
</div>
<div class="mb-3">
<label>Address</label>
<textarea name="customer_address" class="form-control" required></textarea>
</div>
<button class="btn btn-success">Place Order</button>
</form>
</div>
@endsection
resources/views/checkout/thankyou.blade.php
@extends('layouts.app')
@section('content')
<div class="container my-5 text-center">
<h2>Thank you for your order!</h2>
<p>Your order ID is <strong>#{{ $order->id }}</strong></p>
<p>We’ve sent a confirmation email to <strong>{{ $order->customer_email }}</strong>.</p>
<a href="{{ route('shop.index') }}" class="btn btn-primary mt-4">Back to Shop</a>
</div>
@endsection
🧩 Step 6 Viewing Orders in Admin Panel
Create an OrderController for admins:
php artisan make:controller Admin/OrderController
Example method:
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Order;
class OrderController extends Controller
{
public function index()
{
$orders = Order::latest()->paginate(20);
return view('admin.orders.index', compact('orders'));
}
public function show($id)
{
$order = Order::with('items.product')->findOrFail($id);
return view('admin.orders.show', compact('order'));
}
}
Then create Blade views (admin/orders/index.blade.php and admin/orders/show.blade.php) to display them like a simple table.
✅ Summary of Step 7
You have now added:
- Orders & OrderItems tables
- Checkout form for customer details
- Logic to convert cart into a real order
- Order confirmation page
- Admin order management section
This step officially transforms your Laravel project from just a product catalog into a working e-commerce application where people can buy products.