This is part 4 of my walkthrough document for project Orchard based on the document released in March 2010. Check out here for previous posts – Part 1 Hello World, Part 2 Hocking up admin page, Part 3 Working with data
What is Content Type
Orchard Content Type maybe somewhat similar to SharePoint content type conceptually but architecturally its apple and orange. According to the walkthrough document, an Orchard Content Type is:-
Rather than work with database records directly, let’s start using some of the higher-level abstractions that Orchard CMS offers. Fundamental to the Orchard CMS is the idea of multiple content types – the built-in pages and posts types are examples….
Most importantly:
…we will take advantage of it in the next section, when we explore composition of additional “parts” on the content types (the way that comments and tags are parts applied to pages and posts). Assembling the data and UI of the application from multiple types and parts allows the system to remain loosely coupled and allows installed modules to extend each others types in interesting ways.
Objectives:
1. Create a Product content type
2. Add a ProductHandler for Product type
a. Associate repository and ProductRecord type
b. Associate Product type to content type name
Follow These Steps:
1. Go to the ProductRecord.cs file and update it to inherites ContentPartRecord <Note that I removed the ID property because the base class already has one>:
using System.ComponentModel.DataAnnotations;using System.Web.Mvc;using Orchard.Data;using Orchard.ContentManagement;using Orchard.ContentManagement.Records;using Orchard.ContentManagement.Handlers;
namespace Orchard.Commerce.Models{
public class ProductRecord: ContentPartRecord {
[Required] public virtual string Sku { get; set; }
[Required] public virtual string Description { get; set; }
[Required] public virtual decimal Price { get; set; }
}
}
2. Add a Product.cs file in the Model folder.
using System;using System.Collections.Generic;using System.Linq;using System.Web;using Orchard.ContentManagement;using System.ComponentModel.DataAnnotations;
namespace Orchard.Commerce.Models{ public class Product : ContentPart<ProductRecord> { public int Id { get { return Record.Id; } set { Record.Id = value; } }
[Required] public string Sku { get { return Record.Sku; } set { Record.Sku = value; } }
[Required] public string Description { get { return Record.Description; } set { Record.Description = value; } }
[Required] public decimal Price { get { return Record.Price; } set { Record.Price = value; } } } }
Then create a Handlers folder and add a ProductHandler class
using Orchard.ContentManagement.Handlers;using Orchard.ContentManagement;using Orchard.Commerce.Models;using Orchard.Data;
namespace Orchard.Commerce.Handlers{ public class ProductHandler : ContentHandler { public readonly static ContentType ContentType = new ContentType { Name = "product", DisplayName = "Product" };
public ProductHandler(IRepository<ProductRecord> repository) { Filters.Add(new ActivatingFilter<Product>(ProductHandler.ContentType.Name)); Filters.Add(StorageFilter.For(repository)); }
}}
The Product type is an Orchard content type, using ProductRecord for storage. For convenience, we’ve wrapped the properties of the record on the Product type, but this is optional, as the Product type also exposes these properties directly from its Record property, for example Product.Record.SKU. The ProductHandler is responsible for registering the content type with the system, ensuring the the Product type is activated whenever a new “Product” content item is created, and associating the IRepository<ProductRecord> for storage.
We also need to update our Commerce admin pages to use the new content type instead of the record. To do this, we will go through the IContentManager interface to query for products instead of talking to the repository directly. The ContentManager is responsible for resolving the requested content type to a handler (in this case, ProductHandler) and instantiating the type for us.
3. In AdminController.cs…
a. Change all references from “ProductRecord” to “Product”…
b. Change all references to “IRepository<ProductRecord>” to “IContentManager”…
using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;
using Orchard.Themes;using Orchard.UI.Admin;using Orchard.ContentManagement;using Orchard.Data;using Orchard.Commerce.Models;
namespace Orchard.Commerce.Controllers{ [Admin] [Themed] public class AdminController : Controller {
private readonly IContentManager _contentManager;
public AdminController(IContentManager contentManager) { _contentManager = contentManager; }
public ActionResult Index() { return View(_contentManager.Query<Product>().List()); }
public ActionResult Create() { return View(_contentManager.New<Product>("product")); }
[HttpPost] public ActionResult Create(FormCollection input) { var product = _contentManager.New<Product>("product");
if (!TryUpdateModel(product)) { return View(product); }
_contentManager.Create(product);
return RedirectToAction("Index"); } }}
4. Change the “Create.ascx” view to use “Product” instead of “ProductRecord” in the first line (the view model):
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Orchard.Commerce.Models.Product>" %>
5. Change the “Index.ascx” view to use “Product” instead of “ProductRecord” in the first line (the view model):
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Orchard.Commerce.Models.Product>>" %>
6. Type Ctrl-F5 to build and run in Visual Studio. And when you try to add a new product you will be hit by exception. This is because the database table schema has not been updated.
7. Orchard introduces a concept of data migration so that whenever an ContentType is created user will be prompted to update the database via the administration page.
Now create a DataMigrations folder and add a ProductDataMigration class
using Orchard.Data.Migration;
namespace Orchard.Commerce.DataMigrations{ public class ProductDataMigration : DataMigrationImpl { public int Create() { SchemaBuilder.CreateTable("ProductRecord", table => table .ContentPartRecord() .Column<string>("Sku") .Column<string>("Description") .Column<string>("Price") );
return 0010; } }
}
8. Hit Ctrl-F5 and go back to the admin page, you will notice a message on top of the dashboard.
9. Click on Site Configuration –> Features. Notice that Orchard.Commerce is in red. Click on the Update command
10. Surprisingly I am hit with a transaction error.
11. So I found a workaround by renaming the table first from Orchard_Commerce_Product to Orchard_Commerce_Product_bck
12. And I went back to click on Upgrade again. The table migrated successful.
13. And I can start data entry for Products.
Hi, I got an error while working through this. I downloaded your sample and the same happens, I was sure it was something I’d done as well! Happens when I hit Admin/Commerce/Create, on this line :
var product = _contentManager.New(“product”);
Error is :
[InvalidCastException: Specified cast is not valid.]
Orchard.ContentManagement.ContentCreateExtensions.New(IContentManager manager, String contentType) in c:\Projects\Orchard\src\Orchard\ContentManagement\ContentExtensions.cs:19
Orchard.Commerce.Controllers.AdminController.Create() +93
lambda_method(Closure , ControllerBase , Object[] ) +79
etc…
Just to update here, I got it working on a fresh install of Orchard. I think that because there was already a Orchard.Commerce feature hanging around from another tutorial (http://www.orchardproject.net/docs/Creating-a-module-with-a-simple-text-editor.ashx) it caused a problem somewhere along the line. All works now cheers