Build A New MusicStore Project Using NancyFx And PostgreSQL - Part Three

Introduction

In this article, we will finish the shopping cart , the last function we need to complete.We put the logic to the ShoppingCart class , like the MVC MusicStore does.

Get the Shopping Cart

Before we handle our shopping cart, we need to know which shopping cart is in use. So we should add a method to get the shopping cart first!

  1. public static ShoppingCart GetCart(NancyContext context)  
  2.     {  
  3.         var cart = new ShoppingCart();  
  4.         cart.ShoppingCartId = cart.GetCartId(context);  
  5.         return cart;  
  6.     }   

In the above method , we create an instance of ShoppingCart , assign a cart's id to its Property - ShoppingCartId and return this instance at last. We call the instance’s method GetCartId to get the CartId. What’s the content about method GetCartId?

  1. public string GetCartId(NancyContext context)  
  2.     {  
  3.         if (context.Request.Session[CartSessionKey] == null)  
  4.         {  
  5.             if (context.CurrentUser != null)  
  6.             {  
  7.                 context.Request.Session[CartSessionKey] = context.CurrentUser.UserName;  
  8.             }  
  9.             else  
  10.             {  
  11.                 Guid tempCartId = Guid.NewGuid();  
  12.                 context.Request.Session[CartSessionKey] = tempCartId.ToString();  
  13.             }  
  14.         }  
  15.         return context.Request.Session[CartSessionKey].ToString();  
  16.     }   

In MVC MusicStore, the parameter's type of the method is HttpContextBase based on System.Web.I change this type to NancyContext , because Nancy has its own implementation! And we create a GUID as the identification and store the identification in current session for every new user.

When we use session in Nancy , we need to enable it in our bootstrapper , otherwise we will always get a null object.We add some code in the method ApplicationStartup , here is what the method ApplicationStartup looks like :

  1. protected override void ApplicationStartup(TinyIoCContainer container,IPipelines pipelines)  
  2.    {  
  3.        //enable the cookie  
  4.        CookieBasedSessions.Enable(pipelines);  
  5.        //Prevent errors on Linux  
  6.        StaticConfiguration.DisableErrorTraces = false;  
  7.    }   

Quantity of Shopping Cart

Let's go back to the layout page and finish the quantity of shopping cart first!

Here is the method to get the quantity of shopping cart we can find in the ShoppingCart class. We get the quantity by the identification of shopping cart.

  1. public int GetCount()  
  2.     {  
  3.         string cmd = "public.get_total_count_by_cartid";  
  4.         var res = DBHelper.ExecuteScalar(cmd, new  
  5.         {  
  6.             cid = ShoppingCartId  
  7.         }, nullnull, CommandType.StoredProcedure);  
  8.       
  9.         return Convert.ToInt32(res);  
  10.     }   

Create a new Module class named ShopCartModule to handle the shopping cart.Add a new request in the constructor .

  1. Get["/cartsummary"] = _ =>  
  2.    {  
  3.        var cart = ShoppingCart.GetCart(this.Context);  
  4.        return Response.AsJson(cart.GetCount());  
  5.    };   

Then we call this via the AJAX in layout page:

  1. $.ajax({  
  2.       url: "/shoppingcart/cartsummary",  
  3.       method: "get",  
  4.       dataType: "json",  
  5.       success: function (res) {  
  6.           $("#cart-status").text('Cart (' + res + ')');  
  7.       }  
  8.   });   

So far we have finished the layout page!


The next time , we will focus on how to complete the shopping cart.

Add to Cart

There are two situation when we add album to our shopping cart:

  • Add a new album that not existing in our cart (need to insert a new record)
  • Add a new album existed in our cart (need to update a existed record)

Here is the implementation in the ShoppingCart class:

  1. public void AddToCart(Album album)  
  2.     {  
  3.         string getItemCmd = "public.get_cart_item_by_cartid_and_albumid";  
  4.         var cartItem = DBHelper.QueryFirstOrDefault<Cart>(getItemCmd, new  
  5.         {  
  6.             cid = ShoppingCartId,  
  7.             aid = album.AlbumId  
  8.         }, nullnull, CommandType.StoredProcedure);  
  9.         string addToCartCmd = string.Empty;  
  10.       
  11.         if (cartItem == null)  
  12.         {  
  13.             // Create a new cart item if no cart item exists  
  14.             AddCartItem(cartItem, album.AlbumId);  
  15.         }  
  16.         else  
  17.         {  
  18.             UpdateCartItem(cartItem);  
  19.         }  
  20.     }   

