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
- Run the command
- Add the methods
index()
to show the full order history andcheckout()
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
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
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
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 thetotal_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
- First you have to insert the
- Update the
checkout()
method inside theHistoryController
- 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
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 theHistoryController
- 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
- Get all the orders from the user that's logged in
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
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
- Use Laravel's isEmpty() method to check if the collection
@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
2
3
4
5
6
7
8
9
10
11
- 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
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
- 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
2
3
4
5
6
7
8
9
EXERCISE: Fine-tune the checkout login
- When a user checks out, the
stock
of each record must be decremented with the ordered quantity - The
stock
can't be less then 0 - 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:
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 inBasketController
(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.
- More precisely, you can check in the
- 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
- This can be done (server-side) by updating the
- 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