You are on page 1of 31

Parte 8: Carrinho de compras com atualizaes de Ajax

Por Jon Galloway|21 De abril de 2011 A loja de msica do MVC um aplicativo tutorial que apresenta e explica passo a passo como usar o ASP.NET MVC e Visual Studio para desenvolvimento web. A loja de msica do MVC um leve exemplo de implementao de loja que vende lbuns de msica on-line e implementa a administrao do site bsico, entrar usurio e funcionalidade do carrinho de compras. Esta srie de tutoriais detalha todos os passos tomados para construir o aplicativo de exemplo de ASP.NET MVC Music Store. Parte 8 aborda o carrinho de compras com atualizaes de Ajax. Ns vamos permitir que os usurios coloquem lbuns em seus carrinhos sem o registro, mas eles precisaro se registrar como convidados para concluir o check-out. O processo de compras e checkout ser separado em dois controladores: um controlador ShoppingCart que permite anonimamente, adicionando itens a um carrinho e um controlador de Checkout que lida com o processo de checkout. Ns vamos comear com o carrinho de compras nesta seo e, em seguida, criar o processo de check-out na seo a seguir.

Adding the Cart, Order, and OrderDetail model classes


Our Shopping Cart and Checkout processes will make use of some new classes. Right-click the Models folder and add a Cart class (Cart.cs) with the following code. using System.ComponentModel.DataAnnotations; namespace MvcMusicStore.Models { public class Cart { [Key] public int RecordId { get; public string CartId { get; public int AlbumId { get; public int Count { get; public System.DateTime DateCreated public virtual Album Album { get; } }

set; } set; } set; } set; } { get; set; } set; }

This class is pretty similar to others weve used so far, with the exception of the [Key] attribute for the RecordId property. Our Cart items will have a string identifier named CartID to allow anonymous shopping, but the table includes an integer primary key named RecordId. By convention, Entity Framework Code-First expects that the primary key for a table named Cart will be either CartId or ID, but we can easily override that via annotations or code if we want. This is an example of how we can use the simple conventions in Entity Framework Code-First when they suit us, but were not constrained by them when they dont. Next, add an Order class (Order.cs) with the following code. using System.Collections.Generic; namespace MvcMusicStore.Models { public partial class Order

{ public public public public public public public public public public public public public public } } This class tracks summary and delivery information for an order. It wont compile yet, because it has an OrderDetails navigation property which depends on a class we havent created yet. Lets fix that now by adding a class named OrderDetail.cs, adding the following code. namespace MvcMusicStore.Models { public class OrderDetail { public int OrderDetailId { get; set; } public int OrderId { get; set; } public int AlbumId { get; set; } public int Quantity { get; set; } public decimal UnitPrice { get; set; } public virtual Album Album { get; set; } public virtual Order Order { get; set; } } } Well make one last update to our MusicStoreEntities class to include DbSets which expose those new Model classes, also including a DbSet<Artist>. The updated MusicStoreEntities class appears as below. using System.Data.Entity; namespace MvcMusicStore.Models { public class MusicStoreEntities : DbContext { public DbSet<Album> Albums { get; set; } public DbSet<Genre> Genres { get; set; } public DbSet<Artist> Artists { get; set; } public DbSet<Cart> Carts { get; set; } public DbSet<Order> Orders { get; set; } public DbSet<OrderDetail> OrderDetails { get; set; } } } int OrderId { get; set; } string Username { get; set; } string FirstName { get; set; } string LastName { get; set; } string Address { get; set; } string City { get; set; } string State { get; set; } string PostalCode { get; set; } string Country { get; set; } string Phone { get; set; } string Email { get; set; } decimal Total { get; set; } System.DateTime OrderDate { get; set; } List<OrderDetail> OrderDetails { get; set; }

Gerenciando a lgica de negcios do carrinho de compras