Go to the ShopCartModule class and add the code showing below to its constructor.

  1. Get["/addtocart/{id:int}"] = _ =>  
  2.     {  
  3.         int id = 0;  
  4.         if (int.TryParse(_.id, out id))  
  5.         {  
  6.             string cmd = "public.get_album_by_aid";  
  7.             var addedAlbum = DBHelper.QueryFirstOrDefault<Album>(cmd, new  
  8.             {  
  9.                 aid = id  
  10.             }, nullnull, CommandType.StoredProcedure);  
  11.       
  12.             var cart = ShoppingCart.GetCart(this.Context);  
  13.             cart.AddToCart(addedAlbum);  
  14.         }  
  15.         return Response.AsRedirect("~/");  
  16.     };   

We can add one album to our shopping cart when we open its details page. 


When we add some albums to our shopping cart successfully , the quantity of shopping cart will change at once and go back to the index page of the music store.


Index Page of Shopping Cart

After we add some albums to shopping cart , we need to know the details of our cart. So we add a new page to show our carts' information. Actually , the new page is a list that contains the albums and the price in our shopping cart. What we handle in module is demonstrated as follows:

  1. Get["/index"] = _ =>  
  2. {  
  3.     var cart = ShoppingCart.GetCart(this.Context);  
  4.   
  5.     // Set up our ViewModel  
  6.     var viewModel = new ShoppingCartViewModel  
  7.     {  
  8.         CartItems = cart.GetCartItems(),  
  9.         CartTotal = cart.GetTotal()  
  10.     };  
  11.   
  12.     // Return the view  
  13.     return View["Index", viewModel];  
  14. };   

The view's code

  1. @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<NancyMusicStore.ViewModels.ShoppingCartViewModel>  
  2.  @{  
  3.      ViewBag.Title = "Shopping Cart";  
  4.  }  
  5.  <h3>  
  6.      <em>Review</em> your cart:  
  7.  </h3>  
  8.  <p class="button">  
  9.      <a href="javascript:;">Checkout >></a>  
  10.  </p>  
  11.  <div id="update-message">  
  12.  </div>  
  13.  <table>  
  14.      <tr>  
  15.          <th>  
  16.              Album Name  
  17.          </th>  
  18.          <th>  
  19.              Price (each)  
  20.          </th>  
  21.          <th>  
  22.              Quantity  
  23.          </th>  
  24.          <th></th>  
  25.      </tr>  
  26.      @foreach (var item in Model.CartItems)  
  27.      {  
  28.          <tr id="row-@item.RecordId">  
  29.              <td>  
  30.                  <a href="/store/details/@item.AlbumId">@item.Title</a>  
  31.              </td>  
  32.              <td>  
  33.                  @item.Price  
  34.              </td>  
  35.              <td id="item-count-@item.RecordId">  
  36.                  @item.Count  
  37.              </td>  
  38.              <td>  
  39.                  <a href="javascript:void(0);" class="RemoveLink" data-id="@item.RecordId">Remove from cart</a>  
  40.              </td>  
  41.          </tr>  
  42.      }  
  43.      <tr>  
  44.          <td>  
  45.              Total  
  46.          </td>  
  47.          <td></td>  
  48.          <td></td>  
  49.          <td id="cart-total">  
  50.              @Model.CartTotal  
  51.          </td>  
  52.      </tr>  
  53.  </table>   

Here is the screenshot you may get:


Remove From Shopping Cart

There are also two situations when we remove album from shopping cart:

  • Remove album whose quantity is only one (need to delete the album)
  • Remove album whose quantity is greater than one (need to update the quantity of this album)

Here is the implementation in the ShoppingCart class,

  1. public int RemoveFromCart(int id)  
  2.     {  
  3.         string getItemCmd = "public.get_cart_item_by_cartid_and_recordid";  
  4.         var cartItem = DBHelper.QueryFirstOrDefault<Cart>(getItemCmd, new  
  5.         {  
  6.             cid = ShoppingCartId,  
  7.             rid = id  
  8.         }, nullnull, CommandType.StoredProcedure);  
  9.       
  10.         int itemCount = 0;  
  11.         if (cartItem != null)  
  12.         {                  
  13.             if (cartItem.Count > 1)  
  14.             {  
  15.                 UpdateCartItemCount(cartItem, itemCount);                     
  16.             }  
  17.             else  
  18.             {  
  19.                 RemoveCartItem(cartItem.RecordId);  
  20.             }  
  21.         }  
  22.         return itemCount;  
  23.     }   

