Skip to content
全部文档

模块化架构(DDD)

简介

用 Filament 构建大型应用时,你可能希望按领域驱动设计(DDD)组织代码,将应用拆成自包含模块。本指南说明如何将 Filament 与 InterNACHI/Modular 等模块化架构包集成。

模块化方式

在模块化架构中,应用的每个领域都是独立的 Composer 包,通常放在 app-modules/ 目录下。每个模块包含自己的:

  • 模型与业务逻辑
  • Filament resources、pages 与 widgets
  • Service provider
  • 路由、视图与配置
  • 测试

这种方式有多项好处:

  • 领域之间职责清晰分离
  • 团队协作更容易(不同团队可负责不同模块)
  • 更好的可测试性与可维护性
  • 可在多个项目间复用模块

设置 InterNACHI/Modular

首先安装 modular 包:

bash
composer require internachi/modular

创建新模块:

bash
php artisan make:module alerts

这会搭建如下模块结构:

.
+-- app-modules
|   +-- alerts
|   |   +-- composer.json
|   |   +-- src
|   |   |   +-- Providers
|   |   |   |   +-- AlertsServiceProvider.php
|   |   +-- routes
|   |   +-- resources
|   |   +-- database
|   |   +-- tests

配置模块的 composer.json

每个模块应依赖 filament/filament 并定义自己的 service provider:

json
{
    "name": "my-app/alerts",
    "type": "library",
    "require": {
        "filament/filament": "^5.0"
    },
    "autoload": {
        "psr-4": {
            "Modules\\Alerts\\": "src/"
        }
    },
    "extra": {
        "laravel": {
            "providers": [
                "Modules\\Alerts\\Providers\\AlertsServiceProvider"
            ]
        }
    }
}

为模块创建 Filament 插件

每个模块应定义自己的 Filament 插件,用于注册其 resources、pages 与 widgets:

php
namespace Modules\Alerts;

use Filament\Contracts\Plugin;
use Filament\Panel;

class AlertsPlugin implements Plugin
{
    public function getId(): string
    {
        return 'alerts';
    }

    public static function make(): static
    {
        return app(static::class);
    }

    public function register(Panel $panel): void
    {
        $panel
            ->discoverResources(
                in: __DIR__ . '/Filament/Resources',
                for: 'Modules\\Alerts\\Filament\\Resources',
            )
            ->discoverPages(
                in: __DIR__ . '/Filament/Pages',
                for: 'Modules\\Alerts\\Filament\\Pages',
            )
            ->discoverWidgets(
                in: __DIR__ . '/Filament/Widgets',
                for: 'Modules\\Alerts\\Filament\\Widgets',
            );
    }

    public function boot(Panel $panel): void
    {
        //
    }
}

按面板有条件地注册插件

当你有多个面板(如 adminappportal)时,通常希望某些模块只对特定面板注册插件。可在模块的 service provider 中使用 Panel::configureUsing() 有条件地注册插件。

基础条件注册

要为除某一个之外的所有面板注册插件:

php
namespace Modules\Alerts\Providers;

use Filament\Panel;
use Illuminate\Support\ServiceProvider;
use Modules\Alerts\AlertsPlugin;

class AlertsServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        Panel::configureUsing(function (Panel $panel): void {
            if ($panel->getId() !== 'admin') {
                return;
            }

            $panel->plugin(AlertsPlugin::make());
        });
    }
}

用 match 语句处理多个面板

若需为特定面板注册插件,或按面板做不同配置,可使用直接调用 $panel->plugin()match 语句:

php
namespace Modules\Alerts\Providers;

use Filament\Panel;
use Illuminate\Support\ServiceProvider;
use Modules\Alerts\AlertsPlugin;

class AlertsServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        Panel::configureUsing(function (Panel $panel): void {
            match ($panel->getId()) {
                'admin' => $panel->plugin(
                    AlertsPlugin::make()->enableAdminFeatures(),
                ),
                'staff' => $panel->plugin(
                    AlertsPlugin::make(),
                ),
                default => null,
            };
        });
    }
}

这样可按面板为每个插件实例做不同配置;未匹配到的面板则不会收到该插件。

模块目录结构

组织良好、已集成 Filament 的模块可能如下所示:

.
+-- app-modules
|   +-- alerts
|   |   +-- composer.json
|   |   +-- config
|   |   |   +-- alerts.php
|   |   +-- database
|   |   |   +-- factories
|   |   |   +-- migrations
|   |   |   +-- seeders
|   |   +-- resources
|   |   |   +-- views
|   |   |   |   +-- filament
|   |   |   |   |   +-- pages
|   |   +-- routes
|   |   |   +-- web.php
|   |   +-- src
|   |   |   +-- AlertsPlugin.php
|   |   |   +-- Filament
|   |   |   |   +-- Pages
|   |   |   |   +-- Resources
|   |   |   |   |   +-- Alerts
|   |   |   |   |   |   +-- AlertResource.php
|   |   |   |   |   |   +-- Pages
|   |   |   |   |   |   |   +-- CreateAlert.php
|   |   |   |   |   |   |   +-- EditAlert.php
|   |   |   |   |   |   |   +-- ListAlerts.php
|   |   |   |   +-- Widgets
|   |   |   +-- Models
|   |   |   |   +-- Alert.php
|   |   |   +-- Providers
|   |   |   |   +-- AlertsServiceProvider.php
|   |   +-- tests

在面板间共享 resources

有时希望同一 resource 出现在多个面板中,但配置不同。可通过 resource discovery 并结合按面板的自定义来实现:

php
namespace Modules\Users;

use Filament\Contracts\Plugin;
use Filament\Panel;
use Modules\Users\Filament\Resources\UserResource;

class UsersPlugin implements Plugin
{
    protected bool $canManageRoles = false;

    public function getId(): string
    {
        return 'users';
    }

    public static function make(): static
    {
        return app(static::class);
    }

    public function canManageRoles(bool $condition = true): static
    {
        $this->canManageRoles = $condition;

        return $this;
    }

    public function hasRoleManagement(): bool
    {
        return $this->canManageRoles;
    }

    public function register(Panel $panel): void
    {
        $panel->resources([
            UserResource::class,
        ]);
    }

    public function boot(Panel $panel): void
    {
        //
    }
}

然后按不同能力注册:

php
Panel::configureUsing(function (Panel $panel): void {
    match ($panel->getId()) {
        'admin' => $panel->plugin(
            UsersPlugin::make()->canManageRoles(),
        ),
        'staff' => $panel->plugin(
            UsersPlugin::make(),
        ),
        default => null,
    };
});

INFO

Panel::configureUsing() 很强大:模块可自行配置,无需改面板 provider 文件。添加或移除模块时,其 Filament 集成会自动处理。

从模块注册 Livewire 组件

若模块包含 Filament 使用的自定义 Livewire 组件(如自定义页面或 widgets),可在插件的 boot() 方法中注册:

php
use Livewire\Livewire;
use Modules\Alerts\Filament\Pages\AlertsDashboard;

public function boot(Panel $panel): void
{
    Livewire::component('alerts-dashboard', AlertsDashboard::class);
}