本文共 8065 字,大约阅读时间需要 26 分钟。
这里,我们需要做一些事情,这些事情意味着深度改造前台:
1:为商品增加 添加到购物车 按钮,点击后功能实现;
2:商品排序;
3:购物车预览,以及添加 结算 按钮;
4:一个显式 购物车中有*个 商品 的widget;
一:添加到购物车 按钮
修改 Views/Parts/Product.cshtml:
@{
var price = (decimal)Model.Price; var sku = (string)Model.Sku; } <article> Price: @price<br /> Sku: @sku <footer> <button>Add to shoppingcart</button> </footer> </article>
现在,商品列表上已经有了这个按钮:
包括商品详细页面,也有这个按钮。
1.1 问题
问题来了,我们发现,该按钮在商品介绍上面,如果我们想放到下面,该怎么做呢?
1.2 修改 Display 方法
protected override DriverResult Display(ProductPart part, string displayType, dynamic shapeHelper)
{ //return ContentShape("Parts_Product", () => shapeHelper.Parts_Product( // Price: part.UnitPrice, // Sku: part.Sku // )); return Combined(// Shape 1: Parts_Product
ContentShape("Parts_Product", () => shapeHelper.Parts_Product( Price: part.UnitPrice, Sku: part.Sku )),// Shape 2: Parts_Product_AddButton
ContentShape("Parts_Product_AddButton", () => shapeHelper.Parts_Product_AddButton()) ); }
在修改后的方法内,我们创造了一个新的 Shape,叫做 Parts_Product_AddButton,同时,我们使用 Combined 方法,它返回的是依旧是 DriverResult,只不过,它组合了两个形状。
这个时候,我们知道,需要创建一个 Views/Parts/Product.Addbutton.cshtml 文件:
<button>@T("Add to shoppingcart")</button>
当然,我们得把原先的 Product.cshtml 中的代码给恢复过来。
然后,修改 placement.info,为:
<Placement>
<Place Parts_Product_Edit="Content:1" /> <Place Parts_Product="Content:0" /> <Place Parts_Product_AddButton="Content:after" /> </Placement>
然后,看到不一样了:
二:实现 添加到购物车 功能
2.1 前台准备
首先,我们创建文件夹 Controllers,然后控制器:
using System;
using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web.Mvc;namespace TMinji.Shop.Controllers
{ public class ShoppingCartController : Controller {[HttpPost]
public ActionResult Add(int id) { return RedirectToAction("Index"); } }}
然后,修改 Views/Parts/Product.AddButton.cshtml:,
@using (Html.BeginForm("Add", "ShoppingCart", new { id = -1 }))
{ <button type="submit">@T("Add to shoppingcart")</button> }
让它变成一个表单,并且指向到 Add 方法。
注意到,我们这里传递的 id = -1,所以,我们还需要做一件事情,那就是把真是的商品 id 传递过来,才能添加商品。
于是乎,我们首先应该把 id 传递到 shape 中,修改,我们的 Display 方法,如下:
protected override DriverResult Display(ProductPart part, string displayType, dynamic shapeHelper)
{ //return ContentShape("Parts_Product", () => shapeHelper.Parts_Product( // Price: part.UnitPrice, // Sku: part.Sku // )); return Combined(// Shape 1: Parts_Product
ContentShape("Parts_Product", () => shapeHelper.Parts_Product( Price: part.UnitPrice, Sku: part.Sku )),// Shape 2: Parts_Product_AddButton
ContentShape("Parts_Product_AddButton", () => shapeHelper.Parts_Product_AddButton( ProductId: part.Id)) ); }
然后,前台 Views/Parts/Product.AddButton.cshtml 如下:
@{
var productId = (int)Model.ProductId; } @using (Html.BeginForm("Add", "ShoppingCart", new { id = productId })) { <button type="submit">@T("Add to shoppingcart")</button> }
现在,就可以把值传递到控制器了,现在,我们需要实现业务逻辑部分。
2.2 业务逻辑之 Orchard Service
在 Models 目录下,增加 ShoppingCartItem 实体类:
[Serializable]
public sealed class ShoppingCartItem { public int ProductId { get; private set; }private int _quantity;
public int Quantity { get { return _quantity; } set { if (value < 0) throw new IndexOutOfRangeException();_quantity = value;
} }public ShoppingCartItem()
{ }public ShoppingCartItem(int productId, int quantity = 1)
{ ProductId = productId; Quantity = quantity; } }
创建 Services 文件夹,然后创建 IShoppingCart 接口,
public interface IShoppingCart : IDependency
{ IEnumerable<ShoppingCartItem> Items { get; } void Add(int productId, int quantity = 1); void Remove(int productId); ProductPart GetProduct(int productId); decimal Subtotal(); decimal Vat(); decimal Total(); int ItemCount(); void UpdateItems(); }
然后,其实现类:
using Orchard;
using Orchard.ContentManagement; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web; using TMinji.Shop.Models;namespace TMinji.Shop.Services
{ public class ShoppingCart : IShoppingCart { private readonly IWorkContextAccessor _workContextAccessor; private readonly IContentManager _contentManager; public IEnumerable<ShoppingCartItem> Items { get { return ItemsInternal.AsReadOnly(); } }private HttpContextBase HttpContext
{ get { return _workContextAccessor.GetContext().HttpContext; } }private List<ShoppingCartItem> ItemsInternal
{ get { var items = (List<ShoppingCartItem>)HttpContext.Session["ShoppingCart"];if (items == null)
{ items = new List<ShoppingCartItem>(); HttpContext.Session["ShoppingCart"] = items; }return items;
} }public ShoppingCart(IWorkContextAccessor workContextAccessor, IContentManager contentManager)
{ _workContextAccessor = workContextAccessor; _contentManager = contentManager; }public void Add(int productId, int quantity = 1)
{ var item = Items.SingleOrDefault(x => x.ProductId == productId);if (item == null)
{ item = new ShoppingCartItem(productId, quantity); ItemsInternal.Add(item); } else { item.Quantity += quantity; } }public void Remove(int productId)
{ var item = Items.SingleOrDefault(x => x.ProductId == productId);if (item == null)
return;ItemsInternal.Remove(item);
}public ProductPart GetProduct(int productId)
{ return _contentManager.Get<ProductPart>(productId); }public void UpdateItems()
{ ItemsInternal.RemoveAll(x => x.Quantity == 0); }public decimal Subtotal()
{ return Items.Select(x => GetProduct(x.ProductId).UnitPrice * x.Quantity).Sum(); }public decimal Vat()
{ return Subtotal() * .19m; }public decimal Total()
{ return Subtotal() + Vat(); }public int ItemCount()
{ return Items.Sum(x => x.Quantity); }private void Clear()
{ ItemsInternal.Clear(); UpdateItems(); } } }
以上代码不再一一解释,相信大家能看明白,然后,相应的,修改控制器吧:
using Orchard;
using System; using Orchard.Mvc; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web.Mvc; using TMinji.Shop.Services;namespace TMinji.Shop.Controllers
{ public class ShoppingCartController : Controller {private readonly IShoppingCart _shoppingCart;
private readonly IOrchardServices _services;public ShoppingCartController(IShoppingCart shoppingCart, IOrchardServices services)
{ _shoppingCart = shoppingCart; _services = services; }[HttpPost]
public ActionResult Add(int id) {// Add the specified content id to the shopping cart with a quantity of 1.
_shoppingCart.Add(id, 1);// Redirect the user to the Index action (yet to be created)
return RedirectToAction("Index"); }public ActionResult Index()
{// Create a new shape using the "New" property of IOrchardServices.
var shape = _services.New.ShoppingCart();// Return a ShapeResult
return new ShapeResult(this, shape); }}
}
在控制器中,我们看到了三点变化:
1:构造器接受了两个对象,它们是被注入的,这会由 Orchard 完成;
2:Add 方法可以添加商品到购物车了;
3:增加了一个 Index 方法。可以看到在这个方法中,我们又创建了一个 Shape,而这个 Shape 的名字叫做 ShoppingCart。注意哦,它和后台创建 Shape 不一样(记得吗,在 Display 方法 中),这个 shape 对于那个的 cshtml 文件为Views/ShoppingCart.cshtml:
TODO: display our shopping cart contents!
如果我们这个时候运行代码,会发现点击 添加到购物车 后,变成 404 。这是因为,我们没有为路由加上 area,它是什么,是模块名,Orchard 会根据这个 area,路由到对于的模块中的 controller。故,我们应该修改Views/Parts/Product.AddButton.cshtml:
@{
var productId = (int)Model.ProductId; } @using (Html.BeginForm("Add", "ShoppingCart", new { id = productId, area = "TMinji.Shop" })) { <button type="submit">@T("Add to shoppingcart")</button> }
现在,继续运行代码,现在,我们得到错误:
The required anti-forgery form field "__RequestVerificationToken" is not present.
当然,这个错误,熟悉 MVC 的我们,已经知道怎么修改了,修改之:
@{
var productId = (int)Model.ProductId; } @using (Html.BeginFormAntiForgeryPost(Url.Action("Add", "ShoppingCart", new { id = productId, area = "TMinji.Shop" }))) { <button type="submit">@T("Add to shoppingcart")</button> }
再次运行:
我们看到,这个页面没有包含当前的 Theme 的母版页,回到控制器,加一个 ThemedAttribute(当然,我们得 using Orchard.Themes;),如下:
[Themed]
public ActionResult Index() { // Create a new shape using the "New" property of IOrchardServices. var shape = _services.New.ShoppingCart();// Return a ShapeResult
return new ShapeResult(this, shape); }
这个时候,再次运行,得到了如下的理想效果:
总结
1:前台的展现,主要通过 MVC 的控制器来实现的,而后台则绕开了控制器;
2:业务逻辑,使用 Service 机制,而不是我们自己瞎写的类和逻辑(当然,也可以),但 Service 机制看上去更 Orchard;
3:前后台创建 Shape 的方式是不一样的。
本文转自最课程陆敏技博客园博客,原文链接:http://www.cnblogs.com/luminji/p/3860188.html,如需转载请自行联系原作者