Implementing a Shopping Cart in Laravel

Update December 2017
I started a YouTube series based off this blog post but with more features including checkout integeration with Stripe. I’m using the latest version of Laravel and will continue to add more features as the series progresses. Check it out!
Update March 2017
Upgraded to Laravel 5.4
Update December 2016
I’ve updated this demo to Laravel 5.3 as well as the latest version of the Crinsane/LaravelShoppingcart package. Have a look at the commit on GitHub for the changes. There were a few changes in the shopping cart package, but the most annoying thing was the renaming of a variable from $rowid
to $rowId
.
End Update December 2016
Let’s build a practical example of an e-commerce website that has shopping cart functionality. To be clear, we will be using an existing composer package that already has shopping cart functionality and building on top of that. We are not building one from scratch. I’m going to assume you have the basics of Laravel down. If not, head over to Laracasts and watch this wonderful Laravel from Scratch series.
Choosing a Shopping Cart Package
- After some research and googling, I decided to go with the Crinsane/LaravelShoppingcart package. I found it had everything I needed, together with a simple API to work with.
-
Cartalyst also has a great package appropriately named “Cart”. Check out the demo page of it in action. Note, this package is not free and part of their paid service.
-
Another honourable mention is the darryldecode/laravelshoppingcart package. The API also looks very clean, and offers all the basic functionality that one would expect from a shopping cart.
Building the UI
I like to start off by styling the app. I usually make use of Laravel Elixir for front end goodies like SASS compilation, concatenation, minification, live reload, etc. However, for this demo I minimized the amount of custom CSS and just used default bootstrap styling.
The demo consists of three views:
- The shop page:
resources/views/shop.blade.php
View containing all of your products or a subset of products
- The product page:
resources/views/product.blade.php
View containing all the details of a SINGLE product
- The cart page:
resources/views/cart.blade.php
View containing the contents of your shopping cart
The Database
We need a products table to store the products in the database. Here is (a snippet of) my migration for creating the products table:
public function up() { Schema::create('products', function (Blueprint $table) { $table->increments('id'); $table->string('name')->unique(); $table->string('slug')->unique(); $table->text('description'); $table->decimal('price', 10, 2); $table->string('image')->unique(); $table->timestamps(); }); }
Note: In an effort to keep things simple, I’m just storing the one image filename as a string. In reality, you would probably have multiple images for each product. In that case, I would create a photos table and have a one to many relationship between products and photos.
There’s also a products table seeder in database/seeds/ProductsTableSeeder.php
. Usually I would use Model Factories for testing. However, since we are only dealing with a small amount of data, I just manually seeded the database.
Making Things Dynamic
At this point, the shop page and product page are hardcoded with product information. Let’s make things dynamic and pull info from our database.
The Shop Page
Let’s start off by making our Product model: php artisan make:model Product
Let’s make a partial resource route in our routes file: Route::resource('shop', 'ProductController', ['only' => ['index', 'show']]);
. I named the resource shop
as opposed to product
because I want the URL to be something like example.com/shop/product-name.
Let’s make a Product Controller: php artisan make:controller ProductController --resource
.
We’re only going to need the index and show methods for displaying all products and displaying a single product, respectively. You can delete the other ones.
Here’s what our Product Controller’s index method looks like:
public function index() { $products = Product::all(); return view('shop')->with('products', $products); }
And a snippet of the corresponding view in resources/views/shop.blade.php
@foreach ($products->chunk(4) as $items) <div class="row"> @foreach ($items as $product) <div class="col-md-3"> // html to render a product </div> <!-- end col-md-3 --> @endforeach </div> <!-- end row --> @endforeach
Note: I’m using the chunk
method because a div with a class of ‘row’ has to be wrapped around every 4 products.
At this point, our shop page should now be pulling products from the database!
The Product Page
Here’s what our Product Controller’s show method looks like:
public function show($slug) { $product = Product::where('slug', $slug)->firstOrFail(); return view('product')->with('product', $product); }
This searches our database for a slug and returns it to the view.
The view in resources/views/product.blade.php
renders the appropriate information.
I’ve also added a “You May Also Like” section on the product page. This is common on a lot of e-commerce sites. To be honest, I only added it to take up more vertical space. Our implementation will just randomly pick a product that is not itself. Add the following code to your Product Controller:
public function show($slug) { $product = Product::where('slug', $slug)->firstOrFail(); $interested = Product::where('slug', '!=', $slug)->get()->random(4); return view('product')->with(['product' => $product, 'interested' => $interested]); }
Render the $interested collection in the view and it should display “you may also like” products correctly!
Implementing Shopping Cart Functionality
We can now work on getting the shopping cart up and running.
Read the documentation for Crinsane/LaravelShoppingcart and follow the steps to get up and running.
Let’s make a partial resource route in our routes file: Route::resource('shop', 'CartController', ['only' => ['index', 'store', 'update', 'destroy']]);
.
Let’s make a Cart Controller: php artisan make:controller CartController --resource
.
We’re only going to need the index, store, update and destroy methods. You can delete the other ones.
Let’s have a look at the controller:
public function index() { return view('cart'); }
The index method is responsible for showing all contents of the cart. We need the Cart contents and the Cart total price to pass to the view. I just chose to use the Cart::content()
and Cart::total()
facades directly within the cart.blade.php
view.
public function store(Request $request) { Cart::associate('Product','App')->add($request->id, $request->name, 1, $request->price); return redirect('cart')->withSuccessMessage('Item was added to your cart!'); }
The store method is responsible for adding items to the cart. The associate method allows you to associate a model with the cart. This way we can access our model fields directly. The request parameters are coming from a form found in the product.blade.php
view.
<form action="/cart" method="POST"> {!! csrf_field() !!} <input type="hidden" name="id" value="{{ $product->id }}"> <input type="hidden" name="name" value="{{ $product->name }}"> <input type="hidden" name="price" value="{{ $product->price }}"> <input type="submit" class="btn btn-success btn-lg" value="Add to Cart"> </form>
public function destroy($id) { Cart::remove($id); return redirect('cart')->withSuccessMessage('Item has been removed!'); }
The destroy method removes a particular item from the cart.
To update the cart quantity in the “Cart” menu item in the top right, we simply reference Cart::count
in the view (in this case, it’s in the master layout: resources/views/master.blade.php
Note: For the sake of simplicity, I did not include the ability to update quantities in this demo. Typically speaking, you should be able to do this on the cart page. You would just do something like Cart::update($rowId, 2);
within the update method of the Cart Controller.
I’ve now added the ability to update quantities for products in the shopping cart. On the cart page, you will see quantities are represented via a dropdown. Upon changing the quantity, an AJAX request will be sent out to update the quantity accordingly. Take a look at the update method in the Cart Controller as well as the JavaScript located in resources/views/cart.blade.php
. (Yeah, I’m lazy to extract to a dedicated .js file 🙂 )
Another feature that you see on some e-commerce sites is multiple instances for shopping carts. For example, you would have your main shopping cart along with a wishlist. I’ve added this functionality as well, with the ability to move products to and from the main shopping cart and the wishlist.
42 Comments
Awesome
can check user login or not before adding item to shopping cart
Great demo and description !!
i sovled,
CartController.php
public function store(Request $request)
{
if(isset(Auth::user()->name)){
if (Cart::search([‘id’ => $request->id])) {
return redirect(‘cart’)->withSuccessMessage(‘Item is already in your cart!’);
}
Cart::associate(‘Product’,’App’)->add($request->id, $request->name, 1, $request->price);
return redirect(‘cart’)->withSuccessMessage(‘Item was added to your cart!’);
}else{
Cart::destroy();
return redirect(‘cart’)->withErrorMessage(‘您尚未登入, 請先登入’);
}
}
Yeah or even better, add this constructor in your CartController class, before the other methods:
public function __construct()
{
$this->middleware(‘auth’);
}
It will check for authentication for the entire class.
Many thanks,
Hi
i encounter a problem the shopping cart qty does not work , ‘the ajax patch….’ ajax method in cart.blade.php file ,
if the ajax in Laravel inconvenient ?? is it??
do you have idea on this?
OK, it works now,take care of CSRF , PATH …
thanks for your artical again
Hi aquahuang, having problem with ajax method too.
Do you mind shearing how you took care ‘of CSRF , PATH …’
Thanks.
thank you so much for this!
It would be great if you could follow this up with checkout functionality?
Thanks so far!
I keep getting the error saying “class cart not found.” I just followed your codes and also downloaded it, but I can’t see that file been used. (use \Cart as Cart;) Help pls? Hehe
Make sure you do a composer install
Sir , do we have to buy this package to implement it in our laravel app?
Go ahead and use it however you like 🙂
hi,I use Laravel5.3 make a project according to your example step by step .
but I got some error ,Call to undefined function set_active() .
May I know how your helpers.php work in this place?
thank you
Hmmm… the composer.json file should autoload the helpers.php file.
“files”: [
“app/helpers.php”
]
thanks ,it work
Hello Andre, i wanna ask in this code,
when this code :
$item->product->image
$item->product->slug
https://postimg.org/image/66ggu1mv3/
getting this eror
https://postimg.org/image/3nf0e3qb1/
can you help me ? plis
something there is less than parameter, but i dont know
thank you
Hi Zain,
I’ve just updated the demo to use the latest version of the Shopping Cart package. Have a look at this commit. To access the model, you would now do this: $item->model->slug
hi , qty does not work the ajax patch in host server , but it work in localhost :O ? Why ?
It could be a limitation with your server. It works fine on my DigitalOcean droplet 🙂
Patch is old method , you must use post method 😉
i could write code with post merhod for change qty
Route.php
Route::post(‘cart/qty’, ‘CartController@update’)->name(‘qty’);
Ajax :
$(‘.quantity’).on(‘change’, function () {
var id = $(this).attr(‘data-id’)
$.ajax({
type: “POST”,
url: ‘{{Route(‘qty’)}}’,
data: {
‘quantity’: this.value,
‘id’: id
},
success: function (data) {
window.location.href = ‘{{ url(‘/cart’) }}’;
}
});
cartController :
public function update(Request $request)
{
if ($request->ajax()) {
$id = $request->id;
// Validation on max quantity
$validator = Validator::make($request->all(), [
‘quantity’ => ‘required|numeric|between:1,10’
]);
if ($validator->fails()) {
session()->flash(‘error_message’, ‘تعداد باید بین ۱ تا ۱۰ باشد’);
return response()->json([‘success’ => false]);
}
Cart::update($id, $request->quantity);
session()->flash(‘success_message’, ‘تعداد با موفقیت بروزرسانی شد’);
return response()->json([‘success’ => true]);
} else {
return view(‘errors.503’);
}
}
Hi Andre,
Thanks for the tutorial, Everything is working fine. But i have used an additional options array in cart:add method. like this:
$options[‘size’]=$request->size;
$options[‘color’]=$request->color;
Cart::add($request->id, $request->name, 1, $request->price,$options)->associate(‘App\Product’);
For the first time everything works fine. i am able to add black color to the cart. But when i trying to add some other color of the same product to cart. it says the item is already in cart. How can i add 2 different variant to the cart ?
In CartController’s store function, there is a check to see if the item is already in the cart. You can remove that check and you should be able to add multiple items.
After adding Products to the Cart, they automatically wipe out by themselves, What could be causing this?
I’m not exactly sure what you mean, can you elaborate?
when clicking on wishlist buttons item return as empty set. why is that?im using laravel 5.2. i followed your tutorial
Thank you. This article have been very helpful. i just got it to work on my vagrant machine in less than 45mins from start to finish.
hi, is it also possible if this script is used for a restaurant delivery company, that the user can only order from one restaurant in that way when the user wants to order food from 2 restaurants he will get the message: you can only order from one restaurant at the same time? hope you’ll understand my question.
best regards angelo
Help me!
ErrorException in Factory.php line 176:
array_merge(): Argument #2 is not an array
I want not
success: function(data) {
window.location.href = ‘{{ url(‘/cart’) }}’;
}
Replace ajax
…………….
sir. I am using package https://github.com/Crinsane/LaravelShoppingcart.
But I don’t know how integrated with my model.
I just want to decrement the my product stock after order done.
Here’s my code.
“`php
foreach (Cart::content() as $item) {
$item->associatedModel->decrement(‘stock’, $item->qty);
}
“`
hi , qty does not work the ajax patch in localhost? Why
can you update this for 5.4?
Great stuff really helped
Where is this library stored their cart? if in cookies, or session or database?
It uses whatever is defined in Laravel in config/session.php
hi Sir, How to update value of Cart::instance(‘default’)->total(false) without refreshing the page?
I mean the cart Count without refreshing the page
Great Work here
Please how do I make the tax not to be added to the total?
Running php artisan make:route throws error
” [ErrorException]
file_put_contents(C:\xampp\htdocs\mshop\routes/web.php): failed to open stream: No such file or directory
”
How can I correct that.
I getting error please help me
SQLSTATE[42S02]: Base table or view not found: 1146 Table ‘cart_db.sessions’ doesn’t exist (SQL: select * from `sessions` where `id` = 6jkZyw5Dk78RD3Yoouf9Pi37O84k6RcdCu5O5Ln4 limit 1)