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 协议》,转载必须注明作者和本文链接
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
讨论数量: 1

这头像我以为是超哥

1个月前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!