原文地址: http://skywalkersoftwaredevelopment.net/blog/writing-an-orchard-webshop-module-from-scratch-part-5
创建和渲染ProductCatalog的内容类型
这是从头开始编写一个新的Orchard模块的教程的第5篇。
对于本教程的概述,请参阅介绍。
为了网站的访问者能够将产品添加到他们的购物车,我们需要一个产品目录。
产品目录可以是一个简单的产品清单。然而,在本教程中,我们希望主题作者能够接管渲染(rendering)工作,例好对商品按照某种类别分类进行分类。为了做到这一点,我们需要能够'换肤(sknnable)'的东西,清单本身是不容易换肤的(它是可以换肤,但所有的清单将有着相同的外观,这不是我们想要的。我不得不承认我不知道这个案件是否可行,但我曾在定制清单渲染时遇到过一些问题,我不得不给它改成别的样子)。
所以,我们将介绍ProductCatalog内容类型,从中我们可以创建一个ProductCatalog内容项,反过来,如果我们决定,我们要自定义渲染,我们可以创建一个模板。
我们的产品目录的要求如下:
- 每个目录显示一类产品(如书籍,DVD光盘,CD)。没有技术上的限制,以防止其他产品类型显示,这只是我们的演示需要。
- 目录的呈现应该可以被其他主题重写,所以我们将它创建作为一个内容类型(而不是让用户自已创建一个新的列表)。
让我们开始定义ProductCatalog到Migration类,添加以下代码定义:
public int UpdateFrom2()
{
ContentDefinitionManager.AlterTypeDefinition("ProductCatalog", type => type
.WithPart(typeof(ContainerPart).Name)
.WithPart(typeof(MenuPart).Name)
.WithPart(typeof(AdminMenuPart).Name)
.WithPart(typeof(RoutePart).Name)
.Creatable()
);
return 3;
}
这段代码使用ContentDefinitionManager了一个新的Content Type名为的“ProductCatalog”,它包含3个部件(Part),并可以从管理界面上创建。最后,它返回一个新的Ochard Migration的版本号,以保持我们的模块的当前版本的跟踪。
关于部件(Parts):
以便ProductCatalog可以包含其他内容项,ContainerPart是必要。
MenuPart是可选的,但它使我们的网站管理员可以添加产品目录到主菜单上。
AdminMenuPart也是可选,但它可以让我们的网站管理员添加产品目录到管理界面的主菜单上,这是很方便的。
RoutePart事实上是为了能够从前端访问,并同时给ProductCatalog提供了Slug(缩略词)和title属性。
提示一句,正如你可以看到,我使用了typeof(ContainerPart).Name表达式。这是因为我们的模块添加了对项目Orchard.Core的引用,才可以这样。
对于其他类型的内容,你可能要附加的一些在第三方模块中已经定义了的Content Part。然而,当你添加其他模块的引用,你应该提醒你的模块的用户,他们必须先安装依赖的模块。如果他们先安装了你的模块,而没有安装依赖模块,Orchard将抛出一个异常,要恢复该网站的唯一方法是删除你的模块。
这意味着,如果你只是要简单的附加其他模块的某些部件,只用的名称就可以(如“AdvancedMenuPart”,而不是typeof(AdvancedMenuPart).Name)。
当你刷新管理页面,Orchard将通知您,您需要升级您的模块。当我们继续并更新模块,我们将看到,果园创建了一个新的内容类型(Content Type):
我们也看到,在管理界面的“New”节点,多了一个新的菜单项:
在我们继续之前,我只是想提一提,我们不是一定要使用Migration来创建一个新的内容类型,我们可以在管理界面上作到同样的事。
但是,在代码中这样做可以省掉指导我们的模块的用户如何配置一个新的产品目录(product catalog)。作为模块的开发者,我们应该努力使我们的模块尽可能的易用。
我们将继续创建(并发布)一个新的图书目录,它将能够通过指“Books”作为名称来包含Book的内容类型,“Book”作为Content Type, “Books”作为菜单项的名称。我们也将在管理页面显示菜单作为快捷方式。
下一步,我们将新增的3本书到我们之前创建的图书目录:
现在让我们来看看页面:
我们现在有一个基本的产品目录模块,我们的网站管理员可以对它进行管理。
当你点击“More”链接,你会看到该产品的详细资料:
这是Orchard美妙的地方之一:你不必只是盯着有一组属性的集合的网页:一个页面也是一个可以附加某些部件的内容类型,你可以自由添加更多的部件。
在我们的演示,我们能够构建一个全新的类型和可重用的功能,如标签和评论部件。
等一下:我们没有附加ProductPart到我们的图书内容类型吗?我们作了,不是吗?但它们却没有出现在目录中,也没有产品的详细信息。我们还漏掉什么呢?
我们没有看到这些字段的原因,是因为我们没有实现ProductPart驱动程序的显示方法。驱动程序的部件内容(主要)是负责创建形状,它代表部件内容的视图。
现在,我们要在页面上显示我们ProductPart,让我们回到我们的ProductDriver并添加以下代码:
protected override DriverResult Display(ProductPart part, string displayType, dynamic shapeHelper) {
return ContentShape("Parts_Product", () => shapeHelper.Parts_Product(
Price: part.Price,
Sku: part.Sku
));
}
前端页面要显示的内容项目具有附加的ProductPart时,Orchard将调用Display方法,Display方法将调用ContentShape方法并返回ContentShapeResult,这类似于ASP.NET MVC中的PartialView()方法。
ContentShapeResult将包含形状的名称,以及本身形状,这仅仅是一个动态的Clay对象。我们使用shapeHelper对象创建一个形状,并调用一个动态定义的方法。我们通过提交一组参数给这个方法,它将被转化成属性并存储在我们的形状中。产生的形状将成为Razor模板文件的模型,他的名字将根据形状的命名规则进行匹配。关于形状请参见这篇出色的介绍文档!
我们正在创建一个形状名为“Parts_Product”。按照Orchard的规范,这意味着要有一个文件名为“Parts.Product.cshtml”的模版文件存在(下划线取代了.)。
如果我们命名我们的形状“Jacky_Chan_Goes_Mad”,我们的Razor文件应为“Jacky.Chan.Goes.Mad.cshtml”。很显然,我们应该使用有意义的名称,准确地描述我们的形状的特点。以 “Part”作为前缀是一个很好的规范,就像我们的ProductPart。
所以,不要忘记在Views文件夹下创建一个名为“Parts.Product.cshtml”的文件:
否则你会很痛苦的:
在Orchard渲染我们的ProductPart形状之前,我们还需要使用Placement.info配置它的位置(placement):
<Placement>
<Place Parts_Product_Edit="Content:1" />
<Place Parts_Product="Content:0" />
</Placement>
如果您忘记了创建形状的位置,它不会被渲染。
在粗线指示名为“Parts_Product”的形状被添加到区域命名为“Content”在位置0上。位置是用来作为排序指标,以确定的顺序来呈现一个区域内的形状。
有关位置(Placement)的更多的信息,我鼓励您阅读更多关于它的文档。
现在刷新前端页面(不要忘记保存您的更改):
详细页面现在看起来像这样:
这就是创建一个功能性的产品目录所要作的工作。对于这个例子,我们创建了一个单一的产品类型和目录,它很容易让网站管理员定义新的产品类型和产品目录!
在接下来的部分,我们将创建一个购物车的功能,使我们的网站的访问者实际地添加产品并进行结算。