Class: Cart
- A user can add a record to his shopping cart (we call it basket)
- Because every user has his own basket, we store the information in a session variable, called
cart
- Because we need different actions (add item to cart, remove item from cart, clear the cart, ...) on different places (shop detail page, basket overview, ...),
we'll make a new
Cart
class that handles all these actions- We use the same technique as with the Json class earlier in this project
- We can call these actions inside a view and inside a controller
What are sessions?
- A session can be defined as a server-side storage of information that is desired to persist throughout the user's interaction with the web site or web application
How are sessions stored?
- There are different ways to store a session
- Most of the time, we use file based sessions and that's also the default setting in Laravel
- Take a look at the session configuration file config/session.php
- The
driver
refers to the variableSESSION_DRIVER
inside the .env file - The
lifetime
refers to the variableSESSION_LIFETIME
inside the .env file - The
expire_on_close
is set tofalse
, so the session is still active even if you close the browser
- The
'driver' => env('SESSION_DRIVER', 'file'),
...
'lifetime' => env('SESSION_LIFETIME', 120),
'expire_on_close' => false,
1
2
3
4
2
3
4
- Now, take a look at the session variables inside .env
SESSION_DRIVER
is set tofile
SESSION_LIFETIME
is set to120
minutes, or 2 hours
SESSION_DRIVER=file
SESSION_LIFETIME=120
1
2
2
- When you login to the site, you'll find one or more files with a random name inside the folder storage/framework/sessions
- You can open a file to see what's inside a session
Handle sessions
Action | Laravel (helper) | Laravel (facade) | Pure PHP |
---|---|---|---|
set | session()->put('key', 'val') | Session::put('key', 'val') | $_SESSION['key'] = 'val' |
get | session()->get('key') | Session::get('key') | $_SESSION['key'] |
remove key | session()->forget('key') | Session::forget('key') | unset($_SESSION['key']) |
remove keys | session()->forget(['key1', 'key2']) | Session::->forget(['key1', 'key2']) | |
remove all keys | session()->flush() | Session::flush() | session_destroy() |
The Cart class
- Before we proceed to the
Cart
class itself, first take a look at the cart logic - Below you find an example of what's stored inside the session variable
cart
- 3 times the record with
$id = 6
( 3 * 19.99 € = 59.97 €) - 1 time the record with
$id = 3
( 1 * 24.99 € = 24.99 €) - Total items inside your basket = 4
- Total price for your basket = 59.97 € + 24.99 € = 84.96 €
- 3 times the record with
- The cart is an array with three keys:
records
contains an associative array where the key represents the record id and the value contains (an associative array with) all the fields we need in the orderlines tabletotalQty
contains the number of items in our carttotalPrice
contains the total price of our cart
- If we know the result, we can construct our
Cart
class:- We start with an empty (private) property
$cart
built according to the structure we just discussed - Then we have the constructor
- If the session variable
cart
exists, copy the session variable to the$cart
variable - Else, do nothing
- If the session variable
- After the constructor you'll find three methods to modify the cart
- Add a record to the cart (logic comes later)
- Delete a record from the cart (logic comes later)
- Empty the cart by removing the session variable cart
- With the six getters we can retrieve all the information (
getCart()
) or pieces of the information from the cart- We provide all possible combinations here, even if we may not need them (yet)
- The last (private) method calculates the total price and the number of items in our cart
- We start with an empty (private) property
Create the class
- Create a new PHP Class (not a new file) Cart.php inside the folder app/Helpers
(In PhpStorm: right-click on the folder and choose New -> PHP Class) - Replace the content of the class with this code:
<?php
namespace App\Helpers;
class Cart
{
private $cart = [
'records' => [],
'totalQty' => 0,
'totalPrice' => 0
];
// Cart constructor
public function __construct()
{
if (session()->get('cart')){
$this->cart = session()->get('cart');
}
}
// Add a record to the cart
public function add($item)
{
// add logic comes here
session()->put('cart', $this->cart); // save the session
}
// Delete a record from the cart
public function delete($item)
{
// delete logic comes here
session()->put('cart', $this->cart); // save the session
}
// Empty the cart
public function empty()
{
session()->forget('cart');
}
// Get the complete cart
public function getCart()
{
return $this->cart;
}
// Get all the records from the cart
public function getRecords()
{
return $this->cart['records'];
}
// Get one record from the cart
public function getOneRecord($key)
{
if (array_key_exists($key, $this->cart['records'])) {
return $this->cart['records'][$key];
}
}
// Get all the record keys
public function getKeys()
{
return array_keys($this->cart['records']);
}
// Get the number of items
public function getTotalQty()
{
return $this->cart['totalQty'];
}
// Get the total price
public function getTotalPrice()
{
return $this->cart['totalPrice'];
}
// Calculate the number of items and total price
private function updateTotal()
{
// calculate logic comes here
}
}
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
Add an alias to the class
- At the beginning of this course, we added the helper class Json to our project
- Remember that all files inside the folder app/Helpers are automatically loaded by Laravel
- To use the class
Cart
inside Blade we have to add an alias to this class- Open the file config/app.php and add an alias to the
aliases
array (don't forget theFacades
prefix)
- Open the file config/app.php and add an alias to the
'aliases' => [
…
'Json' => Facades\App\Helpers\Json::class,
'Mask' => Facades\App\Helpers\Mask::class,
'Cart' => Facades\App\Helpers\Cart::class,
]
1
2
3
4
5
6
2
3
4
5
6
Update code hinting
- To get code hinting inside our application, we have to update the ide-helper
- In the console, execute the following commands:
php artisan clear-compiled
php artisan ide-helper:generate
1
2
2
BasketController
- Create a new controller class BasketController.php in the folder app/Http/Controllers
- Run the command
php artisan make:controller BasketController
- Run the command
- Open the controller app/Http/Controllers/BasketController.php and add four methods that interact with our
Cart
class
class BasketController extends Controller
{
public function index()
{
return view('basket');
}
public function addToCart($id)
{
$record = Record::findOrFail($id);
Cart::add($record);
return back();
}
public function deleteFromCart($id)
{
$record = Record::findOrFail($id);
Cart::delete($record);
return back();
}
public function emptyCart()
{
Cart::empty();
return redirect('basket');
}
}
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
Add routes
- Open routes/web.php and add four new routes
- A GET route for showing the cart
- A GET route for adding a record to the cart
- A GET route for deleting a record from the cart
- A GET route to make the cart empty
Route::get('basket', 'BasketController@index');
Route::get('basket/add/{id}', 'BasketController@addToCart');
Route::get('basket/delete/{id}', 'BasketController@deleteFromCart');
Route::get('basket/empty', 'BasketController@emptyCart');
1
2
3
4
2
3
4
Basic view
- The basket only needs one view
REMARKS
- In order to test the logic inside the
Cart
class, we start with a simple list of three records that we can add/remove to our cart. At the bottom we show the content of our cart. - In the next chapter we update this page to a 'real' basket
- Update BasketController.php
public function index()
{
// Take the first 3 records, ordered by album title
$records = Record::orderBy('title')->take(3)->get();
$result = compact('records');
Json::dump($result);
return view('basket', $result);
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
- Make a basic view basket.blade.php inside the folder resources/views
- Open the file resources/views/basket.blade.php and update the view
- We make use PHP function json_encode() to return the information of the cart in JSON-format. Also the HTML <pre> tag is used.
@extends('layouts.template')
@section('title', 'Your Basket')
@section('main')
<h1>Basket</h1>
<table class="table">
<thead>
<tr>
<th>#</th>
<th>Artist - Album</th>
<th>Action</th>
</tr>
</thead>
<tbody>
@foreach($records as $record)
<tr>
<td>{{ $record->id }}</td>
<td>{{ $record->artist }} - {{ $record->title }}</td>
<td>
<div class="btn-group btn-group-sm">
<a href="/basket/add/{{ $record->id }}" class="btn btn-outline-success">+1</a>
<a href="/basket/delete/{{ $record->id }}" class="btn btn-outline-danger">-1</a>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
<a href="/basket/empty" class="btn btn-sm btn-outline-danger">Empty basket</a>
<h2 class="mt-5">What's inside my basket?</h2>
<hr>
<h4>Cart::getCart():</h4>
<pre>{{ json_encode(Cart::getCart(), JSON_PRETTY_PRINT) }}</pre>
<hr>
<h4>Cart::getRecords():</h4>
<pre>{{ json_encode(Cart::getRecords(), JSON_PRETTY_PRINT) }}</pre>
<hr>
<h4>Cart::getOneRecord(6):</h4>
<pre>{{ json_encode(Cart::getOneRecord(6), JSON_PRETTY_PRINT) }}</pre>
<hr>
<p><b>Cart::getKeys()</b>: {{ json_encode(Cart::getKeys()) }}</p>
<p><b>Cart::getTotalPrice()</b>: {{ json_encode(Cart::getTotalPrice()) }}</p>
<p><b>Cart::getTotalQty()</b>: {{ json_encode(Cart::getTotalQty()) }}</p>
@endsection
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
Add some logic to the Cart class
Add item to cart
- Open app/Helpers/Cart.php
- The
add($item)
method receives a parameter$item
, containing the selected record (as a collection) - First, extract the
id
and the the price ($singlePrice
) because we need this information later on - Check, with the PHP method array_key_exists(),
if there is an element with the key
$id
inside therecords
array (of our cart)- If there is NOT an element with that key:
- Add a new element with key
$id
to therecords
array- Extract the
id
,title
,artist
,cover
(which can benull
at the moment) andprice
from the collection - Set the quantity
qty
to 1
- Extract the
- Add a new element with key
- Else, if the element already exists
- Increment the quantity by 1
- Calculate the price by multiplying the new quantity with the price of a single item
$singlePrice
- If there is NOT an element with that key:
- Recalculate the total price and the number of items in our cart via
$this->updateTotal()
- Save the cart as a session variable
cart
public function add($item)
{
$id = $item->id;
$singlePrice = $item->price;
if (!array_key_exists($id, $this->cart['records'])) {
$this->cart['records'][$id] = [
'id' => $item->id,
'title' => $item->title,
'artist' => $item->artist,
'cover' => $item->cover,
'qty' => 1,
'price' => $item->price
];
} else {
$this->cart['records'][$id]['qty']++;
$this->cart['records'][$id]['price'] = $singlePrice * $this->cart['records'][$id]['qty'];
}
$this->updateTotal(); // update totalQty and totalPrice
session()->put('cart', $this->cart); // save the session
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Update number of items and total price in cart
- Start with the variables
$totalQty = 0
and$totalPrice = 0
- Loop over each record inside the cart and update
$totalQty
and$totalPrice
- Update the cart with the new values
private function updateTotal()
{
$totalQty = 0;
$totalPrice = 0;
foreach ($this->cart['records'] as $record) {
$totalQty += $record['qty'];
$totalPrice += $record['price'];
}
$this->cart['totalQty'] = $totalQty;
$this->cart['totalPrice'] = round($totalPrice, 2);
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
Delete item from cart
- The
delete($item)
method also receives a parameter$item
, containing the selected record (as a collection) - First extract the
id
and the the price ($singlePrice
) because we need this information later on - Check if there is an element with the key
$id
inside therecords
array (of our cart)- If there is an element with that key:
- Decrement the quantity by 1
- Check the new quantity
- If the quantity is NOT 0, then calculate the price by multiplying the new quantity with the price of a single item
$singlePrice
- If the quantity is 0, then remove (
unset()
) it from the array
- If the quantity is NOT 0, then calculate the price by multiplying the new quantity with the price of a single item
- Recalculate the total price and total quantity in our cart via
$this->updateTotal()
- If there is an element with that key:
- Save the cart as a session variable
cart
public function delete($item)
{
$id = $item->id;
$singlePrice = $item->price;
if (array_key_exists($id, $this->cart['records'])) {
$this->cart['records'][$id]['qty']--;
if ($this->cart['records'][$id]['qty'] != 0) {
$this->cart['records'][$id]['price'] = $singlePrice * $this->cart['records'][$id]['qty'];
} else {
unset($this->cart['records'][$id]);
}
$this->updateTotal();
}
session()->put('cart', $this->cart); // save the session
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
EXERCISE: Add cover and badge
- Refactor the controller so the cover can't be empty
- Add a green Bootstrap badge to the Basket link in the navigation
- The badge shows the number of items in your basket
- The badge is only visible if there is at least one item in your basket