1.首先在项目解决方案中新建一个类库,类库放在项目根目录\Plugins文件夹下。
插件项目推荐的命名方式”Nop.Plugin.{Group}.{Name}”,{Group}是你的插件分组名称,{Name}是你的插件名称。例如:Nop.Plugin.Payments.PayPalStandard。这也不是必须的,你可以自己选择任何名称来命名你的插件。
2.当插件项目建立完成后,你需要修改项目的输出路径。设置成"..\..\Presentation\Nop.Web\Plugins\{Group}.{Name}\"
3.下一步创建一个Description.txt文件,这是每个插件必须的文件。它包含插件的描述元数据。你可以从别的插件项目中复制一个过来,然后修改一下。
Group: Payment methods
FriendlyName: Credit Card
SystemName: Payments.AuthorizeNet
Version: 1.00
SupportedVersions: 2.30
Author: nopCommerce team
DisplayOrder: 1
FileName: Nop.Plugin.Payments.AuthorizeNet.dll
SystemName字段必须是唯一的,Version是插件的版本号,SupportedVersions是支持的nopcommerce版本号,FileName是插件的程序集文件名称,确保“复制到输出目录”属性设置为“复制如果更新”。
1.创建一个新的类库项目”Nop.Plugin.Other.ProductViewTracker”.
2.添加相关的文件夹和description.txt文件。
3.description.txt你可以参考下面的图片
4.添加相关引用并设置它们的复制本地为False
5.在domain命名空间下建立一个公共类 TrackingRecord,继承BaseEntity.注意一点,所有的属性都是virtual,virtual属性为需要对数据库实体的实体框架如何实例化和跟踪类。
1 using Nop.Core; 2 namespace Nop.Plugin.Other.ProductViewTracker.Domain 3 { 4 public partial class TrackingRecord : BaseEntity 5 { 6 7 public virtual int ProductId { get; set; } 8 9 public virtual string ProductName { get; set; } 10 11 public virtual int CustomerId { get; set; } 12 13 public virtual string IpAddress { get; set; } 14 15 public virtual bool IsRegistered { get; set; } 16 17 } 18 }
6.下一个类创建的是实体框架映射类。在映射类中,我们映射列、表关系和数据库表。在Data创建TrackingRecordMap类
using Nop.Data.Mapping; using Nop.Plugin.Other.ProductViewTracker.Domain; namespace Nop.Plugin.Other.ProductViewTracker.Data { public partial class TrackingRecordMap : NopEntityTypeConfiguration<TrackingRecord> { public TrackingRecordMap() { this.ToTable("ProductViewTracking"); this.HasKey(x => x.Id); this.Property(x => x.ProductId); this.Property(m => m.ProductName).HasMaxLength(400); this.Property(m => m.IpAddress); this.Property(m => m.CustomerId); this.Property(m => m.IsRegistered); } } }
7.下一个类是数据访问层中最复杂和最重要的一个类。实体框架对象上下文是一个通过类的传递给我们的数据库访问,并帮助跟踪实体状态(如添加,更新,删除)。上下文也被用来生成数据库模式或更新现有架构。在自定义上下文类中,我们不能引用以前存在的实体,因为这些类型已经关联到另一个对象上下文中。
这也就是为什么我们在跟踪记录中没有复杂的关系属性。
using Nop.Core;using Nop.Data;using System.Data.Entity;using System.Data.Entity.Infrastructure;using System; namespace Nop.Plugin.Other.ProductViewTracker.Data { public class TrackingRecordObjectContext :DbContext,IDbContext { public bool ProxyCreationEnabled { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } public bool AutoDetectChangesEnabled { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } public TrackingRecordObjectContext(string nameOrConnectionString) : base(nameOrConnectionString) { } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Configurations.Add(new TrackingRecordMap()); base.OnModelCreating(modelBuilder); } public string CreateDatabaseInstallationScript() { return ((IObjectContextAdapter)this).ObjectContext.CreateDatabaseScript(); } public void Install() { //It's required to set initializer to null (for SQL Server Compact). //otherwise, you'll get something like "The model backing the 'your context name' context has changed since the database was created. Consider using Code First Migrations to update the database" Database.SetInitializer<TrackingRecordObjectContext>(null); Database.ExecuteSqlCommand(CreateDatabaseInstallationScript()); SaveChanges(); } public void Uninstall() { var dbScript = "DROP TABLE ProductViewTracking"; Database.ExecuteSqlCommand(dbScript); SaveChanges(); } public new IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity { return base.Set<TEntity>(); } public System.Collections.Generic.IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters) where TEntity : BaseEntity, new() { throw new System.NotImplementedException(); } public System.Collections.Generic.IEnumerable<TElement> SqlQuery<TElement>(string sql, params object[] parameters) { throw new System.NotImplementedException(); } public int ExecuteSqlCommand(string sql, bool doNotEnsureTransaction = false, int? timeout = null, params object[] parameters) { throw new System.NotImplementedException(); } public void Detach(object entity) { throw new NotImplementedException(); } } }
8.服务层连接数据访问层和表现层,该服务层将数据层与业务逻辑层包裹,并且表示层依赖于服务层。因为我们的任务很小,我们的服务层只是与仓库沟通(在nopCommerce行为库facade对象上下文)。
接口服务:
using Nop.Core;using Nop.Plugin.Other.ProductViewTracker.Domain; namespace Nop.Plugin.Other.ProductViewTracker.Services { public partial interface IViewTrackingService { /// <summary> /// Logs the specified record. /// </summary> /// <param name="record">The record.</param> void Log(TrackingRecord record); } }
服务:
using Nop.Core.Data; using Nop.Plugin.Other.ProductViewTracker.Domain; namespace Nop.Plugin.Other.ProductViewTracker.Services { public partial class ViewTrackingService : IViewTrackingService { private readonly IRepository<TrackingRecord> _trackingRecordRepository; public ViewTrackingService(IRepository<TrackingRecord> trackingRecordRepository) { _trackingRecordRepository = trackingRecordRepository; } /// <summary> /// 记录指定的记录 /// </summary> /// <param name="record">记录</param> public void Log(TrackingRecord record) { _trackingRecordRepository.Insert(record); } } }
9.依赖注入,新建DependcyRegisterar.cs
using Autofac; using Autofac.Core; using Nop.Core.Configuration; using Nop.Core.Data; using Nop.Core.Infrastructure; using Nop.Core.Infrastructure.DependencyManagement; using Nop.Data; using Nop.Plugin.Other.ProductViewTracker.Data; using Nop.Plugin.Other.ProductViewTracker.Domain; using Nop.Plugin.Other.ProductViewTracker.Services; using Nop.Web.Framework.Mvc; namespace Nop.Plugin.Other.ProductViewTracker { /// <summary> /// Dependency registrar /// </summary> public class DependencyRegistrar : IDependencyRegistrar { private const string CONTEXT_NAME = "nop_object_context_product_view_tracker"; /// <summary> /// Register services and interfaces /// </summary> /// <param name="builder">Container builder</param> /// <param name="typeFinder">Type finder</param> /// <param name="config">Config</param> public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder, NopConfig config) { builder.RegisterType<ViewTrackingService>().As<IViewTrackingService>().InstancePerLifetimeScope(); //data context this.RegisterPluginDataContext<TrackingRecordObjectContext>(builder, CONTEXT_NAME); //override required repository with our custom context builder.RegisterType<EfRepository<TrackingRecord>>() .As<IRepository<TrackingRecord>>() .WithParameter(ResolvedParameter.ForNamed<IDbContext>(CONTEXT_NAME)) .InstancePerLifetimeScope(); } /// <summary> /// Order of this dependency registrar implementation /// </summary> public int Order { get { return 1; } } } }
10.控制器,在Controllers文件夹下添加TrackingController.cs
using Nop.Core; using Nop.Plugin.Other.ProductViewTracker.Domain; using Nop.Plugin.Other.ProductViewTracker.Services; using Nop.Web.Framework.Controllers; using Nop.Services.Catalog; using Nop.Core.Plugins; using System.Web.Mvc; using Nop.Core.Domain.Catalog; using Nop.Core.Domain.Customers; namespace Nop.Plugin.Other.ProductViewTracker.Controllers { public class TrackingController : BasePluginController { private readonly IProductService _productService; private readonly IViewTrackingService _viewTrackingService; private readonly IWorkContext _workContext; public TrackingController(IWorkContext workContext, IViewTrackingService viewTrackingService, IProductService productService, IPluginFinder pluginFinder) { _workContext = workContext; _viewTrackingService = viewTrackingService; _productService = productService; } [ChildActionOnly] public ActionResult Index(int productId) { //Read from the product service Product productById = _productService.GetProductById(productId); //If the product exists we will log it if (productById != null) { //Setup the product to save var record = new TrackingRecord(); record.ProductId = productId; record.ProductName = productById.Name; record.CustomerId = _workContext.CurrentCustomer.Id; record.IpAddress = _workContext.CurrentCustomer.LastIpAddress; record.IsRegistered = _workContext.CurrentCustomer.IsRegistered(); //Map the values we're interested in to our new entity _viewTrackingService.Log(record); } //Return the view, it doesn't need a model return Content(""); } } }
11.路由,添加RouteProvider.cs
using System.Web.Mvc; using System.Web.Routing; using Nop.Web.Framework.Mvc.Routes; namespace Nop.Plugin.Shipping.ByWeight { public partial class RouteProvider : IRouteProvider { public void RegisterRoutes(RouteCollection routes) { routes.MapRoute("Plugin.Other.ProductViewTracker.Log", "tracking/productviews/{productId}", new { controller = "Tracking", action = "Index" } ); } public int Priority { get { return 0; } } } }
12.插件安装程序 ,添加ProductViewTrackerPlugin.cs
using Nop.Core.Plugins; using Nop.Plugin.Other.ProductViewTracker.Data; namespace Nop.Plugin.Other.ProductViewTracker { public class ProductViewTrackerPlugin:BasePlugin { private readonly TrackingRecordObjectContext _context; public ProductViewTrackerPlugin(TrackingRecordObjectContext context) { _context = context; } public override void Install() { _context.Install(); base.Install(); } public override void Uninstall() { _context.Uninstall(); base.Uninstall(); } } }
13.使用,跟踪代码应该被添加到producttemplate.simple.cshtml和producttemplate.grouped.cshtml文件。
@Html.Action(“Index”,”Tracking”,new {productId=Model.Id})