文章目录
概述
NopCommerce源码架构详解-产品列表相关源码分析2。
内容
上一篇我介绍了一上nop中的产品列表相关核心代码轮廓。现在我们来分析里面的详细代码,今天我们就来看看里面处理排序、分页、视图model、价格筛选及视图选择相关的代码。
//sorting PrepareSortingOptions(model.PagingFilteringContext, command); //view mode PrepareViewModes(model.PagingFilteringContext, command); //page size PreparePageSizeOptions(model.PagingFilteringContext, command, category.AllowCustomersToSelectPageSize, category.PageSizeOptions, category.PageSize); //price ranges model.PagingFilteringContext.PriceRangeFilter.LoadPriceRangeFilters(category.PriceRanges, _webHelper, _priceFormatter); var selectedPriceRange = model.PagingFilteringContext.PriceRangeFilter.GetSelectedPriceRange(_webHelper, category.PriceRanges); decimal? minPriceConverted = null; decimal? maxPriceConverted = null; if (selectedPriceRange != null) { if (selectedPriceRange.From.HasValue) minPriceConverted = _currencyService.ConvertToPrimaryStoreCurrency(selectedPriceRange.From.Value, _workContext.WorkingCurrency); if (selectedPriceRange.To.HasValue) maxPriceConverted = _currencyService.ConvertToPrimaryStoreCurrency(selectedPriceRange.To.Value, _workContext.WorkingCurrency); }
产品的列表筛选用到了一个分页的model类CatalogPagingFilteringModel,这个类包含了所有可用筛选条件相关的信息。CatalogPagingFilteringModel相关类的继承关系如下图:

