4.1. 普通模块开发

未匹配的标注

模块架构

模块文件与目录

目录 说明
Admin 后台管理功能组
Api API接口功能组
Asset/ 模块静态文件,模块安装时会被原样复制到 public/vendor/Xxx 目录中
Core/ModuleServiceProvider.php 模块核心提供者,会被自动加载
Docs
Docs/doc/ 模块帮助文档
Docs/module/ 模块说明文档
Docs/release.md 模块更新日志
Migrate 模块数据库迁移文件
ROOT/ 其他系统文件,模块安装时会被原样复制到网站根目录,文件已存在时会覆盖已有文件
ROOT/aa/bb/cc.txt 会被复制到 网站根目录/aa/bb/cc.txt
View 模块视图文件,可以通过 module::Xxx.View.xxx 调用
Web Web前台功能组
config.json 模块配置文件

模块配置文件 config.json

配置文件是一个合法的JSON,请勿在JSON中包含注释,以下为了参数含义会在JSON中包含注释

{
    // 模块唯一标示,请使用 SomeExampleName 首字母大写的驼峰命名方式
    // 如果您的模块后期需要发布到模块市场,在开发前请先创建模块,防止与他人冲突
    "name": "Demo",
    // 模块文字说明
    "title": "开发示例程序",
    // 兼容环境,可选值为 laravel5、laravel9 ,默认为 laravel5
    "env": [
        "laravel5",
        "laravel9"
    ],
    // 模块类型,可以包含多个,目前支持以下值
    // PC:        电脑版
    // Mobile:    手机H5
    // App:       手机APP
    // MiniApp:   小程序
    // WxMiniApp: 微信小程序
    // Theme:     主题模块
    // Admin:     后台管理
    // Arch:      基础功能
    "types": [
        "PC",
        "Mobile"
    ],
    // 当前模块版本号,请使用 主版本号.次版本号.修复版本号 的格式
    // 大的迭代请升级主版本号,常规次二代升级次版本号,Bug修复升级修复版本号
    "version": "1.2.0",
    // 模块依赖,支持多个
    "require": [
        // 依赖 Vendor 模块任何版本
        "Vendor",
        // 依赖 Abc 模块任何版本 
        "Abc:*",
        // 依赖 Abc 模块大于等于1.1.0的版本
        "Abc:>=1.1.0",
        // 依赖 Abc 模块大于1.1.0的版本
        "Abc:>1.1.0",
        // 依赖 Abc 模块小于等于1.1.0的版本 
        "Abc:<=1.1.0",
        // 依赖 Abc 模块小于1.1.0的版本 
        "Abc:<1.1.0",
        // 依赖 Abc 模块1.1.0的版本,其他任何版本都不匹配
        "Abc:==1.1.0"
    ],
    // 推荐模块声明,表示当前模块已适配,推荐安装的模块
    "suggest": [
        "Abc",
        "Abc:*"
    ],
    // 冲突模块声明,表示会和当前模块冲突的模块,禁止同时安装
    "conflicts": [
        "Abc",
        "Abc:*"
    ],
    // 模块依赖的 MSCore 版本,可以通过 \ModStart\ModStart::$version 获取 MSCore 版本号
    "modstartVersion": "*",
    // 模块作者
    "author": "ModStart",
    // 模块描述
    "description": "ModStart开发示例程序",
    // 模块可配置项,可在程序中通过如下方法获取配置信息
    // \ModStart\Module\ModuleManager::getModuleConfig('模块名','配置名')
    "config": {
        // 定义一个名称为 testText 的文本参数
        "testText": [
            [
                "text",
                "文字参数"
            ]
        ],
        // 定义一个名称为 testEnable 的开关
        "testEnable": [
            [
                "switch",
                "功能启用"
            ]
        ],
        // 定义一个名称为 testSelect 的下拉选项,包含两个选项
        "testSelect": [
            [
                "select",
                "下拉选择"
            ],
            [
                "options",
                {
                    "key1": "选项1",
                    "key2": "选项2"
                }
            ]
        ]
    }
}

模块帮助文档 Docs/doc/