Em seguida, criaremos a classe ShoppingCart na pasta modelos. O modelo ShoppingCart lida com acesso a dados para a tabela de carrinho. Alm disso, ele vai lidar com a lgica de negcios para adicionar e remover itens do carrinho de compras. Desde que ns no queremos exigir que os usurios se inscrever para uma conta apenas adicionar itens ao seu carrinho de compras, vamos atribuir usurios um identificador exclusivo temporrio (usando um GUID, ou um identificador globalmente exclusivo) quando eles acessam o carrinho de compras. Ns vai armazenar este ID usando a classe de sesso do ASP.NET. Nota: A sesso do ASP.NET um local conveniente para armazenar informaes especficas do usurio que iro expirar depois de sarem do local. Enquanto o uso indevido de estado da sesso pode ter implicaes de desempenho em sites maiores, nosso uso claro vai funcionar bem para fins de demonstrao. A classe ShoppingCart expe os seguintes mtodos: AddToCart leva um lbum como um parmetro e o adiciona ao carrinho do usurio. Desde que a tabela de carrinho controla a quantidade para cada lbum, inclui a lgica para criar uma nova linha, se necessrio, ou apenas incrementar a quantidade, se o usurio j encomendou uma cpia do lbum. RemoveFromCart leva um ID do lbum e a remove do carrinho do usurio. Se o usurio tivesse uma cpia do lbum no seu carrinho, a linha removida. EmptyCart remove todos os itens do carrinho de compras do usurio. GetCartItems recupera uma lista de CartItems para visualizao ou processamento. GetCount recupera um nmero total de lbuns, um usurio tem em seu carrinho de compras. Total calcula o custo total de todos os itens no carrinho. CreateOrder converte o carrinho de compras para uma ordem durante a fase de checkout. GetCart um mtodo esttico que permite que nossos controladores obter um objeto de carrinho. Ele usa o mtodo GetCartId para lidar com a leitura o CartId da sesso do usurio. O mtodo GetCartId requer o HttpContextBase para que ele possa ler CartId do usurio da sesso do usurio. Aqui a completa classe ShoppingCart: using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace MvcMusicStore.Models { public partial class ShoppingCart { MusicStoreEntities storeDB = new MusicStoreEntities(); string ShoppingCartId { get; set; } public const string CartSessionKey = "CartId"; public static ShoppingCart GetCart(HttpContextBase context) { var cart = new ShoppingCart(); cart.ShoppingCartId = cart.GetCartId(context); return cart; } // Helper method to simplify shopping cart calls public static ShoppingCart GetCart(Controller controller) { return GetCart(controller.HttpContext); } public void AddToCart(Album album) { // Get the matching cart and album instances

var cartItem = storeDB.Carts.SingleOrDefault( c => c.CartId == ShoppingCartId && c.AlbumId == album.AlbumId); if (cartItem == null) { // Create a new cart item if no cart item exists cartItem = new Cart { AlbumId = album.AlbumId, CartId = ShoppingCartId, Count = 1, DateCreated = DateTime.Now }; storeDB.Carts.Add(cartItem); } else { // If the item does exist in the cart, // then add one to the quantity cartItem.Count++; } // Save changes storeDB.SaveChanges(); } public int RemoveFromCart(int id) { // Get the cart var cartItem = storeDB.Carts.Single( cart => cart.CartId == ShoppingCartId && cart.RecordId == id); int itemCount = 0; if (cartItem != null) { if (cartItem.Count > 1) { cartItem.Count--; itemCount = cartItem.Count; } else { storeDB.Carts.Remove(cartItem); } // Save changes storeDB.SaveChanges(); } return itemCount; } public void EmptyCart() { var cartItems = storeDB.Carts.Where( cart => cart.CartId == ShoppingCartId); foreach (var cartItem in cartItems)

{ storeDB.Carts.Remove(cartItem); } // Save changes storeDB.SaveChanges(); } public List<Cart> GetCartItems() { return storeDB.Carts.Where( cart => cart.CartId == ShoppingCartId).ToList(); } public int GetCount() { // Get the count of each item in the cart and sum them up int? count = (from cartItems in storeDB.Carts where cartItems.CartId == ShoppingCartId select (int?)cartItems.Count).Sum(); // Return 0 if all entries are null return count ?? 0; } public decimal GetTotal() { // Multiply album price by count of that album to get // the current price for each of those albums in the cart // sum all album price totals to get the cart total decimal? total = (from cartItems in storeDB.Carts where cartItems.CartId == ShoppingCartId select (int?)cartItems.Count * cartItems.Album.Price).Sum(); return total ?? decimal.Zero; } public int CreateOrder(Order order) { decimal orderTotal = 0; var cartItems = GetCartItems(); // Iterate over the items in the cart, // adding the order details for each foreach (var item in cartItems) { var orderDetail = new OrderDetail { AlbumId = item.AlbumId, OrderId = order.OrderId, UnitPrice = item.Album.Price, Quantity = item.Count }; // Set the order total of the shopping cart orderTotal += (item.Count * item.Album.Price); storeDB.OrderDetails.Add(orderDetail); } // Set the order's total to the orderTotal count order.Total = orderTotal;

// Save the order storeDB.SaveChanges(); // Empty the shopping cart EmptyCart(); // Return the OrderId as the confirmation number return order.OrderId; } // We're using HttpContextBase to allow access to cookies. public string GetCartId(HttpContextBase context) { if (context.Session[CartSessionKey] == null) { if (!string.IsNullOrWhiteSpace(context.User.Identity.Name)) { context.Session[CartSessionKey] = context.User.Identity.Name; } else { // Generate a new random GUID using System.Guid class Guid tempCartId = Guid.NewGuid(); // Send tempCartId back to client as a cookie context.Session[CartSessionKey] = tempCartId.ToString(); } } return context.Session[CartSessionKey].ToString(); } // When a user has logged in, migrate their shopping cart to // be associated with their username public void MigrateCart(string userName) { var shoppingCart = storeDB.Carts.Where( c => c.CartId == ShoppingCartId); foreach (Cart item in shoppingCart) { item.CartId = userName; } storeDB.SaveChanges(); } } }

ViewModels
Nosso controlador de carrinho de compras ser necessrio comunicar algumas informaes complexas para seus pontos de vista que no mapeiam corretamente para nossos objetos de modelo. No queremos modificar nossos modelos para atender nossos pontos de vista; Classes de modelo devem representar nosso domnio, no a interface do usurio. Uma soluo seria passar a informao para nossos pontos de vista usando a classe ViewBag, como fizemos com as informaes de lista suspensa de gerente da loja, mas passar um monte de informaes via ViewBag fica difcil de gerenciar. Uma soluo para isso usar o padro ViewModel . Ao usar esse padro, que podemos criar classes de rigidez que so otimizados para nossos cenrios especficos da vista, e que expem propriedades para o valores/contedo dinmico necessrio pelos nossos modelos de exibio. Nossas classes de controlador podem ento preencher e passar essas

classes de exibio otimizada para nosso modelo de exibio para usar. Isso permite que a segurana de tipos, verificao de tempo de compilao e editor IntelliSense dentro de modelos de exibio. Vamos criar dois modelos de exibio para uso no nosso controlador de carrinho de compras: o ShoppingCartViewModel realizar o contedo do carrinho de compras do usurio, e o ShoppingCartRemoveViewModel ser usado para exibir informaes de confirmao quando um usurio remove algo do seu carrinho. Vamos criar uma nova pasta ViewModels na raiz de nosso projeto para manter as coisas organizadas. Boto direito do mouse o projeto, selecione Adicionar / nova pasta.

Nomeie a pasta ViewModels.

Em seguida, adicione a classe ShoppingCartViewModel na pasta ViewModels. Tem duas propriedades: uma lista de itens do carrinho e um valor decimal para segurar o preo total para todos os itens no carrinho. using System.Collections.Generic; using MvcMusicStore.Models; namespace MvcMusicStore.ViewModels {

public class ShoppingCartViewModel { public List<Cart> CartItems { get; set; } public decimal CartTotal { get; set; } } } Agora adicione o ShoppingCartRemoveViewModel para a pasta ViewModels, com as seguintes quatro propriedades. namespace MvcMusicStore.ViewModels { public class ShoppingCartRemoveViewModel { public string Message { get; set; } public decimal CartTotal { get; set; } public int CartCount { get; set; } public int ItemCount { get; set; } public int DeleteId { get; set; } } }

Controlador de carrinho de compras


O controlador de carrinho de compras possui trs finalidades principais: Adicionando itens a um carrinho e remover itens do carrinho de visualizao de itens no carrinho. Far uso das trs classes acabamos de criar: ShoppingCartViewModel, ShoppingCartRemoveViewModel e ShoppingCart. Como no StoreController e StoreManagerController, vamos adicionar um campo para armazenar uma instncia de MusicStoreEntities. Adicione um novo controlador de carrinho de compras para o projeto usando o modelo de controlador vazio.

Aqui o controlador ShoppingCart completa. As aes do ndice e adicionar o controlador devem parecer muito familiares. As aes do controlador remover e CartSummary lidar com dois casos especiais, que discutiremos na seo a seguir. using System.Linq;

using System.Web.Mvc; using MvcMusicStore.Models; using MvcMusicStore.ViewModels; namespace MvcMusicStore.Controllers { public class ShoppingCartController : Controller { MusicStoreEntities storeDB = new MusicStoreEntities(); // // GET: /ShoppingCart/ public ActionResult Index() { var cart = ShoppingCart.GetCart(this.HttpContext); // Set up our ViewModel var viewModel = new ShoppingCartViewModel { CartItems = cart.GetCartItems(), CartTotal = cart.GetTotal() }; // Return the view return View(viewModel); } // // GET: /Store/AddToCart/5 public ActionResult AddToCart(int id) { // Retrieve the album from the database var addedAlbum = storeDB.Albums .Single(album => album.AlbumId == id); // Add it to the shopping cart var cart = ShoppingCart.GetCart(this.HttpContext); cart.AddToCart(addedAlbum); // Go back to the main store page for more shopping return RedirectToAction("Index"); } // // AJAX: /ShoppingCart/RemoveFromCart/5 [HttpPost] public ActionResult RemoveFromCart(int id) { // Remove the item from the cart var cart = ShoppingCart.GetCart(this.HttpContext); // Get the name of the album to display confirmation string albumName = storeDB.Carts .Single(item => item.RecordId == id).Album.Title; // Remove from cart int itemCount = cart.RemoveFromCart(id); // Display the confirmation message

var results = new ShoppingCartRemoveViewModel { Message = Server.HtmlEncode(albumName) + " has been removed from your shopping cart.", CartTotal = cart.GetTotal(), CartCount = cart.GetCount(), ItemCount = itemCount, DeleteId = id }; return Json(results); } // // GET: /ShoppingCart/CartSummary [ChildActionOnly] public ActionResult CartSummary() { var cart = ShoppingCart.GetCart(this.HttpContext); ViewData["CartCount"] = cart.GetCount(); return PartialView("CartSummary"); } } }

Atualizaes de AJAX com jQuery


Em seguida vamos criar uma pgina de ndice de carrinho de compras que fortemente tipada para o ShoppingCartViewModel e usa o modelo de exibio de lista usando o mesmo mtodo de antes.

No entanto, em vez de usar um HTML. ActionLink para remover itens do carrinho, vamos usar jQuery para "conectar" o evento click para todos os links neste modo de exibio que tem a classe HTML RemoveLink. Ao invs de enviar o formulrio, este manipulador de eventos click far apenas um retorno de chamada AJAX para nossa ao de controlador de RemoveFromCart. O RemoveFromCart retorna um resultado JSON serializado, que nosso callback jQuery, ento, analisa e executa quatro atualizaes rpidas para a pgina usando jQuery: 1. Remove o lbum excludo da lista 2. Atualiza a contagem de carrinho no cabealho 3. Exibe uma mensagem de atualizao para o usurio 4. Atualiza o preo total do carrinho

Desde que o cenrio de remover est sendo manipulado por um retorno de chamada Ajax dentro da viso de ndice, no precisamos uma viso adicional para RemoveFromCart ao. Aqui est o cdigo completo para o modo de exibio de /ShoppingCart/Index: @model MvcMusicStore.ViewModels.ShoppingCartViewModel @{ ViewBag.Title = "Shopping Cart"; } <script src="/Scripts/jquery-1.4.4.min.js" type="text/javascript"></script> <script type="text/javascript"> $(function () { // Document.ready -> link up remove event handler $(".RemoveLink").click(function () { // Get the id from the link var recordToDelete = $(this).attr("data-id"); if (recordToDelete != '') { // Perform the ajax post

$.post("/ShoppingCart/RemoveFromCart", {"id": recordToDelete }, function (data) { // Successful requests get here // Update the page elements 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> <h3> <em>Review</em> your cart: </h3> <p class="button"> @Html.ActionLink("Checkout >>", "AddressAndPayment", "Checkout") </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="row-@item.RecordId"> <td> @Html.ActionLink(item.Album.Title, "Details", "Store", new { id = item.AlbumId }, null) </td> <td> @item.Album.Price </td> <td id="item-count-@item.RecordId"> @item.Count </td> <td> <a href="#" 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> Para testar isso, precisamos ser capaz de adicionar itens ao nosso carrinho de compras. Vamos atualiz-nossaLoja detalhes vista para incluir um boto "Adicionar ao carrinho". Enquanto estamos no assunto, podemos incluir algumas informaes adicionais lbum que ns adicionamos desde ltima atualizamos esta vista: gnero, artista, preo e arte do lbum. O loja detalhes exibir cdigo atualizado aparece como mostrado abaixo. @model MvcMusicStore.Models.Album @{ ViewBag.Title = "Album - " + Model.Title; } <h2>@Model.Title</h2> <p> <img alt="@Model.Title" src="@Model.AlbumArtUrl" /> </p> <div id="album-details"> <p> <em>Genre:</em> @Model.Genre.Name </p> <p> <em>Artist:</em> @Model.Artist.Name </p> <p> <em>Price:</em> @String.Format("{0:F}", Model.Price) </p> <p class="button"> @Html.ActionLink("Add to cart", "AddToCart", "ShoppingCart", new { id = Model.AlbumId }, "") </p> </div> Agora podemos clicar atravs da loja e testar, adicionando e removendo lbuns de e para o nosso carrinho de compras. Execute o aplicativo e navegue at o ndice de loja.

Em seguida, clique em um gnero para exibir uma lista de lbuns.

Clique em um ttulo de lbum agora mostra nossa exibio de detalhes de lbum atualizado, incluindo o boto "Adicionar ao carrinho".

Clique no boto "Adicionar ao carrinho" mostra a nossa viso de compras carrinho de ndice com a lista de Resumo de compras carrinho.

Depois de carregar o seu carrinho de compras, voc pode clicar em retirar do link carrinho para ver a actualizao do Ajax ao seu carrinho de compras.

Ns construmos para fora de um trabalho de carrinho que permite que os utilizadores no registados para adicionar itens ao seu carrinho de compras. Na seo seguinte, ns vai permitir que registrar e completar o processo de checkout.

Parte 9: Registo e check-out


Por Jon Galloway|21 De abril de 2011 A loja de msica do MVC um aplicativo tutorial que apresenta e explica passo a passo como usar o ASP.NET MVC e Visual Studio para desenvolvimento web. A loja de msica do MVC um leve exemplo de implementao de loja que vende lbuns de msica on-line e implementa a administrao do site bsico, entrar usurio e funcionalidade do carrinho de compras. Esta srie de tutoriais detalha todos os passos tomados para construir o aplicativo de exemplo de ASP.NET MVC Music Store. Parte 9 aborda o registro e check-out. Nesta seo, criaremos um CheckoutController que ir recolher informaes de pagamento e endereo de compras. Necessitamos que os usurios se registrem em nosso site antes do check-out, ento este controlador exigir autorizao. Os usurios navegaro para o processo de check-out de seu carrinho de compras clicando no boto "Checkout".

Se o usurio no est logado, vo ser solicitado a eles.

Aps um logon bem-sucedido, o usurio ento mostrado o endereo e pagamento a vista.

Uma vez que eles tm preenchido o formulrio e enviada a ordem, eles sero mostrados a tela de confirmao de pedido.

Tentando ver uma ordem inexistentes ou uma ordem que no pertence a voc mostrar a exibio de erro.

Migrando o carrinho de compras


Enquanto o processo de compra annimo, quando o usurio clica no boto Checkout, eles devero registar-se e faa o login. Os usurios esperam que vamos manter suas informaes de carrinho de compras entre visitas, ento temos de associar as informaes de carrinho de compras com um usurio quando eles concluem o registo ou login. Isto realmente muito simples de fazer, como nossa classe ShoppingCart j tem um mtodo que ir associar todos os itens no carrinho de atual com um nome de usurio. S precisamos de chamar esse mtodo quando um usurio concluir o registo ou login. Abra a classe AccountController que adicionamos quando ns estvamos preparando adeso e autorizao.Adicionar um usando instruo referenciando MvcMusicStore.Models, em seguida, adicione o seguinte mtodo de MigrateShoppingCart: private void MigrateShoppingCart(string UserName)

{ // Associate shopping cart items with logged-in user var cart = ShoppingCart.GetCart(this.HttpContext); cart.MigrateCart(UserName); Session[ShoppingCart.CartSessionKey] = UserName; } Em seguida, modifica a ao de post de LogOn para chamar MigrateShoppingCart depois que o usurio foi validado, como mostrado abaixo: // // POST: /Account/LogOn [HttpPost] public ActionResult LogOn(LogOnModel model, string returnUrl) { if (ModelState.IsValid) { if (Membership.ValidateUser(model.UserName, model.Password)) { MigrateShoppingCart(model.UserName); FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe); if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/") && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\")) { return Redirect(returnUrl); } else { return RedirectToAction("Index", "Home"); } } else { ModelState.AddModelError("", "The user name or password provided is incorrect."); } } // If we got this far, something failed, redisplay form return View(model); } Faa a mesma alterao ao registo post ao, imediatamente depois que a conta de usurio criada com xito: // // POST: /Account/Register [HttpPost] public ActionResult Register(RegisterModel model) { if (ModelState.IsValid) { // Attempt to register the user MembershipCreateStatus createStatus;

Membership.CreateUser(model.UserName, model.Password, model.Email, "question", "answer", true, null, out createStatus); if (createStatus == MembershipCreateStatus.Success) { MigrateShoppingCart(model.UserName); FormsAuthentication.SetAuthCookie(model.UserName, false /* createPersistentCookie */); return RedirectToAction("Index", "Home"); } else { ModelState.AddModelError("", ErrorCodeToString(createStatus)); } } // If we got this far, something failed, redisplay form return View(model); } isso a - agora um carrinho de compras annimo ser automaticamente transferido para uma conta de usurio aps registo ou login.

Criando o CheckoutController
Boto direito do mouse sobre a pasta de controladores e adicionar um novo controlador para o projeto chamado CheckoutController usando o modelo de controlador vazio.

Primeiro, adicione o atributo de autorizar acima da declarao de classe do controlador para exigir que os usurios se registrem antes de fazer check-out: namespace MvcMusicStore.Controllers { [Authorize]

public class CheckoutController : Controller Nota: Isto semelhante para a mudana que fizemos anteriormente para o StoreManagerController, mas nesse caso, o atributo de autorizar necessrio que o usurio seja em uma funo de administrador. No Controller, Checkout, est exigindo o usurio estar logado, mas no esto exigindo que sejam administradores.

Por razes de simplificao, no tratamos com informaes de pagamento neste tutorial. Em vez disso, estamos a permitir que os usurios para verific para fora usando um cdigo promocional. Vamos guardar este cdigo promocional usando uma constante chamada PromoCode. Como o StoreController, ns vai declarar um campo para manter uma instncia da classe MusicStoreEntities, chamada storeDB. A fim de fazer uso da classe MusicStoreEntities, precisamos adicionar um usando instruo para o namespace MvcMusicStore.Models. A parte superior do nosso controlador de Checkout aparece abaixo. using System; using System.Linq; using System.Web.Mvc; using MvcMusicStore.Models; namespace MvcMusicStore.Controllers { [Authorize] public class CheckoutController : Controller { MusicStoreEntities storeDB = new MusicStoreEntities(); const string PromoCode = "FREE"; O CheckoutController ter as seguintes aes de controlador: AddressAndPayment (mtodo GET) ir exibir um formulrio para permitir que o usurio insira suas informaes. AddressAndPayment (mtodo POST) ir validar a entrada e processar o pedido. Complete ser mostrado aps um usurio terminou com xito o processo de checkout. Essa exibio incluir o nmero de ordem do usurio, como confirmao. Primeiro, vamos renomear a ao de controlador de ndice (que foi gerada quando criamos o controlador) para AddressAndPayment. Esta ao de controlador s exibe o formulrio de check-out, por isso no exige qualquer informao de modelo. // // GET: /Checkout/AddressAndPayment public ActionResult AddressAndPayment() { return View(); } Nosso mtodo de AddressAndPayment POST seguir o mesmo padro usado na StoreManagerController: ele vai tentar aceitar o envio do formulrio e completar o pedido e re-ir exibir o formulrio, se ele falhar. Depois de validar a entrada de formulrio encontra nossos requisitos de validao de uma ordem, vamos verificar o valor do formulrio PromoCode diretamente. Assumindo que tudo est correto, que vamos salvar as informaes atualizadas com a ordem, diz o objeto ShoppingCart para concluir o processo de ordem e redirecionar para a ao completa. // // POST: /Checkout/AddressAndPayment [HttpPost] public ActionResult AddressAndPayment(FormCollection values) { var order = new Order(); TryUpdateModel(order);

try { if (string.Equals(values["PromoCode"], PromoCode, StringComparison.OrdinalIgnoreCase) == false) { return View(order); } else { order.Username = User.Identity.Name; order.OrderDate = DateTime.Now; //Save Order storeDB.Orders.Add(order); storeDB.SaveChanges(); //Process the order var cart = ShoppingCart.GetCart(this.HttpContext); cart.CreateOrder(order); return RedirectToAction("Complete", new { id = order.OrderId }); } } catch { //Invalid - redisplay with errors return View(order); } } Aps a concluso bem-sucedida do processo de check-out, usurios sero redirecionados para a ao de controlador completo. Esta aco ir executar uma verificao simples para validar que a ordem pertence efectivamente para o usurio que fez logon antes de mostrar o nmero de ordem, como uma confirmao. // // GET: /Checkout/Complete public ActionResult Complete(int id) { // Validate customer owns this order bool isValid = storeDB.Orders.Any( o => o.OrderId == id && o.Username == User.Identity.Name); if (isValid) { return View(id); } else { return View("Error"); } } Nota: A exibio de erro foi criada automaticamente para ns na pasta /Views/Shared quando iniciamos o projeto. O cdigo completo do CheckoutController a seguinte: using System;

using System.Linq; using System.Web.Mvc; using MvcMusicStore.Models; namespace MvcMusicStore.Controllers { [Authorize] public class CheckoutController : Controller { MusicStoreEntities storeDB = new MusicStoreEntities(); const string PromoCode = "FREE"; // // GET: /Checkout/AddressAndPayment public ActionResult AddressAndPayment() { return View(); } // // POST: /Checkout/AddressAndPayment [HttpPost] public ActionResult AddressAndPayment(FormCollection values) { var order = new Order(); TryUpdateModel(order); try { if (string.Equals(values["PromoCode"], PromoCode, StringComparison.OrdinalIgnoreCase) == false) { return View(order); } else { order.Username = User.Identity.Name; order.OrderDate = DateTime.Now; //Save Order storeDB.Orders.Add(order); storeDB.SaveChanges(); //Process the order var cart = ShoppingCart.GetCart(this.HttpContext); cart.CreateOrder(order); return RedirectToAction("Complete", new { id = order.OrderId }); } } catch { //Invalid - redisplay with errors return View(order); } } // // GET: /Checkout/Complete

public ActionResult Complete(int id) { // Validate customer owns this order bool isValid = storeDB.Orders.Any( o => o.OrderId == id && o.Username == User.Identity.Name); if (isValid) { return View(id); } else { return View("Error"); } } } }

Adicionando a vista AddressAndPayment


Agora, vamos criar o modo de exibio de AddressAndPayment. Boto direito do mouse em um do as AddressAndPayment aes do controlador e adicionar uma exibio chamada AddressAndPayment que fortemente tipado como uma ordem e usa o modelo de edio, como mostrado abaixo.

Essa viso far uso de duas das tcnicas ns olhamos ao construir a viso de StoreManagerEdit: Ns usaremos o Html.EditorForModel() para exibir os campos de formulrio para o modelo de ordem

Ns ir alavancar as regras de validao usando uma classe de ordem com atributos de validao

Vamos comear, atualizando o cdigo de formulrio para usar Html.EditorForModel(), seguido por uma caixa de texto adicional para o cdigo promocional. O cdigo completo para o modo de exibio de AddressAndPayment mostrado abaixo. @model MvcMusicStore.Models.Order @{ ViewBag.Title = "Address And Payment"; } <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script> @using (Html.BeginForm()) { <h2>Address And Payment</h2> <fieldset> <legend>Shipping Information</legend> @Html.EditorForModel() </fieldset> <fieldset> <legend>Payment</legend> <p>We're running a promotion: all music is free with the promo code: "FREE"</p> <div class="editor-label"> @Html.Label("Promo Code") </div> <div class="editor-field"> @Html.TextBox("PromoCode") </div> </fieldset> <input type="submit" value="Submit Order" /> }

Definio de regras de validao para a ordem


Agora que nossa viso est configurado, definiremos as regras de validao para o nosso modelo de ordem como fizemos anteriormente para o modelo de lbum. Boto direito do mouse na pasta modelos e adicionar uma classe chamada ordem. Alm dos atributos de validao que usamos anteriormente para o lbum, vamos tambm estar usando uma expresso Regular para validar o endereo de email do usurio. using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Web.Mvc; namespace MvcMusicStore.Models { [Bind(Exclude = "OrderId")] public partial class Order { [ScaffoldColumn(false)] public int OrderId { get; set; } [ScaffoldColumn(false)] public System.DateTime OrderDate { get; set; } [ScaffoldColumn(false)]

public string Username { get; set; } [Required(ErrorMessage = "First Name is required")] [DisplayName("First Name")] [StringLength(160)] public string FirstName { get; set; } [Required(ErrorMessage = "Last Name is required")] [DisplayName("Last Name")] [StringLength(160)] public string LastName { get; set; } [Required(ErrorMessage = "Address is required")] [StringLength(70)] public string Address { get; set; } [Required(ErrorMessage = "City is required")] [StringLength(40)] public string City { get; set; } [Required(ErrorMessage = "State is required")] [StringLength(40)] public string State { get; set; } [Required(ErrorMessage = "Postal Code is required")] [DisplayName("Postal Code")] [StringLength(10)] public string PostalCode { get; set; } [Required(ErrorMessage = "Country is required")] [StringLength(40)] public string Country { get; set; } [Required(ErrorMessage = "Phone is required")] [StringLength(24)] public string Phone { get; set; } [Required(ErrorMessage = "Email Address is required")] [DisplayName("Email Address")] [RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}", ErrorMessage = "Email is is not valid.")] [DataType(DataType.EmailAddress)] public string Email { get; set; } [ScaffoldColumn(false)] public decimal Total { get; set; } public List<OrderDetail> OrderDetails { get; set; } } } A tentativa de enviar o formulrio com falta ou informaes invlidas agora iro mostrar a mensagem de erro usando a validao do lado do cliente.

Ok, fizemos a maior parte do trabalho duro para o processo de check-out; S temos algumas probabilidades e extremidades para terminar. Precisamos adicionar dois modos de exibio simples, e ns precisamos cuidar a entrega das informaes durante o processo de login carrinho.

Adicionando a exibio completa de Checkout


A viso completa de Checkout bastante simples, como ele s precisa exibir a ID de ordem. Boto direito do mouse sobre a ao de controlador completo e adicionar uma exibio chamada Complete, que fortemente tipado como um int.

Agora vamos atualizar o cdigo de exibio para exibir a ID de ordem, como mostrado abaixo. @model int @{ ViewBag.Title = "Checkout Complete"; } <h2>Checkout Complete</h2> <p>Thanks for your order! Your order number is: @Model</p> <p>How about shopping for some more music in our @Html.ActionLink("store", "Index", "Home") </p>

Vista o erro de atualizao


O modelo padro inclui uma vista erro na pasta compartilhada de exibies para que pode ser reutilizado em outro lugar no site. Esta viso de erro contm um erro muito simples e no usa nosso site, Layout, ento vamos atualiz-lo. Pois esta uma pgina de erro genrica, o contedo muito simples. Ns incluiremos uma mensagem e um link para navegar para a pgina anterior no histrico se o usurio quer tentar re-sua ao. @{ ViewBag.Title = "Error"; } <h2>Error</h2> <p>We're sorry, we've hit an unexpected error. <a href="javascript:history.go(-1)">Click here</a>

if you'd like to go back and try that again.</p> Por favor, use as discusses no http://mvcmusicstore.codeplex.com para quaisquer perguntas ou comentrios.

You might also like