在CatalogPagingFilteringModel构造函数里面对里面的一些字段进行了初始化:
public CatalogPagingFilteringModel() { this.AvailableSortOptions = new List<SelectListItem>(); this.AvailableViewModes = new List<SelectListItem>(); this.PageSizeOptions = new List<SelectListItem>(); this.PriceRangeFilter = new PriceRangeFilterModel(); this.SpecificationFilter = new SpecificationFilterModel(); }
AvailableSortOptions、AvailableViewModes和PageSizeOptions都是类型为List的集合,是为了在页面视图中好展示。
1、Sort
protected virtual void PrepareSortingOptions(CatalogPagingFilteringModel pagingFilteringModel, CatalogPagingFilteringModel command) { if (pagingFilteringModel == null) throw new ArgumentNullException("pagingFilteringModel"); if (command == null) throw new ArgumentNullException("command"); pagingFilteringModel.AllowProductSorting = _catalogSettings.AllowProductSorting; if (pagingFilteringModel.AllowProductSorting) { foreach (ProductSortingEnum enumValue in Enum.GetValues(typeof(ProductSortingEnum))) { var currentPageUrl = _webHelper.GetThisPageUrl(true); var sortUrl = _webHelper.ModifyQueryString(currentPageUrl, "orderby=" + ((int)enumValue).ToString(), null); var sortValue = enumValue.GetLocalizedEnum(_localizationService, _workContext); pagingFilteringModel.AvailableSortOptions.Add(new SelectListItem() { Text = sortValue, Value = sortUrl, Selected = enumValue == (ProductSortingEnum)command.OrderBy }); } } }
2、View model
protected virtual void PrepareViewModes(CatalogPagingFilteringModel pagingFilteringModel, CatalogPagingFilteringModel command) { if (pagingFilteringModel == null) throw new ArgumentNullException("pagingFilteringModel"); if (command == null) throw new ArgumentNullException("command"); pagingFilteringModel.AllowProductViewModeChanging = _catalogSettings.AllowProductViewModeChanging; var viewMode = !string.IsNullOrEmpty(command.ViewMode) ? command.ViewMode : _catalogSettings.DefaultViewMode; pagingFilteringModel.ViewMode = viewMode; if (pagingFilteringModel.AllowProductViewModeChanging) { var currentPageUrl = _webHelper.GetThisPageUrl(true); //grid pagingFilteringModel.AvailableViewModes.Add(new SelectListItem() { Text = _localizationService.GetResource("Catalog.ViewMode.Grid"), Value = _webHelper.ModifyQueryString(currentPageUrl, "viewmode=grid", null), Selected = viewMode == "grid" }); //list pagingFilteringModel.AvailableViewModes.Add(new SelectListItem() { Text = _localizationService.GetResource("Catalog.ViewMode.List"), Value = _webHelper.ModifyQueryString(currentPageUrl, "viewmode=list", null), Selected = viewMode == "list" }); } }
通过查看代码viewMode为不同值返回的html是不一样的。我结合表Category和CategoryTemplate可以看到每个产品分类对应的视图文件名字。如下图:

表CategoryTemplate:

我们打开Nop.Web项目中的视图文件Views/Category/CategoryTemplate.ProductsInGridOrLines.cshtml可以找到下面的代码根据用户选择不同的数据展示方式,分别返回不同的html。
@if (Model.Products.Count > 0) { if (Model.PagingFilteringContext.ViewMode == "list") { @*list mode*@ <div class="product-list"> @foreach (var product in Model.Products) { <div class="item-box"> @Html.Partial("_ProductBox", product) </div> } </div> } else { @*grid mode*@ <div class="product-grid"> @foreach (var product in Model.Products) { <div class="item-box"> @Html.Partial("_ProductBox", product) </div> } </div> } }
3、page size
protected virtual void PreparePageSizeOptions(CatalogPagingFilteringModel pagingFilteringModel, CatalogPagingFilteringModel command, bool allowCustomersToSelectPageSize, string pageSizeOptions, int fixedPageSize) { if (pagingFilteringModel == null) throw new ArgumentNullException("pagingFilteringModel"); if (command == null) throw new ArgumentNullException("command"); if (command.PageNumber <= 0) { command.PageNumber = 1; } pagingFilteringModel.AllowCustomersToSelectPageSize = false; if (allowCustomersToSelectPageSize && pageSizeOptions != null) { var pageSizes = pageSizeOptions.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); if (pageSizes.Any()) { // get the first page size entry to use as the default (category page load) or if customer enters invalid value via query string if (command.PageSize <= 0 || !pageSizes.Contains(command.PageSize.ToString())) { int temp = 0; if (int.TryParse(pageSizes.FirstOrDefault(), out temp)) { if (temp > 0) { command.PageSize = temp; } } } var currentPageUrl = _webHelper.GetThisPageUrl(true); var sortUrl = _webHelper.ModifyQueryString(currentPageUrl, "pagesize={0}", null); sortUrl = _webHelper.RemoveQueryString(sortUrl, "pagenumber"); foreach (var pageSize in pageSizes) { int temp = 0; if (!int.TryParse(pageSize, out temp)) { continue; } if (temp <= 0) { continue; } pagingFilteringModel.PageSizeOptions.Add(new SelectListItem() { Text = pageSize, Value = String.Format(sortUrl, pageSize), Selected = pageSize.Equals(command.PageSize.ToString(), StringComparison.InvariantCultureIgnoreCase) }); } if (pagingFilteringModel.PageSizeOptions.Any()) { pagingFilteringModel.PageSizeOptions = pagingFilteringModel.PageSizeOptions.OrderBy(x => int.Parse(x.Text)).ToList(); pagingFilteringModel.AllowCustomersToSelectPageSize = true; if (command.PageSize <= 0) { command.PageSize = int.Parse(pagingFilteringModel.PageSizeOptions.FirstOrDefault().Text); } } } } else { //customer is not allowed to select a page size command.PageSize = fixedPageSize; } //ensure pge size is specified if (command.PageSize <= 0) { command.PageSize = fixedPageSize; } }
4、price ranges
model.PagingFilteringContext.PriceRangeFilter.LoadPriceRangeFilters(category.PriceRanges, _webHelper, _priceFormatter); var selectedPriceRange = model.PagingFilteringContext.PriceRangeFilter.GetSelectedPriceRange(_webHelper, category.PriceRanges); decimal? minPriceConverted = null; decimal? maxPriceConverted = null; if (selectedPriceRange != null) { if (selectedPriceRange.From.HasValue) minPriceConverted = _currencyService.ConvertToPrimaryStoreCurrency(selectedPriceRange.From.Value, _workContext.WorkingCurrency); if (selectedPriceRange.To.HasValue) maxPriceConverted = _currencyService.ConvertToPrimaryStoreCurrency(selectedPriceRange.To.Value, _workContext.WorkingCurrency); }
5、视图选择
前面我们有提到Nop的分类对应的视图文件是放在表CategoryTemplate里面的,接下来我们来看这部分的逻辑代码:
var templateCacheKey = string.Format(ModelCacheEventConsumer.CATEGORY_TEMPLATE_MODEL_KEY, category.CategoryTemplateId); var templateViewPath = _cacheManager.Get(templateCacheKey, () => { var template = _categoryTemplateService.GetCategoryTemplateById(category.CategoryTemplateId); if (template == null) template = _categoryTemplateService.GetAllCategoryTemplates().FirstOrDefault(); if (template == null) throw new Exception("No default template could be loaded"); return template.ViewPath; }); //activity log _customerActivityService.InsertActivity("PublicStore.ViewCategory", _localizationService.GetResource("ActivityLog.PublicStore.ViewCategory"), category.Name); return View(templateViewPath, model);