TanzCMS开源CMS系统

插件简单写法

最小插件的 plugin.json、ServiceProvider、后台页、资源和模板标签示例。

更新:2026-05-31 03:07:49 浏览:3

插件简单写法

本文用 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} 当源码目录。
  • 不绕过上传、权限、日志和回调统一能力。