User: order history

  • On the Order history page, a user can view all the orders he/she has ever placed
  • But before we can show the history, we first have to create the code behind the Checkout link on the basket page

Preparation

Create controller

  • Create a new HistoryController in the folder app/Http/Controllers/User
    • Run the command php artisan make:controller User/HistoryController
  • Add the methods index() to show the full order history and checkout() to add the data from the cart to the database
class HistoryController extends Controller
{
    // Show the full order history
    public function index ()
    {
        return view('user.history');
    }
    
    // Add data from cart to the database
    public function checkout()
    {
        
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Basic view

  • Make a basic view history.blade.php inside the folder resources/views/user
  • Open the file resources/views/user/history.blade.php and update the view
@extends('layouts.template')

@section('title', 'Order History')

@section('main')
    <h1>Order History</h1>
    @include('shared.alert')
@endsection
1
2
3
4
5
6
7
8

Add routes

  • Open routes/web.php and add two new routes
    • One GET route for showing the order history
    • One GET route for adding the (data from the) cart to the database


 
 


Route::middleware(['auth'])->prefix('user')->group(function () {
    ...
    Route::get('history', 'User\HistoryController@index');
    Route::get('checkout', 'User\HistoryController@checkout');
});
1
2
3
4
5

Checkout

  • Login to the site and add some records to your basket
  • Take a look at the database and at the cart:
    • First you have to insert the user_id and the total_price from the cart session into a new row in the orders table.
    • Next, retrieve the order_id from the just inserted row
    • Finally, loop over all the records inside the cart and add a new row (with the order_id and the necessary columns) to the orderlines table

Database model Cart session variable

  • Update the checkout() method inside the HistoryController
    • Create a new order and save it in the orders table
    • Retrieve the id of the last inserted order
    • Loop over the records array in the cart and save them in the orderlines table
    • Empty the cart
    • Redirect to the history page


 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


public function checkout()
{
    // Create a new order and save it to the orders table
    $order = new Order();
    $order->user_id = auth()->id();
    $order->total_price = Cart::getTotalPrice();
    $order->save();
    // Retrieve the id of the last inserted order
    $order_id = $order->id;
    // Loop over the records array in the cart and save them to the orderlines table
    foreach (Cart::getRecords() as $record) {
        $orderline = new Orderline();
        $orderline->order_id = $order_id;
        $orderline->artist = $record['artist'];
        $orderline->title = $record['title'];
        $orderline->cover = $record['cover'];
        $orderline->total_price = $record['price'];
        $orderline->quantity = $record['qty'];
        $orderline->save();
    }
    // Empty the cart 
    Cart::empty();
    // Redirect to the history page
    $message = 'Thank you for your order.<br>The records will be delivered as soon as possible.';
    session()->flash('success', $message);
    return redirect('/user/history');
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

History

The history is a static view with a card for every order the user made so far

Update the controller

  • Update the index() method inside the HistoryController
    • Get all the orders from the user that's logged in
      • Include the orderlines
      • Order the result in descending order by date
    • Send the result to the history view


 
 
 
 
 
 
 


public function index()
{
    $orders = Order::where('user_id', auth()->id())
        ->with('orderlines')
        ->orderBy('created_at', 'desc')
        ->get();
    $result = compact('orders');
    Json::dump($result);
    return view('/user/history', $result);
}
1
2
3
4
5
6
7
8
9
10

Update the view

  • Open resources/views/user/history.blade.php and update the main section
  • Show a message if the user didn't make any orders yet
    • Use Laravel's isEmpty() method to check if the collection $orders is empty



 
 
 
 
 
 
 


@section('main')
    <h1>Order History</h1>
    @include('shared.alert')
    @if($orders->isEmpty())
        <div class="alert alert-primary">
            You haven't placed any orders yet.
        </div>
    @else
        <!-- order cards come here -->
    @endif
@endsection
1
2
3
4
5
6
7
8
9
10
11

No orders yet

  • If the user has already placed some orders, we show each of them in a separate Bootstrap card

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


    @else
        @foreach($orders as $order)
            <div class="card mb-3">
                <h5 class="card-header">
                    <b>Order date</b>: {{ $order->created_at }}
                </h5>
                <div class="card-body">
                    @foreach($order->orderlines as $orderline)
                        <div class="d-flex">
                            <div>
                                <img class="img-thumbnail cover width-80" src="/assets/vinyl.png"
                                     data-src="{{ $orderline->cover }}"
                                     alt="{{ $orderline->title }}">
                                <p class="text-center mt-2">{{ $orderline->total_price }}</p>
                            </div>
                            <p class="px-3">
                                <b>{{ $orderline->artist . ' - ' . $orderline->title  }}</b><br>
                                <span class="text-black-50">Quantity:{{ $orderline->quantity }} </span>
                            </p>
                        </div>
                    @endforeach
                </div>
                <div class="card-footer">
                     <b>Total price</b>:{{ $order->total_price }}
                </div>
            </div>
        @endforeach
 @endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

Card without cover

  • Replace the dummy cover with the real cover
@section('script_after')
    <script>
        $(function () {
             $('.cover').each(function () {
                $(this).attr('src', $(this).data('src'));
            });
        });
    </script>
@endsection
1
2
3
4
5
6
7
8
9

Card with cover

EXERCISE: Fine-tune the checkout login

  1. When a user checks out, the stock of each record must be decremented with the ordered quantity
  2. The stock can't be less then 0
  3. If someone ordered more records than the stock amount, then you show a message with the records that are in backorder
    • E.g. someone ordered 3 items of Sticky Fingers from The Rolling Stones and we have only 1 item in stock, then this is the message you will see on the history page:

Fine-tune checkout logic

REMARKS

  • The "backorder system" suggested in the above exercise finalizes our order (history) cycle. Yet, the following imperfections can be observed:
    • A backorder system is perhaps not be the best idea for (rare and hard to get) vinyl records
    • Our current implementation does not allow to order records that are not in stock. If a record is in stock, however, you can (back)order more copies than the stock amount.
  • A better (but challenging!) approach might be to prevent the user from adding more copies of a record to its basket than the stock of the corresponding record
    • This can be done (server-side) by updating the addToCart() method in BasketController (where you arrive after clicking the 'Add to cart' button on a shop detail page or a '+' button on the basket page)
      • More precisely, you can check in the addToCart() method if there are enough records in stock to add an additional copy to the cart. If not, you can show a message (alert) on the (shop detail or basket) view.
    • In addition, you can improve the (client-side) UI by disabling (and changing the cursor property of) the corresponding buttons (the 'Add to cart' button on a shop detail page or a '+' button on the basket page) if there are not enough records in stock to allow ordering an additional copy
  • And if you go for perfection, you should even take into account "nearly simultaneous" orders. What if a user has some records in its basket (and cart session) that turn out to be out of stock (due to a recently placed order of another user) when he/she checks out?

Example video of improved order cycle

  • Between 02:49 and 03:12 we simulate the (nearly simultaneous) order (1 x Bob Marley - Uprising and 1 x Atari Teenage Riot - The Future Of War) of another user by decreasing the stock amount of both records by 1 in the database
 
Last Updated: 6/9/2020, 2:12:33 PM