This Laravel example includes simple shopping cart features as listed below. These functionalities help to meet the basic requirements of an online shopping cart application.
This example uses a Laravel package laravelshoppingcart
to achieve the above list of items. Earlier, we have seen how to create an add-to-cart option using this package. If you want to know the package integration procedure, the linked article is having the steps.
This example is mainly focusing on creating a wishlist tool for a shopping cart application. The wishlist add/putback
requests are handled via AJAX and give seamless responses.
The Laravel welcome template contains the HTML code for displaying products and cart tables. The package uses Bootstrap for UI styling.
Another Laravel template wishlist.blade.php
is created for this example to list the items moved to the wishlist from the cart.
Create a databaseĀ db_laravelshoppingcart
and configure it into the Laravel .env
file. When we used the Laravelshoppingcart
package before, it showed a database-free shopping cart. In this example, it uses the database for storing the products.
Run the below make: migration
and migration
commands in a terminal to create and then move a ‘products’ table to the database. The migration file shows the table structure with the auto-generation ID, product name and price column, and timestamp fields.
php artisan make:migration create_products_table
create_products_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->decimal('price', 10, 2);
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('products');
}
};
php artisan migrate
This Laravel seeder class is designed to import the ‘products’ sample data. This seeder can be altered to add more insert queries to add more records as an init-data.
-- Run this command to create the seeder file to the Laravel app database/seeder folder
php artisan make:seeder ProductsSeeder
ProductsSeeder.php
<?php
namespace Database\Seeders;
use App\Models\Product;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class ProductsSeeder extends Seeder
{
public function run()
{
$products = [
[
'name' => 'LOIS CARON Watch',
'price' => 500,
]
];
Product::insert($products);
}
}
-- Run this command by specifying the Laravel seeder class name
php artisan db:seed --class=ProductsSeeder
This controller has theĀ showProductDetails()
function mapped for this example’s landing page route. It helps to view the welcome template to list all the products by dynamically iterating the $product
array built in this case.
ProductController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\View;
use Illuminate\Http\Request;
use App\Models\Product;
class ProductController extends Controller
{
public function showProductDetails()
{
$products = Product::all();
return view('welcome', compact('products'));
}
}
This controller class has handles for all the cart actions supported by this example using the Laravelshoppingcart
package. It implements the following simple shopping cart functionalities by using this package.
a) Add to cart from the product gallery.
b) List cart items from the session.
c) Edit a quantity, or delete a particular cart item.
e) Clear the cart.
CartController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Darryldecode\Cart\Facades\CartFacade as Cart;
use App\Models\Product;
class CartController extends Controller
{
public function addToCart(Request $request)
{
$productId = $request->input('product_id');
// Retrieve product details from the database by product ID
$product = Product::find($productId);
if (!$product) {
return response()->json(['error' => 'Product not found'], 404);
}
$userID = 2;
try {
// Add the product to the cart
\Cart::session($userID)->add([
'id' => $product->id,
'name' => $product->name,
'price' => $product->price,
'quantity' => 1,
'attributes' => [], // You can add additional attributes if needed
'associatedModel' => $product,
]);
return response()->json(['success' => true]);
} catch (\Darryldecode\Cart\Exceptions\InvalidItemException $e) {
return response()->json(['error' => $e->getMessage()], 422); // 422 is the Unprocessable Entity HTTP status code
}
}
public function updateCart(Request $request)
{
$productId = $request->input('product_id');
$quantity = $request->input('quantity');
$userID = 2;
\Cart::session($userID)->update($productId, array(
'quantity' => array(
'relative' => false,
'value' => $quantity
),
));
return response()->json(['success' => true]);
}
public function showCart()
{
$products = Product::all();
$userID = 2;
$cartContent = \Cart::session($userID)->getContent();
$cartCount = $cartContent->count();
$total = \Cart::getTotal();
$wishlistItems = Cart::session('wishlist')->getContent();
$wishlistCount = $wishlistItems->count();
$wishlistProductIds = $wishlistItems->pluck('id')->toArray();
return view('welcome')
->with('products', $products)
->with('wishlistProductIds', $wishlistProductIds)
->with('cartCount', $cartCount)
->with('wishlistCount', $wishlistCount)
->with('cartContent', $cartContent)->with('total', $total);
}
public function removeCart(Request $request)
{
$productId = $request->input('product_id');
$userID = 2;
\Cart::session($userID)->remove($productId);
return response()->json(['success' => true]);
}
public function clearCart(Request $request)
{
$userID = 2;
\Cart::session($userID)->clear();
return response()->json(['success' => true]);
}
}
The wishlist actions supported by this Laravel app are listed below. Like the cart UI, the wishlist template also has controls to perform certain operations.
a) Add to wishlist
addToWishlist()
: It moves a cart item to the wishlist. PHP session is used for managing the items added to the cart and the wishlist.
An item is added to the wishlist session using the add()
method. It is done by the reference of the selected product id which is used as an index. This ID helps to find the product details using the Product::find($id)
.
b) Show wishlist
viewWishlist()
: It retrieves all the products added to the wishlist using the getContent()
.
It calculates the wishlist and cart count to display in the header. It renders the ‘wishlist’ view by supplying the variables to display the wishlist array, cart, and wishlist items count for the shopping cart application header.
c) Remove the wishlist item
removeFromWishlist()
: It clears a particular product from the wishlist based on the provided ID. It runs the Cart::session('wishlist')->remove($productId)
to achieve this action.
d) Back to cart
backToCart()
: It puts the wishlist item back in the cart. It checks if the product is already in the cart. If it is there then this action updates the quantity otherwise add a new cart entry. It prevents having duplicate rows in the cart which will create confusion and increase drop-offs during the purchase.
WishlistController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Darryldecode\Cart\Facades\CartFacade as Cart;
use App\Models\Product;
class WishlistController extends Controller
{
public function addToWishlist(Request $request)
{
$userID = 2;
$productId = $request->input('product_id');
$quantity = $request->input('quantity');
// Retrieve product details from the database by product ID
$product = Product::find($productId);
if (!$product) {
return response()->json(['error' => 'Product not found'], 404);
}
// Adding item to the wishlist
Cart::session('wishlist')->add([
'id' => $productId,
'name' => $product->name,
'price' => $product->price,
'quantity' => $quantity,
'attributes' => [],
]);
\Cart::session($userID)->remove($productId);
$redirectUrl = '/wishlist';
return response()->json([
'message' => 'Product added to wishlist successfully',
'redirect_url' => $redirectUrl
]);
}
public function viewWishlist()
{
$userID = 2;
$cartContent = \Cart::session($userID)->getContent();
$cartCount = $cartContent->count();
// Retrieving items from the wishlist
$wishlistItems = Cart::session('wishlist')->getContent();
$wishlistCount = $wishlistItems->count();
return view('wishlist')
->with('cartCount', $cartCount)
->with('wishlistCount', $wishlistCount)
->with('wishlistItems', $wishlistItems);
}
public function removeFromWishlist(Request $request)
{
$userID = 1;
$productId = $request->input('product_id');
// Removing item from the wishlist
Cart::session('wishlist')->remove($productId);
}
public function backToCart(Request $request)
{
$userID = 2;
$productId = $request->input('product_id');
$quantity = $request->input('quantity');
$product = Product::find($productId);
$cartItems = \Cart::session($userID)->getContent();
$existingCartItem = $cartItems->get($productId);
if ($existingCartItem) {
// If the product is already in the cart, update the quantity
\Cart::session($userID)->update($productId, array(
'quantity' => $quantity
));
$message = 'Item updated in the cart successfully.';
} else {
// If the product is not in the cart, add it to the cart
\Cart::session($userID)->add($product->id, $product->name, $product->price, $quantity);
$message = 'Item added to the cart successfully.';
}
Cart::session('wishlist')->remove($productId);
$redirectUrl = '/';
return response()->json([
'message' => $message,
'redirect_url' => $redirectUrl
]);
}
}
Copy these rules and paste them into Laravel web routes. It defines the endpoints for managing the list of products, cart items, and wishlists within the application. It also contains the routes for performing the cart and wishlist actions.
routes/web.php
<?php
Route::get('/', function () {
return view('welcome');
});
Route::get('/', [ProductController::class, 'showProductDetails']);
Route::post('/add-to-cart', [CartController::class, 'addToCart'])->name('addToCart');
Route::post('/update-cart', [CartController::class, 'updateCart'])->name('updateCart');
Route::post('/remove-cart', [CartController::class, 'removeCart'])->name('removeCart');
Route::post('/clear-cart', [CartController::class, 'clearCart'])->name('clearCart');
Route::get('/', [CartController::class, 'showCart'])->name('cart.show');
Route::get('/wishlist', [WishlistController::class, 'viewWishlist'])->name('wishlist.view');
Route::post('/wishlist/add', [WishlistController::class, 'addToWishlist'])->name('addToWishlist');
Route::post('/wishlist/back', [WishlistController::class, 'backToCart'])->name('backToCart');
Route::post('/wishlist/remove', [WishlistController::class, 'removeFromWishlist'])->name('removeFromWishlist');
The landing HTML template of this Laravel example integrates bootstrap styling for the product and cart table view. The cart view shows additional action ‘to move an item to the wishlist session’. The other actions that are add/remove items, we described a lot in earlier Laravel shopping cart articles.
welcome.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=figtree:400,600&display=swap" rel="stylesheet" />
<style>
.product-container {
background-color: #fad875;
padding: 30px 30px 15px 30px;
}
</style>
</head>
<body>
<div class="container">
<nav class="navbar navbar-light bg-light">
<div class="container-fluid">
<h3>Laravel Cart</h3>
<div class="d-flex">
<a href="/" class="mx-3 text-decoration-none"><img class="mx-1" src="{{ asset('images/shopping-cart.svg') }}"> <span class="text-dark">{{ $cartCount }}</span></a>
<a href="/wishlist" class="text-decoration-none">View Wishlist<span class="text-dark">({{ $wishlistCount }})</span></a>
</div>
</div>
</nav>
<h1 class="text-center mb-3 mt-5">All Products</h1>
<div class="product-container">
<table class="table table-bordered">
<thead>
<tr>
<th scope="col" style="width: 50%;">Product Name</th>
<th scope="col" style="width: 15%;" class="text-center">Quantity</th>
<th scope="col" style="width: 15%;" class="text-end">Price</th>
<th scope="col" class="text-center">Action</th>
</tr>
</thead>
<tbody>
@foreach ($products as $k => $productDetails)
<tr>
<td>{{ $productDetails['name'] }}</td>
<td class="text-center">
1
</td>
<td class="text-end">${{ number_format($productDetails['price'],2) }}</td>
<td class="text-center"><button type="button" class="btn btn-warning add-to-cart-btn cursor-pointer" data-product-id="{{ $productDetails['id'] }}"><span class="add-text">Add to Cart</span> <span class="d-none adding-text"> Adding</span> <span class="d-none added-text"> Added</span></button>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<div class="mt-5">
<div class="d-flex justify-content-between">
<h2>My Cart</h2>
<button type="button" class="btn btn-danger mb-3 clear-cart"><span class="clear-text">Clear Cart</span><span class="d-none clearing-text"> Clearing</span> <span class="d-none cleared-text"> Cleared</span></button>
</div>
<table class="table table-bordered">
<thead>
<tr>
<th style="width: 40%;">Product Name</th>
<th class="text-center" style="width: 10%;">Quantity</th>
<th class="text-end" style="width: 15%;">Price</th>
<th class="text-end" style="width: 15%;">Sub Total</th>
<th class="text-center">Action</th>
</tr>
</thead>
<tbody>
@foreach($cartContent as $item)
<tr>
<td>{{ $item->name }}</td>
<td class="text-center">
<div class="input-group d-flex justify-content-center"><input type="number" min="1" class="form-control quantity-input text-center" value="{{ $item->quantity }}"></div>
<a href="javascript:;" class="update-btn cursor-pointer text-decoration-none" data-product-id="{{ $item->id }}"><span class="update-text">Update</span><span class="d-none updating-text"> Updating</span> <span class="d-none updated-text"> Update</span></a>
</td>
<td class="text-end">${{ number_format($item->price,2) }}</td>
<td class="text-end">${{ number_format($item->quantity * $item->price,2)}}</td>
<td class="text-center">
@if (!in_array($item->id, $wishlistProductIds))
<button type="button" class="btn btn-outline-primary add-to-wishlist" data-product-id="{{ $item->id }}"><span class="add-text">Add to wishlist</span><span class="d-none adding-text"> Adding</span> <span class="d-none added-text"> Added</span></button>
@endif
<button type="button" class="btn btn-outline-danger remove-btn" data-product-id="{{ $item->id }}"><span class="remove-text">Remove</span><span class="d-none removing-text"> Removing</span> <span class="d-none removed-text"> Removed</span></button>
</td>
</tr>
@endforeach
<tr>
<th colspan="3" class="text-end">Total</th>
<td class="text-end">${{ number_format($total,2) }}</td>
<td></td>
<tr>
</tbody>
</table>
</div>
</div>
The cart action requests are sent via AJAX by using the below script. Newly, this example contains a listener to map the .add-to-wishlist
element click-event.
It gets the product ID and quantity from the data attribute and updates the wishlist on the UI. At the same time, it updates the backend PHP session array to add a new item to the wishlist.
After it performs an AJAX POST request to the {addToWishlist}
route, the backend processing happens and returns the response accordingly.
welcome.blade.php
(Part of welcome template)
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$('.add-to-cart-btn').on('click', function() {
var button = $(this);
var productId = button.data('product-id');
button.find('.add-text').addClass('d-none');
button.find('.adding-text').removeClass('d-none');
// Make an AJAX request to add the product to the cart
$.ajax({
type: 'POST',
url: '{{ route('addToCart') }}',
data: {
product_id: productId,
_token: '{{ csrf_token() }}',
},
success: function(response) {
button.find('.adding-text').addClass('d-none');
button.find('.added-text').removeClass('d-none');
location.reload();
},
error: function(error) {
console.error('Error adding product to cart:', error);
}
});
});
$('.update-btn').on('click', function() {
var button = $(this);
var productId = button.data('product-id');
var quantity = button.closest('td').find('.quantity-input').val();
button.find('.update-text').addClass('d-none');
button.find('.updating-text').removeClass('d-none');
// Make an AJAX request to update the product to the cart
$.ajax({
type: 'POST',
url: '{{ route('updateCart') }}',
data: {
product_id: productId,
quantity: quantity,
_token: '{{ csrf_token() }}',
},
success: function(response) {
button.find('.updating-text').addClass('d-none');
button.find('.updated-text').removeClass('d-none');
location.reload();
},
error: function(error) {
console.error('Error updating product to cart:', error);
}
});
});
$('.remove-btn').on('click', function() {
var button = $(this);
var productId = button.data('product-id');
button.find('.remove-text').addClass('d-none');
button.find('.removing-text').removeClass('d-none');
// Make an AJAX request to remove the product from the cart
$.ajax({
type: 'POST',
url: '{{ route('removeCart') }}',
data: {
product_id: productId,
_token: '{{ csrf_token() }}',
},
success: function(response) {
button.find('.removing-text').addClass('d-none');
button.find('.removed-text').removeClass('d-none');
location.reload();
},
error: function(error) {
console.error('Error adding product to cart:', error);
}
});
});
$('.clear-cart').on('click', function() {
var button = $(this);
if (confirm('Are you sure you want to clear the cart?')) {
button.find('.clear-text').addClass('d-none');
button.find('.clearing-text').removeClass('d-none');
// Make an AJAX request to clear the product to the cart
$.ajax({
type: 'POST',
url: '{{ route('clearCart') }}',
data: {
_token: '{{ csrf_token() }}',
},
success: function(response) {
button.find('.clearing-text').addClass('d-none');
button.find('.cleared-text').removeClass('d-none');
location.reload();
},
error: function(error) {
console.error('Error adding product to cart:', error);
}
});
}
});
$('.add-to-wishlist').on('click', function() {
var button = $(this);
var productId = button.data('product-id');
var quantity = button.closest('tr').find('.quantity-input').val();
button.find('.add-text').addClass('d-none');
button.find('.adding-text').removeClass('d-none');
$.ajax({
type: 'POST',
url: '{{ route('addToWishlist') }}',
data: {
product_id: productId,
quantity: quantity,
_token: '{{ csrf_token() }}',
},
success: function(response) {
button.find('.adding-text').addClass('d-none');
button.find('.added-text').removeClass('d-none');
// Handle success response if needed
if (response.redirect_url) {
// Perform the redirect
window.location.href = response.redirect_url;
}
},
error: function(xhr, status, error) {
console.error('Error adding product to wishlist:', error);
console.log(xhr.responseText); // Log the entire response for more details
}
});
});
</script>
This HTML template shows the wishlist layout for managing the list of items added to it. This Laravelshoppingcart
package supports adding/removing products to and from the wishlist session.
wishlist.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=figtree:400,600&display=swap" rel="stylesheet" />
<style>
.product-container {
background-color: #fad875;
padding: 30px 30px 15px 30px;
}
</style>
</head>
<body>
<div class="container">
<nav class="navbar navbar-light bg-light">
<div class="container-fluid">
<h3>Laravel Cart</h3>
<div class="d-flex">
<a href="/" class="mx-3 text-decoration-none"><img class="mx-1" src="{{ asset('images/shopping-cart.svg') }}"> <span class="text-dark">{{ $cartCount }}</span></a>
<a href="/wishlist" class="text-decoration-none">View Wishlist<span class="text-dark">({{ $wishlistCount }})</span></a>
</div>
</div>
</nav>
<h1 class="text-center mb-3 mt-5">Wishlist</h1>
<div class="product-container">
<table class="table table-bordered">
<thead>
<tr>
<th scope="col" style="width: 30%;">Product Name</th>
<th scope="col" style="width: 15%;" class="text-center">Quantity</th>
<th scope="col" style="width: 15%;" class="text-end">Price</th>
<th scope="col" style="width: 15%;" class="text-end">Sub total</th>
<th scope="col" class="text-center">Action</th>
</tr>
</thead>
<tbody>
@foreach ($wishlistItems as $k => $wishlist)
<tr>
<td>{{ $wishlist['name'] }}</td>
<td class="text-center">
{{ $wishlist['quantity'] }}
</td>
<td class="text-end">${{ number_format($wishlist['price'],2) }}</td>
<td class="text-end">${{ number_format($wishlist->quantity * $wishlist->price,2)}}</td>
<td class="text-center">
<button class="btn btn-outline-primary back-to-cart" data-product-id="{{ $wishlist->id }}"><span class="add-text">Putback to cart</span><span class="d-none adding-text"> Adding</span> <span class="d-none added-text"> Added</span></button>
<button class="btn btn-outline-danger remove-wishlist" data-product-id="{{ $wishlist->id }}"><span class="remove-text">Remove</span><span class="d-none removing-text"> Removing</span> <span class="d-none removed-text"> Removed</span></button>
</td>
<input type="hidden" value="{{ $wishlist['quantity'] }}" class="quantity-input">
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
Similar to the cart action script, the Laravel application contains AJAX handlers for performing the wishlist actions. There are two actions supported in this example with the wishlist session.
It moves a particular item back to the cart session from the wishlist. It uses the product ID as a reference for this operation.
On changing, it clears the particular entry from the wishlist and adds the same to the cart.
The UI update is static to increase the speed, but the backend operations are performed asynchronously via AJAX to make the change permanent.
A JavaScript listener detects the click event of the .remove-wishlist
element and performs the AJAX wishlist item delete. It calls the API endpoint {removeFromWishlist}
and clears the entry in the success callback.
wishlist.blade.php
(Part of wishlist template)
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$('.back-to-cart').on('click', function() {
var button = $(this);
var productId = $(this).data('product-id');
var quantity = $(this).closest('tr').find('.quantity-input').val();
button.find('.add-text').addClass('d-none');
button.find('.adding-text').removeClass('d-none');
// Make an AJAX request to remove the product from the cart
$.ajax({
type: 'POST',
url: '{{ route('backToCart') }}',
data: {
product_id: productId,
quantity: quantity,
_token: '{{ csrf_token() }}',
},
success: function(response) {
button.find('.adding-text').addClass('d-none');
button.find('.added-text').removeClass('d-none');
if (response.redirect_url) {
// Perform the redirect
window.location.href = response.redirect_url;
}
},
error: function(error) {
console.error('Error adding product to cart:', error);
}
});
});
$('.remove-wishlist').on('click', function() {
var button = $(this);
var productId = $(this).data('product-id');
button.find('.remove-text').addClass('d-none');
button.find('.removing-text').removeClass('d-none');
// Make an AJAX request to remove the product from the cart
$.ajax({
type: 'POST',
url: '{{ route('removeFromWishlist') }}',
data: {
product_id: productId,
_token: '{{ csrf_token() }}',
},
success: function(response) {
button.find('.removing-text').addClass('d-none');
button.find('.removed-text').removeClass('d-none');
location.reload();
},
error: function(error) {
console.error('Error adding product to cart:', error);
}
});
});
</script>