Add something new to the constructor of ShopCartModule class , and we return json after handle the remove request.

  1. Post["/removefromcart"] = _ =>  
  2.     {  
  3.         var vm = this.Bind<ShoppingCartRemoveRequestViewModel>();  
  4.         string albumName = string.Empty;  
  5.         return Response.AsJson(GetRemoveResult(vm.Id, albumName));  
  6.     };   

Here are the details of the method GetRemoveResult:

  1. private ShoppingCartRemoveViewModel GetRemoveResult(int rid, string albumName)  
  2.     {  
  3.         int itemCount = 0;  
  4.   
  5.         // Remove the item from the cart  
  6.         var cart = ShoppingCart.GetCart(this.Context);  
  7.   
  8.         string cmd = "public.get_album_title_by_recordid";  
  9.         var res = DBHelper.ExecuteScalar(cmd, new  
  10.         {  
  11.             rid = rid  
  12.         }, nullnull, CommandType.StoredProcedure);  
  13.   
  14.         if (res != null)  
  15.         {  
  16.             albumName = res.ToString();  
  17.             itemCount = cart.RemoveFromCart(rid);  
  18.         }  
  19.   
  20.         var results = new ShoppingCartRemoveViewModel  
  21.         {  
  22.             Message = albumName + " has been removed from your shopping cart.",  
  23.             CartTotal = cart.GetTotal(),  
  24.             CartCount = cart.GetCount(),  
  25.             ItemCount = itemCount,  
  26.             DeleteId = rid  
  27.         };  
  28.         return results;  
  29.     }   

At last, we need to add the JAVASCRIPT code to handle the request in index page of shopping cart when we

click the Delete button.

  1. @section scripts{  
  2. <script type="text/javascript">  
  3.        $(function () {  
  4.             $(".RemoveLink").click(function () {  
  5.                 var recordToDelete = $(this).attr("data-id");      
  6.                 if (recordToDelete != '') {  
  7.                     $.post("/shoppingcart/removefromcart", { "id": recordToDelete },  
  8.                         function (data) {  
  9.                             if (data.ItemCount == 0) {  
  10.                                 $('#row-' + data.deleteid).fadeOut('slow');  
  11.                             } else {  
  12.                                 $('#item-count-' + data.deleteId).text(data.itemCount);  
  13.                             }      
  14.                             $('#cart-total').text(data.cartTotal);  
  15.                             $('#update-message').text(data.message);  
  16.                             $('#cart-status').text('Cart (' + data.cartCount + ')');  
  17.                         });  
  18.                 }  
  19.             });      
  20.         });  
  21. </script>  
  22. }   

Check Out

After we confirm the information of what we want to buy , we will check them out immediately, and finish our personal information that we must fill out.


Create a new module class named CheckOutModule to handle this function.

Add the below code first !

  1. this.RequiresAuthentication();   

When someone posts his/her order to the server, we will create the order and its details . We get the order's username from the nancycontext which is required when we insert the data to the database.

  1. Post["/addressandpayment"] = _ =>  
  2.    {  
  3.        var order = this.Bind<Order>();  
  4.        order.Username = this.Context.CurrentUser.UserName;  
  5.        order.OrderDate = DateTime.UtcNow;  
  6.      
  7.        string cmd = "public.add_order";  
  8.        var res = DBHelper.ExecuteScalar(cmd, new  
  9.        {  
  10.            odate = order.OrderDate,  
  11.            uname = order.Username,  
  12.            fname = order.FirstName,  
  13.            lname = order.LastName,  
  14.            adr = order.Address,  
  15.            cn = order.City,  
  16.            sn = order.State,  
  17.            pcode = order.PostalCode,  
  18.            cname = order.Country,  
  19.            ph = order.Phone,  
  20.            ea = order.Email,  
  21.            t = order.Total  
  22.        }, nullnull, CommandType.StoredProcedure);  
  23.      
  24.        if (Convert.ToInt32(res) != 0)  
  25.        {  
  26.            order.OrderId = Convert.ToInt32(res);  
  27.            var cart = ShoppingCart.GetCart(this.Context);  
  28.            cart.CreateOrder(order);  
  29.      
  30.            string redirectUrl = string.Format("/checkout/complete/{0}", res.ToString());  
  31.            return Response.AsRedirect(redirectUrl);  
  32.        }  
  33.        return View["AddressAndPayment"];  
  34.    };   

After creating the order and its details successfully, we will redirect to the complete page. This page will show us the order number.


At this time ,we have  finished our project NancyMusicStore.

What Next?

Maybe someone will think that this is the last article of this series , because this project is finished now. But we just finished this project we haven't deployed it on our server.

In my next article (the last one of this series), I will show you how to deploy this project on both Windows and Linux.