Laravel Modules 使用数据库进行模块管理以及 Filement Modules 自定义模块管理器

Laravel Modules 使用数据库进行模块管理

Laravel Modules 默认是使用文件系统进行进行模块管理的,如果本地开发是没有问题的,但是如果放到线上,由于模块信息默认是在项目根目录的,那么就要把根目录的这个文件进行权限配置。背离了我们只放开 public/storage 目录权限的目标(这里描述的的可能不准确),所以下面只有两个选择

  1. 把模块信息文件放到 public 或者 storage 下。
  2. 采用数据库进行模块管理

在这里我选择使用数据库进行模块管理的操作,创建 model 和迁移文件 php artisan make:model ModuleActivator -m
编辑迁移文件

<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. */ public function up(): void { Schema::create('module_activators', function (Blueprint $table) { $table->id(); $table->string('module_name')->unique(); $table->boolean('module_status'); $table->timestamps(); }); } /** * Reverse the migrations. */ public function down(): void { Schema::dropIfExists('module_activators'); } };

增加两个字段 module_namemodule_status 分别表示模块名称和模块状态
执行迁移 php artisan migrate
接下来修改配置文件modules.php 如果没有文件,记得 publish 一下就可以了

'activators' => [ 'file' => [ 'class' => FileActivator::class, 'statuses-file' => base_path('modules_statuses.json'), ], 'database' => [ 'class' => DatabaseActivator::class, 'model' => ModuleActivator::class, ] ], 'activator' => 'database',

activators 增加 database 配置,activator 修改为 database
database 配置中的 model 就是刚才创建的 ModuleActivator 模型。
database 配置中的 classDatabaseActivator 数据库管理器,我们自己创建,代码如下

<?php namespace App\Activators; use Nwidart\Modules\Contracts\ActivatorInterface; use Nwidart\Modules\Module; use Illuminate\Container\Container; use Illuminate\Database\Eloquent\Model; class DatabaseActivator implements ActivatorInterface { /** * 模块管理的模型 * @var class-string<Model> $modelClass */ private string $modelClass; public function __construct(Container $app) { $this->modelClass = $app['config']['modules.activators.database.model']; } /** * Enables a module */ public function enable(Module $module): void { $this->setActiveByName($module->getName(), true); } /** * Disables a module */ public function disable(Module $module): void { $this->setActiveByName($module->getName(), false); } /** * Determine whether the given status same with a module status. */ public function hasStatus(Module|string $module, bool $status): bool { $name = $module instanceof Module ? $module->getName() : $module; $moduleRecord = $this->modelClass::where('module_name', $name)->first(); if ($moduleRecord) { return boolval($moduleRecord['module_status']) === $status; } else { return $status === false; } } /** * Set active state for a module. */ public function setActive(Module $module, bool $active): void { $this->setActiveByName($module->getName(), $active); } /** * Sets a module status by its name */ public function setActiveByName(string $name, bool $active): void { $this->modelClass::upsert(['module_name' => $name, 'module_status' => $active], ['module_name'], ['module_status']); } /** * Deletes a module activation status */ public function delete(Module $module): void { $moduleRecord = $this->modelClass::where('module_name', $module->getName())->first(); if ($moduleRecord) { $moduleRecord->delete(); } } /** * Deletes any module activation statuses created by this class. */ public function reset(): void { $this->modelClass::truncate(); } }

database 配置中的 model, 就是为了可以更换为你自己的模块状态管理模型。
这个类实现了 ActivatorInterface 接口。

这样模块的激活状态就会存储在数据库中了。可以使用 laravel modulesartisan 命令来测试模块的激活状态。

filament-module 自动注册模块,只激活启用模块中的 Plugin

filament-modulesfilament 中的一个插件,可以搭配 laravel modules 来进行开发,每一个模块都是一个 filament 插件。方便我们模块化的组织系统。

但是默认的配置filament-modules.auto-register-plugins 存在一个问题,如果打开这个配置,那么所有的模块中的 Plugin 都会被注册到 filament 中,即使这个模块没有被激活。
这显然不是我们想要的,我们只希望激活的模块中的 Plugin 被注册到 filament 中。
但是如果把这个配置设置为 false 又得自己手动注册管理,会更麻烦。
所以我们需要一个自定义的模块管理器,来管理模块的激活状态,并且根据模块的激活状态来注册 Plugin

<?php namespace App\Plugin; use Coolsam\Modules\Facades\FilamentModules; use Coolsam\Modules\ModulesPlugin as BaseModulesPlugin; use Nwidart\Modules\Facades\Module; class ModulesPlugin extends BaseModulesPlugin { protected function getModulePlugins(): array { if (! config('filament-modules.auto-register-plugins', false)) { return []; } $enabledModules = Module::allEnabled(); $pluginPaths = []; foreach ($enabledModules as $module) { $path = $module->getPath() . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'Filament' . DIRECTORY_SEPARATOR . '*Plugin.php'; $modulePluginPaths = glob($path); if(is_array($modulePluginPaths)) { $pluginPaths = array_merge($pluginPaths, $modulePluginPaths); } } return collect($pluginPaths) ->map(fn ($path) => FilamentModules::convertPathToNamespace($path)) ->filter()->toArray(); // return collect($enabledModules)->map(function ($module) { // $path = $module->getPath() . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'Filament' . DIRECTORY_SEPARATOR . $module->getName() . 'Plugin.php'; // if(file_exists($path)) { // return FilamentModules::convertPathToNamespace($path); // } // return null; // })->filter()->toArray(); } }

注意我注释的部分,一般我个人都会使用我注释部分的代码。因为我的规划是每个模块都只有一个插件,且插件名跟模块名称统一(这样略显死板)。这样注释部分的速度会更快。未注释的部分,单纯是为了防止模块有多个插件的情况。喜欢用哪个就自己开放关闭就可以了。
这个类继承了 use Coolsam\Modules\ModulesPlugin ,并重写了 getPlugins 方法。这个方法用于获取所有激活模块的插件类。

这样修改PanelProvider 中的 plugin 方法 ->plugin(ModulesPlugin::make()), 参数设定为上面那个自定义类就可以了,这样就可以自动注册已激活模块的插件了。

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 1