Skip to content

TranslatableBackend 多语言基类控制器

介绍

TranslatableBackend 类继承自 Backend 基类,专门用于支持多语言数据管理的后台控制器。它通过关联的翻译模型,将多语言字段(如标题、内容等)存储到独立的翻译表中,并提供统一的 CRUD 操作、搜索、数据权限控制等功能。 通过继承此类,可以快速实现多语言数据模型的后台管理,无需重复编写多语言处理逻辑。

属性说明

多语言相关属性

属性名类型默认值说明
$translationForeignMethodstring'translations'主模型与翻译模型的关联方法名
$translationForeignKeystring'main_id'翻译表中的外键字段,关联主表主键
$translationLocaleKeystring'locale'翻译表中标识语言的字段名
$supportedLocalesarray['zh-cn', 'en-us']支持的语言列表
$multilingualFieldsarray[]需要多语言支持的字段名数组
$translationModelModelnull翻译模型类实例

继承自 Backend 的重要属性

  • $model: 主模型实例

  • $dataLimit: 数据权限控制

  • $dataLimitField: 数据权限字段

  • $searchable: 搜索配置(支持多语言字段搜索)

  • $with: 关联预加载(自动包含翻译关联)

  • 其他属性参考 Backend 文档

方法说明

重写的方法

applyWhere($query, $params)

扩展了父类的查询条件处理,支持对多语言字段的搜索。

多语言字段搜索逻辑:

如果字段在 $multilingualFields 中,则通过 whereHas 在翻译关联上添加条件。

index(Request $request)

获取数据列表,自动预加载翻译关联,并对返回结果进行多语言字段格式化。

返回格式:

多语言字段会转换为以语言代码为键的对象,例如:

json
{
    "id": 1,
    "title": {
        "zh-cn": "标题中文",
        "en-us": "English Title"
    },
    "status": 1
}

selectpage(Request $request)

Selectpage 下拉选择方法,同样支持多语言字段的格式化。

add(Request $request)

添加数据,处理多语言字段的拆分与保存。

请求数据格式:

json
{
    "status": 1,
    "title": {
        "zh-cn": "标题中文",
        "en-us": "English Title"
    },
    "content": {
        "zh-cn": "内容中文",
        "en-us": "English Content"
    }
}

处理流程:

使用 extractMultilingualData() 分离主表字段和多语言字段。

分别对主模型和翻译模型进行验证(如果启用 $modelValidate)。

保存主记录。

遍历 $supportedLocales,为每个语言创建翻译记录。

edit(Request $request)

编辑数据,更新主表字段和多语言翻译。

处理流程:

提取多语言数据。

验证主表和翻译数据。

更新主记录。

使用 updateOrCreate 更新或创建各语言翻译。

del(Request $request)

软删除数据,同时软删除关联的翻译记录(需模型支持软删除)。

destroy(Request $request)

真实删除数据,同时硬删除翻译记录。

restore(Request $request)

恢复软删除的数据,同时恢复翻译记录。

multi(Request $request)

批量更新(仅支持主表字段,不支持多语言字段)。

新增方法

extractMultilingualData(array $data): array

从请求数据中分离主表字段和多语言翻译数据。

返回:

[$masterData, $translations]

$masterData: 主表字段数组

$translations: 以语言代码为键的翻译数据数组

renderTranslations($list)

格式化列表数据,将翻译关联数据转换为以语言为键的多语言字段格式。

使用示例

1. 定义主模型

php
<?php

namespace plugin\condoradmin\app\model;

use support\Model;
use Respect\Validation\Validator as v;
use Illuminate\Database\Eloquent\SoftDeletes;

class SystemTest extends Model
{
    use SoftDeletes;
    /**
     * @var string
     */
    protected $table = 'system_test';

    /**
     * @var string
     */
    protected $primaryKey = 'id';