模块帮助文档位于 Docs/doc 目录中,每个帮助文档保存为一个 *.md Markdown 文档,格式如下:

# 帮助文档标题

---

帮助文档内容

使用模块开发助手后台上传模块时,会自动解析 Docs/doc 目录中的帮助文档并上传关联到模块中。

帮助文档使用帮助文档的文件名作为唯一标识,如果有更新会自动更新发布。

模块说明文档 content.md

文档位置位于 Docs/module/content.md

模块帮助文档位于 Docs/module/content.md ,使用模块开发助手后台上传模块时,会自动更新到模块说明文档中。

模块更新日志文档 release.md

文档位于 Docs/release.md

模块格式严格按照如下,使用模块开发助手后台上传模块时,会自动更新到模块发布更新日志中。

## 1.1.0 版本发布说明

- 新增:XXX功能
- 新增:XXX功能
- 优化:XXX功能
- 修复:XXX功能

---

## 1.0.0 版本发布说明

- 新增:XXX功能
- 新增:XXX功能
- 优化:XXX功能
- 修复:XXX功能

多个版本使用 --- 分割。

模块视图文件

模块视图文件均位于 module/Xxx/View

在Web模块中引用视图

use ModStart\Module\ModuleBaseController;

class XxxController extends ModuleBaseController{
    // ...
    public function index(){
        // 使用 module/View/Xxx/pc/aaa/bbb.blade.php 视图
        return $this->view('aaa.bbb',[
            // ...
        ]);
    }
    // ...
}

在Admin模块中引用视图

use Illuminate\Routing\Controller;

class XxxController extends Controller{
    // ...
    public function index(){
        // 使用 module/View/Xxx/admin/aaa/bbb.blade.php 视图
        return view('module::Xxx.View.admin.aaa.bbb',[
            // ...
        ]);
    }
    // ...
}

静态资源文件

静态资源路径

模块中包含的静态资源文件(如图片、CSS、JS等)该如何处理,保证用户安装主题模块后可以通过链接访问?

模块安装时,静态资源会从 module/Xxx/Asset/ 复制到 public/vendor/Xxx/,因此需要将静态资源文件统一放在主题模块的 Asset 目录中。

blade 文件中引用静态资源时需要使用如下的方式

@asset('vendor/Xxx/js/test.js')

系统文件复制与覆盖

在模块中创建文件 module/Xxx/ROOT/aaa/bbb/ccc.txt, 在安装时会自动复制到 <根目录>/aaa/bbb/ccc.txt 中。

如系统本身包含了 <根目录>aaa/bbb/ccc.txt 文件,会自动将已有的文件 aaa/bbb/ccc.txt 重命名为 aaa/bbb/ccc.txt._delete_.Xxx并替换为最新的文件。

  • 其中 Xxx 是模块名称,表示是被哪个模块删除掉的
  • 在模块 Xxx 被卸载时,如果发现存在 aaa/bbb/ccc.txt._delete_.Xxx 文件,会自动复原为 aaa/bbb/ccc.txt

提示: 模块开发时,应尽量避免系统文件的复制与覆盖,系统在升级时如果该文件被修改过可能会自动覆盖为最新的文件,会带来不可预知的后果。

开发阶段静态资源软连接

系统安装后,静态资源会从 module/Xxx/Asset/ 复制到 public/vendor/Xxx/ ,开发阶段如何处理这个问题?

开发阶段创建一个从 module/Xxx/Asset/public/vendor/Xxx/ 的软连接,这样就可以通过 http://xxx/vendor/Xxx/ 访问到模块静态资源文件了。

  • Linux:运行命令 ln -s module/Xxx/Asset public/vendor/Xxx
  • Windows:手动创建快捷方式

后台导航菜单注册

Core/ModuleServiceProvider.php 中配置,通过如下方式注册菜单:

