NopCommerce源码架构详解-插件机制相关源码分析3

前面两篇文章写了NopCommerce插件机制的底层实现原理,现在我来举一个具体的插件例子,分析一下nop插件的文件结构及关键代码。

NopCommerce源码架构详解概述

文章目录

概述

NopCommerce源码架构详解-插件机制相关源码分析3。

内容

前面两篇文章写了NopCommerce插件机制的底层实现原理,现在我来举一个具体的插件例子,分析一下nop插件的文件结构及关键代码。研究过NopCommerce源码的同学如果细心都会发现,Nop前台首页的幻片图片其实是调用的一个插件。下面我们就来分析这个幻灯插件Nop.Plugin.Widgets.NivoSlider是如何实现的。

首先,我们看看这个NivoSlider插件的文件结构。

2020-04-23-02-49-59

从上图就可以看到Nop自带的插件它其实是就一个完整的mvc项目,也有MVC相应的文件结构。我来看看个项目的属性中的生成设置是什么?

2020-04-23-02-50-10

可以从上图中插件的生成目录是Nop.Web的Plugins目录下,也就是所有的插件都在Web项目的里,一个插件一个文件夹,如下图:

2020-04-23-02-50-18

请请注意每个插件项目都有两个类名分别是以Plugin和Settings结尾的类。比如:插件Nop.Plugin.Widgets.NivoSlider有下面的两个类:

1、NivoSliderPlugin

2、NivoSliderSettings

Nop.Plugin.Widgets.GoogleAnalytics插件也有两个类:

1、GoogleAnalyticPlugin

2、GoogleAnalyticsSettings

其实,聪明的你看这个两个类的命名就知道他们的作用。

Plugin是插件的核心类,它继承于基类BasePlugin和实现接口IWidgetPlugin,作用是插件最终要执行的Controller与Action的路由配置信息、插件安装,卸载以及插件调用的区域名字(GetWidgetZones)等等。

Settings就是插件本身的一些基本设置信息。

在Home/Index.cshtml中有下面代码:

@Html.Widget("home_page_top")

@Html.Widget其实是调用的HtmlHelper的一个扩展方法。

public static MvcHtmlString Widget(this HtmlHelper helper, string widgetZone, object additionalData = null)
{
    return helper.Action("WidgetsByZone", "Widget", new { widgetZone = widgetZone, additionalData = additionalData });
}

从上面可以看到它最终是调用的Controller为Widget的一个Action方法WidgetsByZone。下面我们就来看这个方法为WidgetsByZone到底做了些什么?

Nop.Web.Controllers.WidgetController : BasePublicController

[ChildActionOnly]
public ActionResult WidgetsByZone(string widgetZone, object additionalData = null)
{
    var cacheKey = string.Format(ModelCacheEventConsumer.WIDGET_MODEL_KEY, _storeContext.CurrentStore.Id, widgetZone);
    var cacheModel = _cacheManager.Get(cacheKey, () =>
    {
        //model
        var model = new List<RenderWidgetModel>();

        var widgets = _widgetService.LoadActiveWidgetsByWidgetZone(widgetZone, _storeContext.CurrentStore.Id);
        foreach (var widget in widgets)
        {
            var widgetModel = new RenderWidgetModel();

            string actionName;
            string controllerName;
            RouteValueDictionary routeValues;
            widget.GetDisplayWidgetRoute(widgetZone, out actionName, out controllerName, out routeValues);
            widgetModel.ActionName = actionName;
            widgetModel.ControllerName = controllerName;
            widgetModel.RouteValues = routeValues;

            model.Add(widgetModel);
        }
        return model;
    });

    //如果没有Model返回为空字符串
    if (cacheModel.Count == 0)
        return Content("");

    //"RouteValues" property of widget models depends on "additionalData".
    //We need to clone the cached model before modifications (the updated one should not be cached)
    var clonedModel = new List<RenderWidgetModel>();
    foreach (var widgetModel in cacheModel)
    {
        var clonedWidgetModel = new RenderWidgetModel();
        clonedWidgetModel.ActionName = widgetModel.ActionName;
        clonedWidgetModel.ControllerName = widgetModel.ControllerName;
        if (widgetModel.RouteValues != null)
            clonedWidgetModel.RouteValues = new RouteValueDictionary(widgetModel.RouteValues);

        if (additionalData != null)
        {
            if (clonedWidgetModel.RouteValues == null)
                clonedWidgetModel.RouteValues = new RouteValueDictionary();
            clonedWidgetModel.RouteValues.Add("additionalData", additionalData);
        }

        clonedModel.Add(clonedWidgetModel);
    }

    return PartialView(clonedModel);
}

