Hello Orchard Final Part–Building the module front end

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:

image

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.

image

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:

image

<%@ 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.

image

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).

clip_image006

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.

image

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

image

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.

6 thoughts on “Hello Orchard Final Part–Building the module front end

  1. 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

  2. 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

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>