AdminMenu::register(function () {
    return [
        [
            'title' => '一级菜单',
            'icon' => 'tools',
            'sort' => 150,
            'children' => [
                [
                    'title' => '二级菜单',
                    'url' => '\Module\Xxx\Admin\Controller\XxxController@index',
                ]
            ]
        ],
        [
            'title' => '一级菜单',
            'icon' => 'tools',
            'sort' => 150,
            'children' => [
                [
                    'title' => '二级菜单',
                    'children' => [
                        [
                            'title' => '三级菜单',
                            'url' => '\Module\Xxx\Admin\Controller\XxxController@index',
                        ],
                        [
                            'title' => '三级菜单',
                            'url' => '\Module\Xxx\Admin\Controller\XxxController@index',
                        ]
                    ],
                ]
            ]
        ],
    ];
});

ModStart系统按照如下相同的规则进行菜单合并:

  • 一级菜单(title+icon+sort)
  • 二级菜单(title)

如需要隐藏某一个菜单不显示在菜单栏但出现在权限管理中,只需要给菜单项增加参数,如下

// ...
[
  'title' => '二级菜单',
  'url' => '\Module\Xxx\Admin\Controller\XxxController@index',
  // 增加该参数不会显示在菜单栏
  'hide' => true,
]
// ...

后台导航菜单使用规范

我们强烈建议您按照系统推荐的方式组织菜单避免用户安装多个模块后系统菜单变得混乱。

  • 独立的业务功能模块可以插入一级菜单,用于管理模块涉及的业务功能
  • 物料类、工具类的模块使用二级或三级菜单
  • 菜单由上至下应遵循使用频率递减的特性

系统内置了如下的菜单大类组织方式,强烈建议您遵守如下约定。

目录内容 排序(sort值) 图标(icon) 说明
用户管理 100 users 用户管理模块
|– 用户管理
|– …
物料管理 200 description 系统基础物料管理
|– 导航配置
|– 文章管理
|– 友情链接
|– …
功能设置 300 tools 模块业务功能相关的设置
|– 用户设置
|– …
系统设置 400 cog 技术相关配置
|– 基础配置
|– 短信设置
|– 支付设置
|– …
后台权限 500 user-o 管理员、角色、管理日志
|– 管理角色
|– 管理账号
|– 管理日志
运维工具 600 magic-wand 系统运维阶段功能模块
|– …
系统管理 700 code-alt 系统功能管理(通常用于开发阶段)
|– 模块管理

后台权限管理

权限校验原理

后台权限管理统一在 ModStart\Admin\Middleware\AuthMiddleware.php 管理,匹配规则如下:

  • ① 根据访问路径拼接权限标识,如 \Module\Aaa\Admin\Controller\BbbController@ccc
  • ② 如果管理员权限标识列表中包含第 ① 步拼接的权限标识,则校验权限成功,否则进行第 ③ 步
  • ③ 如果当前控制器定义了静态变量 $PermitMethodMap,对权限标识进行转换,转换表如下
    • currentMethod => checkMethod 使用 当前 Controller 的 checkMethod 检查权限
    • currentMethod => controller@method 使用 Controller@method 检查权限
    • currentMethod => @rule 使用 rule 检查权限
    • currentMethod => * 忽略匹配不到时的权限检查
    • * => * 本 Controller 的所有方法匹配不到时忽略权限检查
  • ④ 查找管理员权限标识中是否拥有权限标识,如果有则校验权限成功,否则权限校验失败

权限标识定义方法

在后台导航菜单定义时,默认会 url 作为权限校验标识

// ...
[
  'title' => '二级菜单',
  'url' => '\Module\Xxx\Admin\Controller\XxxController@index',
  // 增加隐藏菜单的参数
  'hide' => true,
]
// ...

也可自定义权限标识

// ...
[
  'title' => '二级菜单',
  'url' => '\Module\Xxx\Admin\Controller\XxxController@index',
  'rule' => 'MyCustomRule',
  'hide' => true,
]
// ...

管理员权限标识获取方法

使用了RBAC标准授权:

用户表(admin_user) ↔ 角色关联表(admin_user_role) ↔ 角色表(admin_role) ↔ 角色权限表(admin_role_rule)

用户登录后可通过如下方法获取用户角色标识列表

Session::get('_adminRules',[])

模块生命周期Hook

需要在模块的安装、启用、禁用、卸载的时机执行代码,可通过创建类 Module/Xxx/Core/ModuleHook 实现。

<?php

namespace Module\Xxx\Core;