上面的_cacheManager.Get是用到了缓存,你可以先不用关心这一块,以后我会专门用一篇文章到介绍Nop中的缓存实现。今天我们只关心_cacheManager.Get里面的lambda方法体。里面有一句代码:

 var widgets = _widgetService.LoadActiveWidgetsByWidgetZone(widgetZone, _storeContext.CurrentStore.Id);

上面是调用了widgetService的方法LoadActiveWidgetsByWidgetZone来获取满足的WidgetPlugin集合。下面我们来看看WidgetService有些什么?

Nop.Services.Cms
WidgetService : IWidgetService

/// <summary>
/// Load active widgets
/// </summary>
/// <param name="widgetZone">Widget zone</param>
/// <param name="storeId">Load records allowed only in a specified store; pass 0 to load all records</param>
/// <returns>Widgets</returns>
public virtual IList<IWidgetPlugin> LoadActiveWidgetsByWidgetZone(string  widgetZone, int storeId = 0)
{
    if (String.IsNullOrWhiteSpace(widgetZone))
        return new List<IWidgetPlugin>();
    //通过GetWidgetZones方法返回的集合与widgetZone做比较
    return LoadActiveWidgets(storeId)
           .Where(x => x.GetWidgetZones().Contains(widgetZone, StringComparer.InvariantCultureIgnoreCase))
           .ToList();
}

/// <summary>
/// Load active widgets
/// </summary>
/// <param name="storeId">Load records allowed only in a specified store; pass 0 to load all records</param>
/// <returns>Widgets</returns>
public virtual IList<IWidgetPlugin> LoadActiveWidgets(int storeId = 0)
{
    return LoadAllWidgets(storeId)
           .Where(x => _widgetSettings.ActiveWidgetSystemNames.Contains(x.PluginDescriptor.SystemName, StringComparer.InvariantCultureIgnoreCase))
           .ToList();
}

/// <summary>
/// Load all widgets
/// </summary>
/// <param name="storeId">Load records allowed only in a specified store; pass 0 to load all records</param>
/// <returns>Widgets</returns>
public virtual IList<IWidgetPlugin> LoadAllWidgets(int storeId = 0)
{
    return _pluginFinder.GetPlugins<IWidgetPlugin>(storeId: storeId).ToList();
}

最后,我们来看看该插件的核心类NivoSlider:

Nop.Plugin.Widgets.NivoSlider
NivoSliderPlugin : BasePlugin, IWidgetPlugin
using System.Collections.Generic;
using System.IO;
using System.Web.Routing;
using Nop.Core;
using Nop.Core.Plugins;
using Nop.Services.Cms;
using Nop.Services.Configuration;
using Nop.Services.Localization;
using Nop.Services.Media;

namespace Nop.Plugin.Widgets.NivoSlider
{
    /// <summary>
    /// PLugin
    /// </summary>
    public class NivoSliderPlugin : BasePlugin, IWidgetPlugin
    {
        private readonly IPictureService _pictureService;
        private readonly ISettingService _settingService;
        private readonly IWebHelper _webHelper;

        public NivoSliderPlugin(IPictureService pictureService, 
            ISettingService settingService, IWebHelper webHelper)
        {
            this._pictureService = pictureService;
            this._settingService = settingService;
            this._webHelper = webHelper;
        }

        /// <summary>
        /// 获取该插件的所有区域名称,到时可以通过Html.Widget来调用
        /// </summary>
        /// <returns>Widget zones</returns>
        public IList<string> GetWidgetZones()
        {
            return new List<string>() { "home_page_top" };
        }

        /// <summary>
        /// 获取该插件的路由配置信息
        /// </summary>

        public void GetConfigurationRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues)
        {
            actionName = "Configure";
            controllerName = "WidgetsNivoSlider";
            routeValues = new RouteValueDictionary() { { "Namespaces", "Nop.Plugin.Widgets.NivoSlider.Controllers" }, { "area", null } };
        }

        /// <summary>
        /// 获取要显示的指定区域的路由信息
        /// </summary>
        /// <param name="widgetZone">Widget zone where it's displayed</param>
        /// <param name="actionName">Action name</param>
        /// <param name="controllerName">Controller name</param>
        /// <param name="routeValues">Route values</param>
        public void GetDisplayWidgetRoute(string widgetZone, out string actionName, out string controllerName, out RouteValueDictionary routeValues)
        {
            actionName = "PublicInfo";
            controllerName = "WidgetsNivoSlider";
            routeValues = new RouteValueDictionary()
            {
                {"Namespaces", "Nop.Plugin.Widgets.NivoSlider.Controllers"},
                {"area", null},
                {"widgetZone", widgetZone}
            };
        }