    /**
     * 指示是否自动维护时间戳
     *
     * @var bool
     */
    public $timestamps = true;

    protected $dateFormat = 'U';

    // 定义时间戳字段名
    const CREATED_AT = 'createtime';
    const UPDATED_AT = 'updatetime';

    // 让所有属性都可以批量分配
    protected $guarded = [];

    protected function serializeDate(\DateTimeInterface $date)
    {
        return $date->format('Y-m-d H:i:s');
    }

    public function rules()
    {
        return [
            'price' => v::optional(v::notEmpty())->setName(trans('fields.price', [], 'test')),
            'views' => v::optional(v::notEmpty())->setName(trans('fields.views', [], 'test')),
            'activitytime' => v::optional(v::notEmpty())->setName(trans('fields.activitytime', [], 'test')),
            'refreshtime' => v::optional(v::notEmpty())->setName(trans('fields.refreshtime', [], 'test')),
        ];
    }

    public function getPriceAttribute($value)
    {
        return floatval($value);
    }

    /**
     * 关联翻译
     */
    public function translations()
    {
        return $this->hasMany(SystemTestTranslations::class, 'main_id', 'id');
    }
}

2. 定义翻译模型

php
<?php

namespace plugin\condoradmin\app\model;

use support\Model;
use Respect\Validation\Validator as v;

class SystemTestTranslations extends Model
{
    /**
     * @var string
     */
    protected $table = 'system_test_translations';

    /**
     * @var string
     */
    protected $primaryKey = 'id';

    /**
     * 指示是否自动维护时间戳
     *
     * @var bool
     */
    public $timestamps = true;

    protected $dateFormat = 'U';

    // 定义时间戳字段名
    const CREATED_AT = 'createtime';
    const UPDATED_AT = 'updatetime';

    // 让所有属性都可以批量分配
    protected $guarded = [];

    protected function serializeDate(\DateTimeInterface $date)
    {
        return $date->format('Y-m-d H:i:s');
    }

    public function rules()
    {
        return [
            'name' => v::optional(v::notEmpty())->setName(trans('fields.name', [], 'test')),
            'target' => v::optional(v::notEmpty())->setName(trans('fields.target', [], 'test')),
            'title' => v::optional(v::notEmpty())->setName(trans('fields.title', [], 'test')),
            'content' => v::optional(v::notEmpty())->setName(trans('fields.content', [], 'test')),
            'image' => v::optional(v::notEmpty())->setName(trans('fields.image', [], 'test')),
            'images' => v::optional(v::notEmpty())->setName(trans('fields.images', [], 'test')),
            'attachfile' => v::optional(v::notEmpty())->setName(trans('fields.attachfile', [], 'test')),
            'keywords' => v::optional(v::notEmpty())->setName(trans('fields.keywords', [], 'test')),
            'description' => v::optional(v::notEmpty())->setName(trans('fields.description', [], 'test')),
        ];
    }
}

3. 定义控制器

php
<?php

namespace plugin\condoradmin\app\controller;

use plugin\condoradmin\app\library\TranslatableBackend;
use plugin\condoradmin\app\model\SystemTest;
use plugin\condoradmin\app\model\SystemTestTranslations;

class SystemTestController extends TranslatableBackend
{

    protected array $searchable = [
        'name' => ['type' => 'string'],
        'title' => ['type' => 'string'],
        'price' => ['type' => 'int'],
        'views' => ['type' => 'int'],
        'activitytime' => ['type' => 'int'],
        'refreshtime' => ['type' => 'int'],
        'createtime' => ['type' => 'int'],
    ];

    // 多语言字段
    protected array $multilingualFields = ['name', 'target', 'title', 'content', 'image', 'images', 'attachfile', 'keywords', 'description'];


    public function __construct()
    {
        parent::__construct();
        // 主表模型
        $this->model = new SystemTest();
        // 多语言表模型
        $this->translationModel = new SystemTestTranslations();
    }
}