Building Order History & Customer Order Tracking

🧩 Step 9 — Building Order History & Customer Order Tracking

📌 Why This Feature Matters

In any e-commerce store, simply processing a payment isn’t enough.
Customers expect:

  • A clear order confirmation page
  • A dashboard to review their past orders
  • Real-time status tracking (like “Pending”, “Processing”, “Shipped”, “Delivered”)
  • Automatic updates if something changes

For the admin/store owner, this also means:

  • Having a central system to manage orders
  • Being able to update statuses (and notify users)

This builds trust and transparency—key factors for customer loyalty and repeat sales.


🧱 Core Components of Order Tracking System

We’ll build the following:

  1. Orders Table
    • Holds overall order info (user, total, status, payment status, timestamps)
  2. Order Items Table
    • Each product purchased in the order
  3. Order Status Flow
    • Pending → Processing → Shipped → Delivered (with optional “Cancelled” or “Returned”)
  4. Customer Order History Page
    • Users can see their previous orders and details
  5. Admin Order Management Panel
    • Admin can view, filter, and update orders
  6. Email Notifications (Optional)
    • Send confirmation or status update emails

⚙️ Step 1: Database Schema

orders table

Schema::create('orders', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->onDelete('cascade');
    $table->string('order_number')->unique();
    $table->decimal('total_amount', 10, 2);
    $table->enum('status', ['pending','processing','shipped','delivered','cancelled'])->default('pending');
    $table->enum('payment_status', ['unpaid','paid'])->default('unpaid');
    $table->string('shipping_address');
    $table->timestamps();
});

order_items table

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();
});

⚙️ Step 2: Models & Relationships

Order.php

class Order extends Model {
    protected $fillable = [
        'user_id','order_number','total_amount','status','payment_status','shipping_address'
    ];

    public function user() {
        return $this->belongsTo(User::class);
    }

    public function items() {
        return $this->hasMany(OrderItem::class);
    }
}

OrderItem.php

class OrderItem extends Model {
    protected $fillable = [
        'order_id','product_id','quantity','price'
    ];

    public function product() {
        return $this->belongsTo(Product::class);
    }
}

⚙️ Step 3: Placing an Order

When a user checks out:

  1. Validate cart & payment
  2. Create an Order record
  3. Create multiple OrderItem records from cart
  4. Mark payment as paid if successful

Example snippet inside CheckoutController:

$order = Order::create([
    'user_id' => auth()->id(),
    'order_number' => 'ORD-' . strtoupper(Str::random(8)),
    'total_amount' => $cartTotal,
    'payment_status' => 'paid',
    'shipping_address' => $request->address,
]);

foreach($cartItems as $item){
    OrderItem::create([
        'order_id' => $order->id,
        'product_id' => $item->product_id,
        'quantity' => $item->quantity,
        'price' => $item->price
    ]);
}

⚙️ Step 4: Customer Order History Page

Route:

Route::get('/my-orders', [OrderController::class, 'index'])->middleware('auth');

Controller:

public function index() {
    $orders = Order::with('items.product')
        ->where('user_id', auth()->id())
        ->latest()
        ->get();

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

Blade view (orders/index.blade.php):

<h1>My Orders</h1>
@foreach($orders as $order)
  <div class="border p-3 mb-3">
     <h3>Order #{{ $order->order_number }}</h3>
     <p>Status: {{ ucfirst($order->status) }}</p>
     <p>Total: ${{ $order->total_amount }}</p>
     <ul>
       @foreach($order->items as $item)
         <li>{{ $item->product->name }} x {{ $item->quantity }}</li>
       @endforeach
     </ul>
  </div>
@endforeach

⚙️ Step 5: Admin Order Management

Route:

Route::middleware(['auth','admin'])->group(function(){
   Route::get('/admin/orders', [AdminOrderController::class, 'index']);
   Route::post('/admin/orders/{order}/update-status', [AdminOrderController::class, 'updateStatus']);
});

AdminOrderController:

public function index() {
    $orders = Order::with('user')->latest()->get();
    return view('admin.orders.index', compact('orders'));
}

public function updateStatus(Request $request, Order $order) {
    $order->update(['status' => $request->status]);
    return back()->with('success','Order status updated');
}

Blade (admin/orders/index.blade.php):

<table>
  <thead>
    <tr><th>Order #</th><th>Customer</th><th>Status</th><th>Action</th></tr>
  </thead>
  <tbody>
  @foreach($orders as $order)
    <tr>
      <td>{{ $order->order_number }}</td>
      <td>{{ $order->user->name }}</td>
      <td>{{ ucfirst($order->status) }}</td>
      <td>
        <form method="POST" action="/admin/orders/{{ $order->id }}/update-status">
           @csrf
           <select name="status" onchange="this.form.submit()">
             <option>pending</option>
             <option>processing</option>
             <option>shipped</option>
             <option>delivered</option>
           </select>
        </form>
      </td>
    </tr>
  @endforeach
  </tbody>
</table>

⚙️ Step 6: Order Status Tracking (Customer View)

You can enhance the customer experience with a visual order progress bar, e.g.:

@if($order->status == 'pending')
  <div>🟡 Pending</div>
@elseif($order->status == 'processing')
  <div>🟢 Processing</div>
@elseif($order->status == 'shipped')
  <div>📦 Shipped</div>
@elseif($order->status == 'delivered')
  <div>✅ Delivered</div>
@endif

For advanced UX, store timestamps for each status change in a order_status_logs table to display a timeline.


⚙️ Step 7: Notifications (Optional)

  • Use Laravel Notifications or Mail to send:
    • Order confirmation email
    • Status update emails (e.g., shipped/delivered)
  • Improves trust and keeps customers informed

💡 Additional Tips

  • Paginate the customer orders list if they have many.
  • Allow invoice download (PDF).
  • Add order cancellation request if status is still pending.
  • Keep audit logs for admin actions on orders.

✅ Summary

You’ve now built:

  • Complete order history for each customer
  • Admin dashboard to manage orders
  • Real-time status tracking to keep customers updated

This feature makes your store feel truly professional and reliable.

Leave a Comment