        /// <summary>
        /// 安装插件
        /// </summary>
        public override void Install()
        {
            //图片信息
            var sampleImagesPath = _webHelper.MapPath("~/Plugins/Widgets.NivoSlider/Content/nivoslider/sample-images/");

            //插件相关的配置信息
            var settings = new NivoSliderSettings()
            {
                Picture1Id = _pictureService.InsertPicture(File.ReadAllBytes(sampleImagesPath + "banner1.jpg"), "image/pjpeg", "banner_1", true).Id,
                Text1 = "",
                Link1 = _webHelper.GetStoreLocation(false),
                Picture2Id = _pictureService.InsertPicture(File.ReadAllBytes(sampleImagesPath + "banner2.jpg"), "image/pjpeg", "banner_2", true).Id,
                Text2 = "",
                Link2 = _webHelper.GetStoreLocation(false),
                Picture3Id = _pictureService.InsertPicture(File.ReadAllBytes(sampleImagesPath + "banner3.jpg"), "image/pjpeg", "banner_3", true).Id,
                Text3 = "",
                Link3 = _webHelper.GetStoreLocation(false),
                //Picture4Id = _pictureService.InsertPicture(File.ReadAllBytes(sampleImagesPath + "banner4.jpg"), "image/pjpeg", "banner_4", true).Id,
                //Text4 = "",
                //Link4 = _webHelper.GetStoreLocation(false),
            };
            _settingService.SaveSetting(settings);

            this.AddOrUpdatePluginLocaleResource("Plugins.Widgets.NivoSlider.Picture1", "Picture 1");
            this.AddOrUpdatePluginLocaleResource("Plugins.Widgets.NivoSlider.Picture2", "Picture 2");
            this.AddOrUpdatePluginLocaleResource("Plugins.Widgets.NivoSlider.Picture3", "Picture 3");
            this.AddOrUpdatePluginLocaleResource("Plugins.Widgets.NivoSlider.Picture4", "Picture 4");
            this.AddOrUpdatePluginLocaleResource("Plugins.Widgets.NivoSlider.Picture5", "Picture 5");
            this.AddOrUpdatePluginLocaleResource("Plugins.Widgets.NivoSlider.Picture", "Picture");
            this.AddOrUpdatePluginLocaleResource("Plugins.Widgets.NivoSlider.Picture.Hint", "Upload picture.");
            this.AddOrUpdatePluginLocaleResource("Plugins.Widgets.NivoSlider.Text", "Comment");
            this.AddOrUpdatePluginLocaleResource("Plugins.Widgets.NivoSlider.Text.Hint", "Enter comment for picture. Leave empty if you don't want to display any text.");
            this.AddOrUpdatePluginLocaleResource("Plugins.Widgets.NivoSlider.Link", "URL");
            this.AddOrUpdatePluginLocaleResource("Plugins.Widgets.NivoSlider.Link.Hint", "Enter URL. Leave empty if you don't want this picture to be clickable.");

            base.Install();
        }

        /// <summary>
        /// 卸载插件
        /// </summary>
        public override void Uninstall()
        {
            //settings
            _settingService.DeleteSetting<NivoSliderSettings>();

            //locales
            this.DeletePluginLocaleResource("Plugins.Widgets.NivoSlider.Picture1");
            this.DeletePluginLocaleResource("Plugins.Widgets.NivoSlider.Picture2");
            this.DeletePluginLocaleResource("Plugins.Widgets.NivoSlider.Picture3");
            this.DeletePluginLocaleResource("Plugins.Widgets.NivoSlider.Picture4");
            this.DeletePluginLocaleResource("Plugins.Widgets.NivoSlider.Picture5");
            this.DeletePluginLocaleResource("Plugins.Widgets.NivoSlider.Picture");
            this.DeletePluginLocaleResource("Plugins.Widgets.NivoSlider.Picture.Hint");
            this.DeletePluginLocaleResource("Plugins.Widgets.NivoSlider.Text");
            this.DeletePluginLocaleResource("Plugins.Widgets.NivoSlider.Text.Hint");
            this.DeletePluginLocaleResource("Plugins.Widgets.NivoSlider.Link");
            this.DeletePluginLocaleResource("Plugins.Widgets.NivoSlider.Link.Hint");

            base.Uninstall();
        }
    }
}

上面纯代码看起来可以有点枯燥,下图是我根据理解画的上面逻辑的UML序列图。

2020-04-23-02-51-04
原文出处:蓝狐软件工作室【蓝狐】

原文链接:http://m.lanhusoft.com/Article/349.html

本文观点不代表 .Net中文网 立场,转载请联系原作者。

发表评论

登录后才能评论