With the backend Product administration page done, now we can build the front end to list the products and a simple shopping cart
Following are copy-pastes from the walkthrough at orchardproject.net with my modifications
1. Let’s first update our HomeController query the ContentManager for the list of products, passing the model to the View:
using Orchard.Commerce.Models;using Orchard.ContentManagement;
…
[Themed] public class HomeController : Controller {
private readonly IContentManager _contentManager;
public HomeController(IContentManager contentManager) { _contentManager = contentManager; }
public ActionResult Index() { return View(_contentManager.Query<Product>().List()); } }
2. Now let’s update the Home/Index.ascx view page to render the list of products. To make this easier, delete the existing Index.ascx page and right-click the Index() action to “Add > View” in Visual Studio. This time, we’ll add a strongly-typed view that takes the Product type, and choose “List” for the View content type:
3. In Index.ascx, add this at the top of the file:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Orchard.Commerce.Models.Product>>" %><div class="page-title"><%=Html.TitleForPage("Product list")%></div> <table>
4. Type Ctrl-F5 to build and run the site. Click the “Shop” menu item on the front-end to reveal your list of products.
5. Let’s now create a “Details” view so we can verify we can see tags and add comments (recall the UI composition section above). In the HomeController, add this:
using Orchard.Mvc.Results;
…
public ActionResult Details(int id) { var product = _contentManager.Get<Product>(id); if (product == null) return new NotFoundResult();
var viewModel = _contentManager.BuildDisplayModel<Product>(product, "Details");
return View(viewModel); }
6. Let’s then create the “Details.ascx” view under Views > Home:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Orchard.Mvc.ViewModels.ContentItemViewModel<Orchard.Commerce.Models.Product>>" %><div class="page-title"> <%=Html.TitleForPage("Product details")%></div><%= Html.DisplayForItem(Model) %><div> <%=Html.ActionLink("Back to List", "Index") %></div>
Note the “Details” string. It a convention for the type of display (other include summary, list, etc.). Let’s update the ProductDriver to handle the composition:
protected override DriverResult Display(Product product, string displayType){ return ContentPartTemplate(product, "Product.Fields").Location("primary", "1");}
This is very similar to the “Editor” method. Product.Fields is the template used to render the fields. Let’s create Product.Fields.ascx in the “DisplayTemplates” folder.
7. Add Product.Fields.ascx under the “Views > DisplayTemplates” folder:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Orchard.Commerce.Models.Product>" %><fieldset> <legend> <%= Html.Encode(Model.Sku)%></legend> <p> </p> <h3> <%= Html.Encode(Model.Description)%></h3> <p> Price: <%= Html.Encode(String.Format("{0:F}", Model.Price))%></p></fieldset>
8. And finally update the Index view to link the “sku” field to the product details page:
Replace this:
<td> <%: item.Sku %></td>
With this:
<td> <%= Html.ActionLink(item.Sku ?? "sku", "Details", new { id = item.Id })%></td>
9. Type Ctrl-F5 to build and run the site. Now, when clicking on a “sku” in the index view, we get to the product details page (with comments and tags).
Implementing a basic shopping cart
To give another take on UI composition, we are going to implement a simple shopping cart, and we will display it in the “sidebar” zone using the MVC 2 RenderAction method.
1. Let’s first define the data model we are going to use to store the shopping cart data (we are using simple records, not content items for this simple example). Add a ShoppingCartRecord class in the Models folder:
using Orchard.Data.Conventions;using System.Collections.Generic;
namespace Orchard.Commerce.Models{ public class ShoppingCartRecord { public ShoppingCartRecord() { Entries = new List<ShoppingCartEntryRecord>(); }
public virtual int Id { get; set; }
public virtual int UserId { get; set; }
[CascadeAllDeleteOrphan] public virtual IList<ShoppingCartEntryRecord> Entries { get; set; }
}
public class ShoppingCartEntryRecord { public virtual int Id { get; set; } public virtual ProductRecord Product { get; set; } public virtual int Quantity { get; set; } }}
2. Next, let’s update the HomeController class to use more services we are going to need:
using Orchard.Data;using Orchard.UI.Notify;using Orchard.Security;using Orchard.Localization;
…
[Themed] public class HomeController : Controller { private readonly IContentManager _contentManager; private readonly IRepository<ShoppingCartRecord> _repository; private readonly INotifier _notifier; public virtual IUser CurrentUser { get; set; } public Localizer T { get; set; }
public HomeController(IContentManager contentManager, IRepository<ShoppingCartRecord> repository, INotifier notifier) { _contentManager = contentManager; _repository = repository; _notifier = notifier; }
Some things to note about this code:
§ “IRespository<ShoppingCartRecord> gives access to the database
§ “CurrentUser” gives access to the logged-in user
§ “T” gives access to the localization service
§ “INotifier” gives access to the notification area of the application
Now, let’s implement the “AddToCart” action…
We’ll create the cart for the user if needed, add 1 product to the list, and display a “Product added” message to the notification area of the site.
Note that in this simple example, we don’t handle anonymous users very well – there is a single shopping cart for the whole site. We also don’t support “merging” the shopping cart content when an anonymous user decides to log in after creating his shopping cart. These are more advanced applications of a shopping cart implementation that we will skip for now.
3. In HomeController, add the AddToCart action method:
public ActionResult AddToCart(int id) { // Retrieve shopping cart for current user
ShoppingCartRecord cart;
if (CurrentUser == null) { cart = _repository.Fetch(c => c.UserId == 0).SingleOrDefault(); }
else { cart = _repository.Fetch(c => c.UserId == CurrentUser.Id).SingleOrDefault(); }
// Create cart if none found
if (cart == null) { cart = new ShoppingCartRecord(); _repository.Create(cart); cart.UserId = (CurrentUser == null ? 0 : CurrentUser.Id); }
// Add product to cart entry var product = _contentManager.Get<Product>(id).Record; var entry = cart.Entries.Where(e => e.Product == product).SingleOrDefault();
if (entry == null) { entry = new ShoppingCartEntryRecord { Product = product, Quantity = 0 }; cart.Entries.Add(entry); }
entry.Quantity++; _notifier.Add(NotifyType.Information, T("Added {1} to shopping cart", entry.Quantity, product.Sku)); // Back to product list return RedirectToAction("Index"); }
4. Finally, update the “Index.ascx” view (under Views > Home) to include a “Add to cart” column:
a. Add a “Buy” column…
<th>Buy</th>
b. And add an “Add To Cart” link…
<td><%= Html.ActionLink("Add to cart", "AddToCart", new { id= item.Id })%></td>
5. Type Ctrl-F5 to build and run the site.
NOTE: Since we created a new record class, there is a need to update the database. Go into the admin page and trigger Database Update under Site Configuration –> Dev Tools menu.
We now have a working “Add to cart” link next to each product in the list
We also get notification that a product has been added to the cart
Well that’s it! There is another section about rendering the shopping cart content on the sidebar which I will update with another post later due to some issue with the Controller rendering.
If you want to revisit back the previous past, you can go to the TOC page I created here.
Hi,Patrick
I also implemented the new module following your posts, you did a great job, i meet many issues when i implemented it following the old walkthrgouth.
but I hit a issue in shoppingcart part, a error occurred when I try to add a product to shopping cart, it show the Commerce_ShoppingCartRecord is not a valid object, I checked the database, this table has not been created in database. Did you hit same issue?
Thanks
Junfeng
Hi Jun Feng,
Plese download my source code compare it to yours.
I get the same error when I try to access /Commerce/home/AddToCart/XXX
It’s the same error as step 15 on http://patrickyong.net/2010/07/26/hello-orchard-part-3working-with-data/. When I run /DevTools/DatabaseUpdate/UpdateDatabase it says “Database updated successfully” but the table isn’t there.
I wondered if I needed to write my an UpdateFromX() database migration function to create the two tables ShoppingCartRecord and ShoppingCardEntryRecord, but I got stuck when thinking about how to tell Orchard about the one to many relationship between the two.
Is this something that Orchard should handle automatically? Is there a missing step in this tutorial? Or am I being stupid?!
Thanks for any help here
Just to add to this, I get the same error when I use the downloaded source code, the table is missing
If anyone has problems with this last step in the tutorial see this forum post :
http://social.msdn.microsoft.com/Forums/en-US/orchardsupport/thread/2ed769fb-99cf-4a5f-b9dc-f2bcb5e32428
This page gets high rank from google search for OrchardProject Ecommerce – would greatly benefit future visitors to see a linke to the module that might as well be called ‘the reference ecommerce implementation at http://gallery.orchardproject.net/List/Modules/Orchard.Module.Nwazet.Commerce.
thx much for the contribution and would be really interested in followups re: lessons learned.