class ModuleHook
{
    /**
     * 安装完成
     */
    public function hookInstalled() { }

    /**
     * 已启用
     */
    public function hookEnabled() { }

    /**
     * 禁用前
     */
    public function hookBeforeDisable() { }

    /**
     * 卸载前
     */
    public function hookBeforeUninstall() { }
}

模块管理与操作

模块操作相关方法都集中在 ModStart\Module\ModuleManager 类中。

常见示例调用如下:

// 列出所有安装并启用的模块
\ModStart\Module\ModuleManager::listAllEnabledModules();

// 判断模块是否安装并启用
\ModStart\Module\ModuleManager::isModuleEnabled('Xxx');
// 或
modstart_module_enabled('Xxx');

// 模块 Xxx 是否安装了 >=1.2.0 的版本
modstart_module_enabled('Member','>=1.2.0');

命令行模块管理

安装 module-install

php artisan modstart:module-install {module} {--force}

卸载 module-uninstall

php artisan modstart:module-uninstall {module}

启用 module-enable

php artisan modstart:module-enable {module}

禁用 module-disable

php artisan modstart:module-disable {module}

安装全部 module-install-all

php artisan modstart:module-install-all

一条命令安装全部模块,该命令会计算模块的依赖顺序,按照顺序依次安装。

接口文档注解

使用注解可以在模块打包时生成接口文档,一个接口文档注解示例如下

/**
 * @Api 新闻
 */
class NewsController extends Controller
{
  /**
   * @Api 新闻分页
   * @ApiBodyParam search.categoryId int 新闻分类ID
   * @ApiResponseData {
   *  "total": 1,
   *  "page" : 1,
   *  "pageSize": 10,
   *  "records": [
   *      {
   *        "id":1,
   *        "categoryId":1,
   *        "title":"标题",
   *        "summary":"摘要",
   *        "content":"内容"
   *      }
   *  ]
   * }
   */
  public function paginate()
  {
     // ...
  }
}

类注解

  • 接口分组:@Api 分组

方法注解

  • 接口名称:@Api 名称
  • 接口说明:@ApiDesc 接口说明
  • 接口请求方式:@ApiMethod post|get
  • 接口请求格式:@ApiDataType json|formData
  • 接口请求头:@ApiHeadParam api-token string required 参数说明
  • 接口请求Body参数:@ApiBodyParam bizId int required 企业ID
  • 接口请求Query参数:@ApiQueryParam bizId int required 企业ID
  • 接口返回Code特殊值:@ApiResponseCode 10000 用户未登录
  • 接口返回Data内容格式:@ApiResponseData { }

工具类注解

使用注解可以在模块打包时生成工具类使用文档,一个工具类文档注解示例如下

/**
 * Class MCms
 *
 * @Util CMS操作
 */
class TestUtil
{
  /**
   * @Util 获取栏目
   * @param $catUrl string 栏目URL
   * @return array
   */
  public static function getCatByUrl($catUrl)
  {
     // ...
  }
}

类注解

  • 工具类分组:@Util 分组

方法注解

  • 名称:@Util 名称
  • 参数:@param $name string 说明
  • 返回:@return array

模块手动第三方依赖包

模块开发的重要的原则是要保证模块所有的依赖代码都位于模块目录中 /module/Xxx。 如需要引入第三方依赖,推荐做法是在模块目录中创建 SDK/ 目录,将第三方依赖包放在该目录中,同时使用如下方法引入 namespace

第一步,创建 SDK 目录

引入两个包 package-apackage-b 为例,完成后的目录结构参考

/module/Xxx
└── SDK
    ├── package-a
    │   └── src
    └── package-b
        └── src

第二步,在使用包的地方显示引入

其中 AuthorA\PackageA 表示包A的 namespaceAuthorB\PackageB 表示包B的 namespace

\ModStart\Module\ModuleClassLoader::addNamespace('AuthorA\PackageA', __DIR__ . '/../SDK/package-a/src');
\ModStart\Module\ModuleClassLoader::addNamespace('AuthorB\PackageB', __DIR__ . '/../SDK/package-b/src');

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
讨论数量: 0
发起讨论 只看当前版本


暂无话题~