nopcommerce的新闻模块一直都没有新闻类别,但是很多情况下都会使用到类别,怎么办呢,自己动手吧。
1、首先创建新闻类别表,脚本如下:
CREATE TABLE [dbo].[NewsCategory]( [Id] [int] IDENTITY(1,1) NOT NULL, [Name] [nvarchar](400) NOT NULL, [Description] [nvarchar](max) NOT NULL, [ParentCategoryId] [int] NOT NULL, [Published] [bit] NOT NULL, [Deleted] [bit] NOT NULL, [DisplayOrder] [int] NOT NULL, [CreatedOn] [datetime] NOT NULL, [UpdatedOn] [datetime] NOT NULL, CONSTRAINT [PK_NewsCategory] PRIMARY KEY CLUSTERED ( [Id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO
2、\Libraries\Nop.Core\Domain\News下面增加实体类,代码如下:
using System; using System.Collections.Generic; using Nop.Core.Domain.Localization; namespace Nop.Core.Domain.News { /// <summary> /// Represents a news category /// </summary> public partial class NewsCategory : BaseEntity { /// <summary> /// Gets or sets the name /// </summary> public virtual string Name { get; set; } /// <summary> /// Gets or sets the description /// </summary> public virtual string Description { get; set; } /// <summary> /// Gets or sets the parent category identifier /// </summary> public virtual int ParentCategoryId { get; set; } /// <summary> /// Gets or sets a value indicating whether the entity is published /// </summary> public virtual bool Published { get; set; } /// <summary> /// Gets or sets a value indicating whether the entity has been deleted /// </summary> public virtual bool Deleted { get; set; } /// <summary> /// Gets or sets the display order /// </summary> public virtual int DisplayOrder { get; set; } /// <summary> /// Gets or sets the date and time of instance creation /// </summary> public virtual DateTime CreatedOn { get; set; } /// <summary> /// Gets or sets the date and time of instance update /// </summary> public virtual DateTime UpdatedOn { get; set; } } }
3、\Libraries\Nop.Data\Mapping\News下面增加新闻类别映射类,因为nopcommerce使用Entity Framework (EF) Code-First方法,
允许你在nopcommerce代码中定义实体 (所有的核心实体类都在Nop.Core中定义),所以必须在此添加映射类,代码如下:
using System.Data.Entity.ModelConfiguration; using Nop.Core.Domain.News; namespace Nop.Data.Mapping.News { public partial class NewsCategoryMap : EntityTypeConfiguration<NewsCategory> { public NewsCategoryMap() { this.ToTable("NewsCategory"); this.HasKey(bp => bp.Id); this.Property(bp => bp.Name).IsRequired().IsMaxLength(); this.Property(bp => bp.Description).IsRequired().IsMaxLength(); } } }
4、\Libraries\Nop.Services\News下面的INewsService.cs和NewsService.cs增加新闻类别相关操作。
INewsService.cs整理后代码如下:
using System; using System.Collections.Generic; using Nop.Core; using Nop.Core.Domain.News; namespace Nop.Services.News { /// <summary> /// News service interface /// </summary> public partial interface INewsService { #region news /// <summary> /// Deletes a news /// </summary> /// <param name="newsItem">News item</param> void DeleteNews(NewsItem newsItem); /// <summary> /// Gets a news /// </summary> /// <param name="newsId">The news identifier</param> /// <returns>News</returns> NewsItem GetNewsById(int newsId); /// <summary> /// Gets all news /// </summary> /// <param name="languageId">Language identifier; 0 if you want to get all records</param> /// <param name="dateFrom">Filter by created date; null if you want to get all records</param> /// <param name="dateTo">Filter by created date; null if you want to get all records</param> /// <param name="pageIndex">Page index</param> /// <param name="pageSize">Page size</param> /// <param name="showHidden">A value indicating whether to show hidden records</param> /// <returns>News items</returns> IPagedList<NewsItem> GetAllNews(int languageId, DateTime? dateFrom, DateTime? dateTo, int pageIndex, int pageSize, bool showHidden = false); /// <summary> /// Inserts a news item /// </summary> /// <param name="news">News item</param> void InsertNews(NewsItem news); /// <summary> /// Updates the news item /// </summary> /// <param name="news">News item</param> void UpdateNews(NewsItem news); #endregion #region news category /// <summary> /// Deletes a news category /// </summary> /// <param name="category">News Category item</param> void DeleteNewsCategory(NewsCategory category); /// <summary> /// Gets a news category /// </summary> /// <param name="categoryId">The news category identifier</param> /// <returns>NewsCategory</returns> NewsCategory GetNewsCategoryById(int categoryId); /// <summary> /// Gets all news category /// </summary> /// <param name="pageIndex">Page index</param> /// <param name="pageSize">Page size</param> /// <param name="showHidden">A value indicating whether to show hidden records</param> /// <returns>News Category items</returns> IPagedList<NewsCategory> GetAllNewsCategory(int pageIndex, int pageSize, bool showHidden = false); /// <summary> /// Gets all news category /// </summary> /// <param name="showHidden">A value indicating whether to show hidden records</param> /// <returns>News Category items</returns> IList<NewsCategory> GetAllNewsCategory(bool showHidden = false); /// <summary> /// Inserts a news category item /// </summary> /// <param name="category">News Category item</param> void InsertNewsCategory(NewsCategory category); /// <summary> /// Updates the news category item /// </summary> /// <param name="category">News Category item</param> void UpdateNewsCategory(NewsCategory category); #endregion } }
NewsService.cs整理后代码如下:
using System; using System.Collections.Generic; using System.Linq; using Nop.Core; using Nop.Core.Caching; using Nop.Core.Data; using Nop.Core.Domain.News; using Nop.Core.Events; namespace Nop.Services.News { /// <summary> /// News service /// </summary> public partial class NewsService : INewsService { #region Constants private const string NEWS_BY_ID_KEY = "Nop.news.id-{0}"; private const string NEWS_PATTERN_KEY = "Nop.news."; private const string NEWSCATEGORY_BY_ID_KEY = "Nop.newscategory.id-{0}"; private const string NEWSCATEGORY_PATTERN_KEY = "Nop.newscategory."; #endregion #region Fields private readonly IRepository<NewsItem> _newsItemRepository; private readonly IRepository<NewsCategory> _newsCategoryRepository; private readonly ICacheManager _cacheManager; private readonly IEventPublisher _eventPublisher; #endregion #region Ctor public NewsService(IRepository<NewsItem> newsItemRepository, IRepository<NewsCategory> newsCategoryRepository, ICacheManager cacheManager, IEventPublisher eventPublisher) { _newsItemRepository = newsItemRepository; _newsCategoryRepository = newsCategoryRepository; _cacheManager = cacheManager; _eventPublisher = eventPublisher; } #endregion #region Methods #region news /// <summary> /// Deletes a news /// </summary> /// <param name="newsItem">News item</param> public virtual void DeleteNews(NewsItem newsItem) { if (newsItem == null) throw new ArgumentNullException("newsItem"); _newsItemRepository.Delete(newsItem); _cacheManager.RemoveByPattern(NEWS_PATTERN_KEY); //event notification _eventPublisher.EntityDeleted(newsItem); } /// <summary> /// Gets a news /// </summary> /// <param name="newsId">The news identifier</param> /// <returns>News</returns> public virtual NewsItem GetNewsById(int newsId) { if (newsId == 0) return null; string key = string.Format(NEWS_BY_ID_KEY, newsId); return _cacheManager.Get(key, () => { var n = _newsItemRepository.GetById(newsId); return n; }); } /// <summary> /// Gets all news /// </summary> /// <param name="languageId">Language identifier; 0 if you want to get all records</param> /// <param name="dateFrom">Filter by created date; null if you want to get all records</param> /// <param name="dateTo">Filter by created date; null if you want to get all records</param> /// <param name="pageIndex">Page index</param> /// <param name="pageSize">Page size</param> /// <param name="showHidden">A value indicating whether to show hidden records</param> /// <returns>News items</returns> public virtual IPagedList<NewsItem> GetAllNews(int languageId, DateTime? dateFrom, DateTime? dateTo, int pageIndex, int pageSize, bool showHidden = false) { var query = _newsItemRepository.Table; if (dateFrom.HasValue) query = query.Where(n => dateFrom.Value <= n.CreatedOnUtc); if (dateTo.HasValue) query = query.Where(n => dateTo.Value >= n.CreatedOnUtc); if (languageId > 0) query = query.Where(n => languageId == n.LanguageId); if (!showHidden) query = query.Where(n => n.Published); query = query.OrderByDescending(b => b.CreatedOnUtc); var news = new PagedList<NewsItem>(query, pageIndex, pageSize); return news; } /// <summary> /// Inserts a news item /// </summary> /// <param name="news">News item</param> public virtual void InsertNews(NewsItem news) { if (news == null) throw new ArgumentNullException("news"); _newsItemRepository.Insert(news); _cacheManager.RemoveByPattern(NEWS_PATTERN_KEY); //event notification _eventPublisher.EntityInserted(news); } /// <summary> /// Updates the news item /// </summary> /// <param name="news">News item</param> public virtual void UpdateNews(NewsItem news) { if (news == null) throw new ArgumentNullException("news"); _newsItemRepository.Update(news); _cacheManager.RemoveByPattern(NEWS_PATTERN_KEY); //event notification _eventPublisher.EntityUpdated(news); } #endregion #region news category /// <summary> /// Deletes a news category /// </summary> /// <param name="category">News Category item</param> public virtual void DeleteNewsCategory(NewsCategory category) { if (category == null) throw new ArgumentNullException("category"); _newsCategoryRepository.Delete(category); _cacheManager.RemoveByPattern(NEWSCATEGORY_PATTERN_KEY); //event notification _eventPublisher.EntityDeleted(category); } /// <summary> /// Gets a news category /// </summary> /// <param name="categoryId">The news category identifier</param> /// <returns>NewsCategory</returns> public virtual NewsCategory GetNewsCategoryById(int categoryId) { if (categoryId == 0) return null; string key = string.Format(NEWSCATEGORY_BY_ID_KEY, categoryId); return _cacheManager.Get(key, () => { var n = _newsCategoryRepository.GetById(categoryId); return n; }); } /// <summary> /// Gets all news category /// </summary> /// <param name="pageIndex">Page index</param> /// <param name="pageSize">Page size</param> /// <param name="showHidden">A value indicating whether to show hidden records</param> /// <returns>News Category items</returns> public virtual IPagedList<NewsCategory> GetAllNewsCategory(int pageIndex, int pageSize, bool showHidden = false) { var categories = GetAllNewsCategory(showHidden); return new PagedList<NewsCategory>(categories, pageIndex, pageSize); } /// <summary> /// Gets all news category /// </summary> /// <param name="showHidden">A value indicating whether to show hidden records</param> /// <returns>News Category items</returns> public virtual IList<NewsCategory> GetAllNewsCategory(bool showHidden = false) { var query = _newsCategoryRepository.Table; if (!showHidden) query = query.Where(n => n.Published); query = query.OrderBy(b => b.DisplayOrder); var unsortedCategories = query.ToList(); //sort categories var sortedCategories = unsortedCategories.SortCategoriesForTree(0); return sortedCategories; } /// <summary> /// Inserts a news category item /// </summary> /// <param name="category">News Category item</param> public virtual void InsertNewsCategory(NewsCategory category) { if (category == null) throw new ArgumentNullException("category"); _newsCategoryRepository.Insert(category); _cacheManager.RemoveByPattern(NEWSCATEGORY_PATTERN_KEY); //event notification _eventPublisher.EntityInserted(category); } /// <summary> /// Updates the news category item /// </summary> /// <param name="category">News Category item</param> public virtual void UpdateNewsCategory(NewsCategory category) { if (category == null) throw new ArgumentNullException("category"); _newsCategoryRepository.Update(category); _cacheManager.RemoveByPattern(NEWSCATEGORY_PATTERN_KEY); //event notification _eventPublisher.EntityUpdated(category); } #endregion #endregion } }
另外需要增加NewsExtensions.cs,主要用于新闻类别的排序(对无限极分类的排序等等),代码如下:
using System; using System.Collections.Generic; using System.Linq; using Nop.Core.Domain.News; namespace Nop.Services.News { /// <summary> /// Extensions /// </summary> public static class NewsExtensions { /// <summary> /// Sort news categories for tree representation /// </summary> /// <param name="source">Source</param> /// <param name="parentId">Parent category identifier</param> /// <returns>Sorted categories</returns> public static IList<NewsCategory> SortCategoriesForTree(this IList<NewsCategory> source, int parentId) { var result = new List<NewsCategory>(); var temp = source.ToList().FindAll(c => c.ParentCategoryId == parentId); foreach (var cat in temp) { result.Add(cat); result.AddRange(SortCategoriesForTree(source, cat.Id)); } return result; } public static string GetCategoryNameWithPrefix(this NewsCategory category, INewsService newsService) { string result = string.Empty; while (category != null) { if (String.IsNullOrEmpty(result)) result = category.Name; else result = "--" + result; category = newsService.GetNewsCategoryById(category.ParentCategoryId); } return result; } public static string GetCategoryBreadCrumb(this NewsCategory category, INewsService newsService) { string result = string.Empty; while (category != null && !category.Deleted) { if (String.IsNullOrEmpty(result)) result = category.Name; else result = category.Name + " >> " + result; category = newsService.GetNewsCategoryById(category.ParentCategoryId); } return result; } } }
5、到此底层的数据操作已经基本完成,开始修改后台管理部分。
Nop.Admin\Models\News\增加新闻类别页面所需要的ViewModel,代码如下:
using System; using System.Collections.Generic; using System.Web.Mvc; using FluentValidation.Attributes; using Nop.Admin.Validators.News; using Nop.Web.Framework; using Nop.Web.Framework.Mvc; namespace Nop.Admin.Models.News { [Validator(typeof(NewsCategoryValidator))] public class NewsCategoryModel : BaseNopEntityModel { public NewsCategoryModel() { AvailableCategories = new List<SelectListItem>(); } [NopResourceDisplayName("Admin.ContentManagement.News.NewsCategoryItems.Fields.Name")] public string Name { get; set; } [NopResourceDisplayName("Admin.ContentManagement.News.NewsCategoryItems.Fields.Description")] [AllowHtml] public string Description { get; set; } [NopResourceDisplayName("Admin.ContentManagement.News.NewsCategoryItems.Fields.ParentName")] public int ParentCategoryId { get; set; } [NopResourceDisplayName("Admin.ContentManagement.News.NewsCategoryItems.Fields.ParentName")] public string ParentCategoryName { get; set; } [NopResourceDisplayName("Admin.ContentManagement.News.NewsCategoryItems.Fields.Published")] public bool Published { get; set; } [NopResourceDisplayName("Admin.ContentManagement.News.NewsCategoryItems.Fields.DisplayOrder")] public int DisplayOrder { get; set; } [NopResourceDisplayName("Admin.ContentManagement.News.NewsCategoryItems.Fields.CreatedOn")] public DateTime CreatedOn { get; set; } public IList<SelectListItem> AvailableCategories { get; set; } } }
Nop.Admin\Validators\News\增加类别的验证类NewsCategoryValidator.cs,主要用于后台添加时候的非空字数限制等等,代码如下:
using FluentValidation; using Nop.Admin.Models.News; using Nop.Services.Localization; namespace Nop.Admin.Validators.News { public class NewsCategoryValidator : AbstractValidator<NewsCategoryModel> { public NewsCategoryValidator(ILocalizationService localizationService) { RuleFor(x => x.Name) .NotNull() .WithMessage(localizationService.GetResource("Admin.ContentManagement.News.NewsCategoryItems.Fields.Name.Required")); RuleFor(x => x.Description) .NotNull() .WithMessage(localizationService.GetResource("Admin.ContentManagement.News.NewsCategoryItems.Fields.Description.Required")); } } }
修改Nop.Admin/MappingExtensions.cs文件,增加新闻类别相关内容,这几个方法主要是NewsCategory和NewsCategoryModel之间的相互转换,代码如下:
//news category items public static NewsCategoryModel ToModel(this NewsCategory entity) { return Mapper.Map<NewsCategory, NewsCategoryModel>(entity); } public static NewsCategory ToEntity(this NewsCategoryModel model) { return Mapper.Map<NewsCategoryModel, NewsCategory>(model); } public static NewsCategory ToEntity(this NewsCategoryModel model, NewsCategory destination) { return Mapper.Map(model, destination); }
修改Nop.Admin/Infrastructure/AutoMapperStartupTask.cs文件,增加相关映射规则,如果不加的话会出错的哦,
AutoMapper是基于对象到对象约定的映射工具,常用于(但并不仅限制于)把复杂的对象模型转为DTO,一般用于ViewModel模式和跨服务范畴。
AutoMapper给用户提供了便捷的配置API,就像使用约定来完成自动映射那样。
代码如下:
Mapper.CreateMap<NewsCategory, NewsCategoryModel>() .ForMember(dest => dest.AvailableCategories, mo => mo.Ignore()) .ForMember(dest => dest.ParentCategoryName, mo => mo.Ignore()) .ForMember(dest => dest.CreatedOn, mo => mo.Ignore()); Mapper.CreateMap<NewsCategoryModel, NewsCategory>() .ForMember(dest => dest.CreatedOn, mo => mo.Ignore());
最后修改Nop.Admin\Controllers\NewsController.cs,增加新闻类别的增删改查操作,如下:
#region News category items public ActionResult CategoryList() { if (!_permissionService.Authorize(StandardPermissionProvider.ManageNews)) return AccessDeniedView(); var news = _newsService.GetAllNewsCategory(0, _adminAreaSettings.GridPageSize, true); var gridModel = new GridModel<NewsCategoryModel> { Data = news.Select(x => { var m = x.ToModel(); m.CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOn, DateTimeKind.Utc); m.Name = x.GetCategoryBreadCrumb(_newsService); m.Published = x.Published; m.DisplayOrder = x.DisplayOrder; return m; }), Total = news.TotalCount }; return View(gridModel); } [HttpPost, GridAction(EnableCustomBinding = true)] public ActionResult CategoryList(GridCommand command) { if (!_permissionService.Authorize(StandardPermissionProvider.ManageNews)) return AccessDeniedView(); var news = _newsService.GetAllNewsCategory(0, _adminAreaSettings.GridPageSize, true); var gridModel = new GridModel<NewsCategoryModel> { Data = news.Select(x => { var m = x.ToModel(); m.CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOn, DateTimeKind.Utc); m.Name = x.GetCategoryBreadCrumb(_newsService); m.Published = x.Published; m.DisplayOrder = x.DisplayOrder; return m; }), Total = news.TotalCount }; return new JsonResult { Data = gridModel }; } public ActionResult CategoryCreate() { if (!_permissionService.Authorize(StandardPermissionProvider.ManageNews)) return AccessDeniedView(); var model = new NewsCategoryModel(); //default values model.Published = true; //categories model.AvailableCategories.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" }); foreach (var c in _newsService.GetAllNewsCategory(true)) model.AvailableCategories.Add(new SelectListItem() { Text = c.GetCategoryNameWithPrefix(_newsService), Value = c.Id.ToString() }); return View(model); } [HttpPost, FormValueExists("save", "save-continue", "continueEditing")] public ActionResult CategoryCreate(NewsCategoryModel model, bool continueEditing) { if (!_permissionService.Authorize(StandardPermissionProvider.ManageNews)) return AccessDeniedView(); if (ModelState.IsValid) { var newsItem = model.ToEntity(); newsItem.CreatedOn = DateTime.UtcNow; newsItem.UpdatedOn = DateTime.UtcNow; _newsService.InsertNewsCategory(newsItem); SuccessNotification(_localizationService.GetResource("Admin.ContentManagement.News.NewsCategoryItems.Added")); return continueEditing ? RedirectToAction("CategoryEdit", new { id = newsItem.Id }) : RedirectToAction("CategoryList"); } return View(model); } public ActionResult CategoryEdit(int id) { if (!_permissionService.Authorize(StandardPermissionProvider.ManageNews)) return AccessDeniedView(); var newsItem = _newsService.GetNewsCategoryById(id); if (newsItem == null) //No news item found with the specified id return RedirectToAction("CategoryList"); var model = newsItem.ToModel(); //categories model.AvailableCategories.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" }); foreach (var c in _newsService.GetAllNewsCategory(true)) model.AvailableCategories.Add(new SelectListItem() { Text = c.GetCategoryNameWithPrefix(_newsService), Value = c.Id.ToString() }); return View(model); } [HttpPost, FormValueExists("save", "save-continue", "continueEditing")] public ActionResult CategoryEdit(NewsCategoryModel model, bool continueEditing) { if (!_permissionService.Authorize(StandardPermissionProvider.ManageNews)) return AccessDeniedView(); var newsItem = _newsService.GetNewsCategoryById(model.Id); if (newsItem == null) //No news item found with the specified id return RedirectToAction("CategoryList"); if (ModelState.IsValid) { newsItem = model.ToEntity(newsItem); newsItem.UpdatedOn = DateTime.UtcNow; _newsService.UpdateNewsCategory(newsItem); SuccessNotification(_localizationService.GetResource("Admin.ContentManagement.News.NewsCategoryItems.Updated")); return continueEditing ? RedirectToAction("CategoryEdit", new { id = newsItem.Id }) : RedirectToAction("List"); } return View(model); } [HttpPost, ActionName("DeleteCategory")] public ActionResult DeleteCategoryConfirmed(int id) { if (!_permissionService.Authorize(StandardPermissionProvider.ManageNews)) return AccessDeniedView(); var newsItem = _newsService.GetNewsCategoryById(id); if (newsItem == null) //No news item found with the specified id return RedirectToAction("CategoryList"); _newsService.DeleteNewsCategory(newsItem); SuccessNotification(_localizationService.GetResource("Admin.ContentManagement.News.NewsCategoryItems.Deleted")); return RedirectToAction("CategoryList"); } #endregion
6、接下来就是页面View了,代码太多,这里不再具体列出,不熟悉MVC机制的可以先补充一下MVC相关知识。
稍后整理提供下载。
分享是一种美。版权所有,转载请注明出处 http://www.nopchina.net/