# 插件开发指南 ## 目录格式 ``` plugins/{vendor}/{name}/ plugin.json README.md # 默认 / 英文 README.zh_CN.md # 可选,后台中文界面优先 database/migrations/ # 可选 resources/views/ # 可选 src/PluginServiceProvider.php ``` ### plugin.json ```json { "name": "vendor/my-plugin", "title": "显示名", "version": "1.0.0", "description": "说明", "provider": "Plugins\\Vendor\\MyPlugin\\PluginServiceProvider", "requires": ["larablog/payment"], "optional": [], "docs": "README.md" } ``` - `requires`:硬依赖,未启用时 `PluginManager::enable` 会拒绝 - `docs`:相对插件根目录的说明文件(默认 `README.md`)。后台按当前语言优先读 `README.{locale}.md`(如 `README.zh_CN.md`),再试语言前缀(如 `README.zh.md`),最后回退到 `docs` 指定文件。Markdown 会渲染成 HTML,在侧栏弹层中展示。只允许指向插件目录内(`..`、绝对路径、软链越界会被拒绝) 在 `composer.json` → `autoload.psr-4` 注册命名空间,然后: ```bash composer dump-autoload php artisan plugins:sync # 启用后如有迁移: php artisan migrate ``` ## 生命周期 1. `PluginManager::discover()` 扫描磁盘 2. `plugins:sync` / 后台同步写入 `plugins` 表 3. 启用后 `registerEnabledProviders()` 注册 Provider(Filament panel 构建前也会注册,以便 `Panel::configureUsing`) 4. Provider `register()` / `boot()` 内挂 Hook、路由、Filament Resource、迁移 ## Hook 类型 | 方法 | 用途 | |---|---| | `listen` + `dispatch` | 事件通知(如 `comment.created`、`order.paid`) | | `gather` | 字符串管道(主题槽 `theme.*`) | | `collect` | 数组合并(Filament 组件/列/动作) | | `filter` | 值管道(表单 mutate 等;每个监听器可替换值) | | `listeners` | 取回监听器自行折叠(`article.access` 用它保证只许变严) | ```php use App\Domain\Plugin\Hook; Hook::listen('comment.created', function (Comment $comment): void { /* ... */ }); Hook::listen('theme.sidebar', function (string $html): string { return $html.'

侧栏注入

'; }); Hook::listen('filament.article.form', function (array $components): array { return [/* Section / Field */]; }); Hook::listen('article.access', function (AccessDecision $decision, array $ctx): AccessDecision { return $decision->tightenWith(/* 更严决策 */); }); ``` ## 约定扩展点(首批) | 点 | 类型 | 用途 | |---|---|---| | `theme.*` | gather | 前台主题槽 | | `article.access` | fold | 收紧阅读权限;核心逐个 `tightenWith` 折叠,放宽无效、非 `AccessDecision` 返回值忽略 | | `filament.article.form` | collect | 文章表单追加组件 | | `filament.article.table.columns` | collect | 文章表追加列 | | `filament.article.table.filters` | collect | 文章表追加筛选 | | `filament.article.table.query` | filter | 文章表查询(如子查询列) | | `filament.article.actions` | collect | 文章动作 | | `filament.article.mutate_before_fill` | filter | 编辑回填 | | `filament.article.mutate_before_save` | filter | 保存前改数据(勿在此 unset 限制字段) | | `filament.article.validate_access_restrictions` | filter | 密码/付费/会员互斥校验(strip 之前) | | `filament.article.after_save` | dispatch | 保存后写插件表 | | `order.paid` / `order.refunded` | dispatch | 支付插件发出 | ## 三类注入 1. **UI**:前台主题槽 + 后台 Filament collect 2. **功能**:access / lifecycle Hook 3. **数据**:插件自有 migration/model,用外键或 `product_type`+`product_id` 关联 ## Filament 页面/资源 放在插件命名空间,于 `register()` 中: ```php Panel::configureUsing(function (Panel $panel): void { if ($panel->getId() !== 'admin') return; $panel->resources([OrderResource::class])->pages([SettingsPage::class]); }); ``` Resource / Page 需用 `canAccess` / `shouldRegisterNavigation` 检查插件已启用。 ## 后台 - 「插件」页:卡片启停、依赖展示、使用说明 - 可选:继续用核心 `PluginSkeletonPage` 做极简占位 ## 内置插件 | 插件 | 状态 | |---|---| | `larablog/ai-comment-moderation` | 可运行 | | `larablog/payment` | Stub 订单/权益可跑通 | | `larablog/paid-content` | 文章付费(依赖 payment) | | `larablog/membership` | 会员套餐 / Stub 订阅 / 会员可见文章(依赖 payment) | | `larablog/plugin-marketplace` | 骨架 | | `larablog/theme-marketplace` | 骨架 |