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!
- public static ShoppingCart GetCart(NancyContext context)
- {
- var cart = new ShoppingCart();
- cart.ShoppingCartId = cart.GetCartId(context);
- return cart;
- }
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?
- public string GetCartId(NancyContext context)
- {
- if (context.Request.Session[CartSessionKey] == null)
- {
- if (context.CurrentUser != null)
- {
- context.Request.Session[CartSessionKey] = context.CurrentUser.UserName;
- }
- else
- {
- Guid tempCartId = Guid.NewGuid();
- context.Request.Session[CartSessionKey] = tempCartId.ToString();
- }
- }
- return context.Request.Session[CartSessionKey].ToString();
- }
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 :
- protected override void ApplicationStartup(TinyIoCContainer container,IPipelines pipelines)
- {
-
- CookieBasedSessions.Enable(pipelines);
-
- StaticConfiguration.DisableErrorTraces = false;
- }
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.
- public int GetCount()
- {
- string cmd = "public.get_total_count_by_cartid";
- var res = DBHelper.ExecuteScalar(cmd, new
- {
- cid = ShoppingCartId
- }, null, null, CommandType.StoredProcedure);
-
- return Convert.ToInt32(res);
- }
Create a new Module class named ShopCartModule to handle the shopping cart.Add a new request in the constructor .
- Get["/cartsummary"] = _ =>
- {
- var cart = ShoppingCart.GetCart(this.Context);
- return Response.AsJson(cart.GetCount());
- };
Then we call this via the AJAX in layout page:
- $.ajax({
- url: "/shoppingcart/cartsummary",
- method: "get",
- dataType: "json",
- success: function (res) {
- $("#cart-status").text('Cart (' + res + ')');
- }
- });
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:
- public void AddToCart(Album album)
- {
- string getItemCmd = "public.get_cart_item_by_cartid_and_albumid";
- var cartItem = DBHelper.QueryFirstOrDefault<Cart>(getItemCmd, new
- {
- cid = ShoppingCartId,
- aid = album.AlbumId
- }, null, null, CommandType.StoredProcedure);
- string addToCartCmd = string.Empty;
-
- if (cartItem == null)
- {
-
- AddCartItem(cartItem, album.AlbumId);
- }
- else
- {
- UpdateCartItem(cartItem);
- }
- }
Go to the ShopCartModule class and add the code showing below to its constructor.
- Get["/addtocart/{id:int}"] = _ =>
- {
- int id = 0;
- if (int.TryParse(_.id, out id))
- {
- string cmd = "public.get_album_by_aid";
- var addedAlbum = DBHelper.QueryFirstOrDefault<Album>(cmd, new
- {
- aid = id
- }, null, null, CommandType.StoredProcedure);
-
- var cart = ShoppingCart.GetCart(this.Context);
- cart.AddToCart(addedAlbum);
- }
- return Response.AsRedirect("~/");
- };
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:
- Get["/index"] = _ =>
- {
- var cart = ShoppingCart.GetCart(this.Context);
-
-
- var viewModel = new ShoppingCartViewModel
- {
- CartItems = cart.GetCartItems(),
- CartTotal = cart.GetTotal()
- };
-
-
- return View["Index", viewModel];
- };
The view's code
- @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<NancyMusicStore.ViewModels.ShoppingCartViewModel>
- @{
- ViewBag.Title = "Shopping Cart";
- }
- <h3>
- <em>Review</em> your cart:
- </h3>
- <p class="button">
- <a href="javascript:;">Checkout >></a>
- </p>
- <div id="update-message">
- </div>
- <table>
- <tr>
- <th>
- Album Name
- </th>
- <th>
- Price (each)
- </th>
- <th>
- Quantity
- </th>
- <th></th>
- </tr>
- @foreach (var item in Model.CartItems)
- {
- <tr id="[email protected]">
- <td>
- <a href="/store/details/@item.AlbumId">@item.Title</a>
- </td>
- <td>
- @item.Price
- </td>
- <td id="[email protected]">
- @item.Count
- </td>
- <td>
- <a href="javascript:void(0);" class="RemoveLink" data-id="@item.RecordId">Remove from cart</a>
- </td>
- </tr>
- }
- <tr>
- <td>
- Total
- </td>
- <td></td>
- <td></td>
- <td id="cart-total">
- @Model.CartTotal
- </td>
- </tr>
- </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,
- public int RemoveFromCart(int id)
- {
- string getItemCmd = "public.get_cart_item_by_cartid_and_recordid";
- var cartItem = DBHelper.QueryFirstOrDefault<Cart>(getItemCmd, new
- {
- cid = ShoppingCartId,
- rid = id
- }, null, null, CommandType.StoredProcedure);
-
- int itemCount = 0;
- if (cartItem != null)
- {
- if (cartItem.Count > 1)
- {
- UpdateCartItemCount(cartItem, itemCount);
- }
- else
- {
- RemoveCartItem(cartItem.RecordId);
- }
- }
- return itemCount;
- }
Add something new to the constructor of ShopCartModule class , and we return json after handle the remove request.
- Post["/removefromcart"] = _ =>
- {
- var vm = this.Bind<ShoppingCartRemoveRequestViewModel>();
- string albumName = string.Empty;
- return Response.AsJson(GetRemoveResult(vm.Id, albumName));
- };
Here are the details of the method GetRemoveResult:
- private ShoppingCartRemoveViewModel GetRemoveResult(int rid, string albumName)
- {
- int itemCount = 0;
-
-
- var cart = ShoppingCart.GetCart(this.Context);
-
- string cmd = "public.get_album_title_by_recordid";
- var res = DBHelper.ExecuteScalar(cmd, new
- {
- rid = rid
- }, null, null, CommandType.StoredProcedure);
-
- if (res != null)
- {
- albumName = res.ToString();
- itemCount = cart.RemoveFromCart(rid);
- }
-
- var results = new ShoppingCartRemoveViewModel
- {
- Message = albumName + " has been removed from your shopping cart.",
- CartTotal = cart.GetTotal(),
- CartCount = cart.GetCount(),
- ItemCount = itemCount,
- DeleteId = rid
- };
- return results;
- }
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.
- @section scripts{
- <script type="text/javascript">
- $(function () {
- $(".RemoveLink").click(function () {
- var recordToDelete = $(this).attr("data-id");
- if (recordToDelete != '') {
- $.post("/shoppingcart/removefromcart", { "id": recordToDelete },
- function (data) {
- if (data.ItemCount == 0) {
- $('#row-' + data.deleteid).fadeOut('slow');
- } else {
- $('#item-count-' + data.deleteId).text(data.itemCount);
- }
- $('#cart-total').text(data.cartTotal);
- $('#update-message').text(data.message);
- $('#cart-status').text('Cart (' + data.cartCount + ')');
- });
- }
- });
- });
- </script>
- }
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 !
- 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.
- Post["/addressandpayment"] = _ =>
- {
- var order = this.Bind<Order>();
- order.Username = this.Context.CurrentUser.UserName;
- order.OrderDate = DateTime.UtcNow;
-
- string cmd = "public.add_order";
- var res = DBHelper.ExecuteScalar(cmd, new
- {
- odate = order.OrderDate,
- uname = order.Username,
- fname = order.FirstName,
- lname = order.LastName,
- adr = order.Address,
- cn = order.City,
- sn = order.State,
- pcode = order.PostalCode,
- cname = order.Country,
- ph = order.Phone,
- ea = order.Email,
- t = order.Total
- }, null, null, CommandType.StoredProcedure);
-
- if (Convert.ToInt32(res) != 0)
- {
- order.OrderId = Convert.ToInt32(res);
- var cart = ShoppingCart.GetCart(this.Context);
- cart.CreateOrder(order);
-
- string redirectUrl = string.Format("/checkout/complete/{0}", res.ToString());
- return Response.AsRedirect(redirectUrl);
- }
- return View["AddressAndPayment"];
- };
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.