Blog

what's on my mind

Implementing a Shopping Cart in Laravel

/ / Laravel / PHP / Web Development / 37 comments

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.

 

Live Demo    View on GitHub

 

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

shop

  • The product page: resources/views/product.blade.php
    View containing all the details of a SINGLE product

shop

  • The cart page: resources/views/cart.blade.php
    View containing the contents of your shopping cart

shop

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.

37 Comments

  • event on July 16, 2016 | Reply

    Awesome

  • aquahuang on August 15, 2016 | Reply

    can check user login or not before adding item to shopping cart

  • aquahuang on August 15, 2016 | Reply

    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(‘您尚未登入, 請先登入’);
    }

    }

    • Ed on August 17, 2016 | Reply

      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.

      • aqua on August 18, 2016 | Reply

        Many thanks,

  • aqua on August 18, 2016 | Reply

    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?

    • aquahuang on August 19, 2016 | Reply

      OK, it works now,take care of CSRF , PATH …
      thanks for your artical again

      • ED on October 16, 2016 | Reply

        Hi aquahuang, having problem with ajax method too.
        Do you mind shearing how you took care ‘of CSRF , PATH …’
        Thanks.

  • renzo on September 14, 2016 | Reply

    thank you so much for this!

  • Joe on September 23, 2016 | Reply

    It would be great if you could follow this up with checkout functionality?

    Thanks so far!

  • Jay on December 1, 2016 | Reply

    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

    • Andre Madarang Author on December 12, 2016 | Reply

      Make sure you do a composer install

  • sachinraee on December 12, 2016 | Reply

    Sir , do we have to buy this package to implement it in our laravel app?

    • Andre Madarang Author on December 12, 2016 | Reply

      Go ahead and use it however you like 🙂

  • Kai on December 12, 2016 | Reply

    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

    • Andre Madarang Author on December 12, 2016 | Reply

      Hmmm… the composer.json file should autoload the helpers.php file.

      “files”: [
      “app/helpers.php”
      ]

      • Kai on December 13, 2016 | Reply

        thanks ,it work

    • Andre Madarang Author on December 24, 2016 | Reply

      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

  • masoud on December 27, 2016 | Reply

    hi , qty does not work the ajax patch in host server , but it work in localhost :O ? Why ?

    • Andre Madarang Author on January 5, 2017 | Reply

      It could be a limitation with your server. It works fine on my DigitalOcean droplet 🙂

      • masoud on January 5, 2017 | Reply

        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’);
        }

        }

  • Manoj on January 3, 2017 | Reply

    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 ?

    • Andre Madarang Author on January 5, 2017 | Reply

      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.

  • Christian Simon on January 5, 2017 | Reply

    After adding Products to the Cart, they automatically wipe out by themselves, What could be causing this?

    • Andre Madarang Author on January 5, 2017 | Reply

      I’m not exactly sure what you mean, can you elaborate?

  • ruhina05 on January 12, 2017 | Reply

    when clicking on wishlist buttons item return as empty set. why is that?im using laravel 5.2. i followed your tutorial

  • Alex on January 30, 2017 | Reply

    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.

  • angelo on January 30, 2017 | Reply

    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

  • StevenTri on February 10, 2017 | Reply

    Help me!

    ErrorException in Factory.php line 176:
    array_merge(): Argument #2 is not an array

  • StevenTri on February 15, 2017 | Reply

    I want not
    success: function(data) {
    window.location.href = ‘{{ url(‘/cart’) }}’;
    }
    Replace ajax

    …………….

  • Irsyad on March 7, 2017 | Reply

    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);
    }
    “`

  • Mashfik Hasan on April 26, 2017 | Reply

    hi , qty does not work the ajax patch in localhost? Why

  • Ben on June 1, 2017 | Reply

    can you update this for 5.4?

  • Lenny on July 7, 2017 | Reply

    Great stuff really helped

  • Ste on August 1, 2017 | Reply

    Where is this library stored their cart? if in cookies, or session or database?

    • Andre Madarang Author on August 2, 2017 | Reply

      It uses whatever is defined in Laravel in config/session.php

Leave a Comment

Comment *
Name *
Email * (will not be visible)
Website