插件简单写法
最小插件的 plugin.json、ServiceProvider、后台页、资源和模板标签示例。
插件简单写法
本文用 demo_plugin 说明一个最小插件怎么写。插件用于可选能力,必须可以启用、禁用和卸载。
一、目录
plugins/demo_plugin/
├─ plugin.json
├─ src/
│ ├─ DemoPluginServiceProvider.php
│ ├─ DemoPluginAdminController.php
│ └─ Templates/DemoPluginTag.php
├─ routes/admin.php
├─ routes/web.php
├─ resources/assets/admin.css
├─ resources/assets/admin.js
└─ database/migrations/
二、plugin.json
{
"code": "demo_plugin",
"name": "示例插件",
"version": "1.0.0",
"providers": [
"Plugins\\demo_plugin\\DemoPluginServiceProvider"
],
"requires": {
"tanzcms": "^1.2"
},
"menus": [
{
"top": "plugin",
"group": "plugin.demo",
"group_title": "示例插件",
"id": "plugin.demo_plugin.settings",
"title": "插件设置",
"url": "plugins/demo-plugin/settings",
"permission": "plugin.demo_plugin.settings",
"sort": 10
}
],
"template_tags": [
{
"value": "demo_plugin",
"label": "示例插件标签",
"snippet": "{demo_plugin return={{return}}}...{/demo_plugin}",
"defaults": {
"return": "item"
}
}
]
}
三、ServiceProvider
<?php
namespace Plugins\demo_plugin;
use App\Cms\Templates\TemplateTagRegistry;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
use Plugins\demo_plugin\Templates\DemoPluginTag;
class DemoPluginServiceProvider extends ServiceProvider
{
public function boot(): void
{
$base = dirname(__DIR__);
if (! $this->app->routesAreCached()) {
Route::middleware('web')->group($base.'/routes/web.php');
Route::middleware('web')->group($base.'/routes/admin.php');
}
app(TemplateTagRegistry::class)->register(app(DemoPluginTag::class));
$this->loadMigrationsFrom($base.'/database/migrations');
}
}
四、后台路由
<?php
use Illuminate\Support\Facades\Route;
use Plugins\demo_plugin\DemoPluginAdminController;
Route::prefix('/admin/plugins/demo-plugin')->group(function (): void {
Route::get('/settings', [DemoPluginAdminController::class, 'page']);
Route::post('/settings', [DemoPluginAdminController::class, 'save']);
});
实际接入时,后台路由必须继承管理员认证、权限和 CSRF 边界。
五、后台控制器
<?php
namespace Plugins\demo_plugin;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class DemoPluginAdminController extends Controller
{
public function page(): string
{
return '<section class="tanz-plugin-section"></section>';
}
public function save(Request $request): JsonResponse
{
$data = $request->validate([
'enabled' => ['nullable', 'boolean'],
]);
return response()->json([
'message' => '保存成功',
'data' => $data,
]);
}
}
后台页面可以输出少量容器结构,但复杂列表、弹窗、表单和按钮应复用后台通用组件。
六、模板标签
<?php
namespace Plugins\demo_plugin\Templates;
use App\Cms\Templates\Contracts\TemplateTagHandlerContract;
use App\Cms\Templates\TemplateRenderContext;
class DemoPluginTag implements TemplateTagHandlerContract
{
public function names(): array
{
return ['demo_plugin'];
}
public function render(string $name, array $attributes, string $body, TemplateRenderContext $context): string
{
return '';
}
}
标签处理器应返回结构化循环结果或小片段 HTML,不应拼整页。
七、公开回调
插件需要第三方通知时,使用统一回调入口:
/api/callback/{plugin}/{action}/{payload?}
回调必须校验签名、记录日志并处理幂等。
八、插件写法禁忌
- 不把插件能力写进核心控制器。
- 不直接修改
.env。 - 不在插件禁用后让核心依赖插件类。
- 不把发布后的
public/assets/plugins/{code}当源码目录。 - 不绕过上传、权限、日志和回调统一能力。