💾 更新助手

SaveHelper 是 ThinkAdmin 提供的数据更新助手,专注于简化数据状态的更新操作,如数据禁用、启用以及状态更改等。通过简洁的接口和直观的操作,快速实现数据状态的更新。

📚 基础概念

🤔 基本介绍

SaveHelper(更新助手)是 ThinkAdmin 框架提供的一个工具类,专门用于快速更新数据记录的状态或字段值。

简单理解:就像数据的"状态切换器",帮你快速更新数据的状态,如启用/禁用、上架/下架等。

传统方式(不推荐)

// ❌ 传统方式:需要手动处理更新逻辑
public function state()
{
    $ids = input('id');
    $status = input('status');
    
    // 1. 验证数据
    if (empty($ids)) {
        $this->error('ID 不能为空');
    }
    if (!in_array($status, [0, 1])) {
        $this->error('状态值范围异常');
    }
    
    // 2. 批量更新
    $ids = str2arr($ids);
    foreach ($ids as $id) {
        SystemUser::where('id', $id)->update(['status' => $status]);
    }
    
    $this->success('更新成功');
}

使用 SaveHelper(推荐)

// ✅ 使用 SaveHelper:一行代码完成更新
public function state()
{
    SystemUser::mSave($this->_vali([
        'status.require' => '状态值不能为空!',
        'status.in:0,1' => '状态值范围异常!',
    ]));
}

🤔 使用优势

问题1:代码重复

不使用 SaveHelper 时,每个状态更新都需要重复编写相同的逻辑:

// ❌ 每个状态更新都要写这些代码
$ids = input('id');
$status = input('status');
// 验证、更新、返回结果... 大量重复代码

问题2:容易出错

手动处理更新逻辑容易出错:

// ❌ 容易出错:忘记验证、忘记处理条件等
$ids = input('id');
SystemUser::whereIn('id', $ids)->update(['status' => $status]);
// 没有验证,可能更新错误的数据

问题3:维护困难

当需要修改更新逻辑时,需要在多个地方修改:

// ❌ 需要在多个控制器中修改相同的逻辑
// UserController.php
SystemUser::whereIn('id', $ids)->update(['status' => $status]);

// OrderController.php
SystemOrder::whereIn('id', $ids)->update(['status' => $status]);
// 重复代码

使用 SaveHelper 的优势

// ✅ 统一接口,代码简洁
SystemUser::mSave($this->_vali([...]));
SystemOrder::mSave($this->_vali([...]));

🎯 核心功能

功能1:快速更新

一行代码完成数据更新:

// 更新用户状态
SystemUser::mSave($this->_vali([
    'status.require' => '状态值不能为空!',
    'status.in:0,1' => '状态值范围异常!',
]));

功能2:批量操作

支持批量更新多条记录:

// 批量更新用户状态
// POST 数据:id=1,2,3&status=1
SystemUser::mSave($this->_vali([
    'status.require' => '状态值不能为空!',
]));
// 自动更新 id=1,2,3 的用户状态为 1

功能3:条件限制

支持设置更新条件,确保数据安全:

// 只更新未删除的记录
SystemUser::mSave(
    $this->_vali([...]),
    'id',
    ['is_deleted' => 0]  // 更新条件
);

功能4:回调处理

支持前置和后置回调,处理业务逻辑:

// 前置回调:更新前处理
protected function _save_filter($query, array &$data)
{
    // 验证和处理数据
}

// 后置回调:更新后处理
protected function _save_result(bool $result)
{
    // 处理更新结果
}

📖 相关概念

状态字段

  • 用于标识数据状态的字段
  • 例如:status(0=禁用,1=启用)

批量更新

  • 一次性更新多条记录
  • 例如:同时更新多个用户的状态

条件限制

  • 设置更新条件,只更新符合条件的记录
  • 例如:只更新未删除的记录

🎯 使用场景

场景1:用户管理

启用/禁用用户账号:

public function state()
{
    SystemUser::mSave($this->_vali([
        'status.require' => '状态值不能为空!',
        'status.in:0,1' => '状态值范围异常!',
    ]));
}

场景2:商品管理

上架/下架商品:

public function state()
{
    SystemGoods::mSave($this->_vali([
        'status.require' => '状态值不能为空!',
        'status.in:0,1' => '状态值范围异常!',
    ]), 'id', ['is_deleted' => 0]);
}

场景3:订单管理

审核通过/拒绝订单:

public function audit()
{
    SystemOrder::mSave($this->_vali([
        'audit_status.require' => '审核状态不能为空!',
        'audit_status.in:1,2' => '审核状态范围异常!',
    ]));
}

场景4:内容管理

发布/隐藏文章:

public function publish()
{
    SystemArticle::mSave($this->_vali([
        'status.require' => '状态值不能为空!',
        'status.in:0,1' => '状态值范围异常!',
    ]));
}

🚀 主要功能

  • 状态更新: 快速更新数据状态(启用/禁用等)
  • 批量处理: 支持批量更新多条数据记录
  • 安全验证: 完善的数据验证和权限控制
  • 错误处理: 详细的错误处理和日志记录
  • 事务支持: 支持数据库事务操作

📋 使用场景

  • 用户状态管理(启用/禁用用户)
  • 商品状态管理(上架/下架商品)
  • 订单状态管理(处理/完成订单)
  • 内容状态管理(发布/隐藏内容)
  • 批量数据操作

⚙️ 工作原理

数据定位

  • 根据传入的数据 ID 和条件自动定位数据记录
  • 支持主键字段和自定义查询条件
  • 确保数据操作的安全性和准确性
  • 支持批量数据定位

状态更新

  • 提供清晰的状态选项供用户选择
  • 自动执行状态更新操作
  • 支持单条和批量数据更新
  • 支持自定义状态值

安全保护

  • 数据验证和权限控制
  • 防止恶意数据修改
  • 详细的操作日志记录
  • 支持操作权限验证

事务处理

  • 支持数据库事务操作
  • 确保数据一致性
  • 支持回滚机制
  • 异常处理机制

具体实现原理

为了充分利用 SaveHelper 数据更新助手在模型(Model)和控制器(Controller)中的功能,系统进行了以下优化:

  • 模型继承:若需在模型中使用 SaveHelper 快捷查询器,该模型应继承 \think\admin\Model 类。这样做可以确保模型拥有 SaveHelper 提供的所有功能,方便进行数据的快速保存和查询操作。
  • 控制器继承:对于需要使用 SaveHelper 快捷查询器的控制器,应继承 \think\admin\Controller 类。通过继承这个基类,控制器可以方便地调用 SaveHelper 的相关方法,实现对数据的快速处理和响应。
  • 前端数据传递:前端需要传递需要修改的数据 ID 以及数据状态值给后端。为了确保数据的安全性和准确性,强烈建议前端传递第二个参数(如操作类型、验证信息等)。这样可以为后端提供更多的上下文信息,有助于后端进行更精确的数据处理和验证。
  • 数据安全考虑:为了防止数据被恶意修改,使用 SaveHelper 时指定第二个参数是非常必要的。此外,还可以结合验证器 _vali() 和限定条件 $where 变量来进一步确保数据的安全性。验证器可以对数据进行严格的校验,确保只有符合规则的数据才能被保存;而 $where 变量则可以限制操作的范围,防止对不应修改的数据进行操作。

通过以上优化,SaveHelper 快捷查询器在模型和控制器中的使用变得更加灵活和安全,同时前端传递的数据也得到了更好的处理和验证,提升了整个系统的稳定性和安全性。

// 1.0 模型用法
// 参数 $data 为待修改数据
// 参数 $field 为主键字段
// 参数 $where 为修改查询条件
SystemUser::mSave($data, $field, $where);

// 1.1 模型通用更新
SystemUser::mSave();

// 1.2 配合验证器使用(推荐方式)
SystemUser::mSave($this->_vali([
    'status.require' => '状态值不能为空!',
    'status.in:0,1'  => '状态值范围异常!',
]));

// 1.3 配置验证器和限制条件
SystemUser::mSave($this->_vali([
    'status.require' => '状态值不能为空!',
    'status.in:0,1'  => '状态值范围异常!',
]), 'id', [
    'is_deleted' => 0,
    'status' => 1,
]);


// 2.0 控制器用法(不推荐,建议使用模型方法)
// 参数 $dbQuery 为模型名称
// 参数 $data 为待修改数据
// 参数 $field 为主键字段
// 参数 $where 为修改查询条件
$this->_save('SystemUser', $data, $field, $where);

// 2.1 通用修改器(不推荐,建议使用模型方法)
$this->_save('SystemUser');

// 2.2 配合验证器使用(不推荐,建议使用模型方法)
$this->_save('SystemUser', $this->_vali([
    'status.require' => '状态值不能为空!',
    'status.in:0,1'  => '状态值范围异常!',
]));

数据回调处理

对于数据更新操作,Controller 内置了两个回调方法:

// 更新前置操作,允许使用引用更改查询条件和数据
// 参数 $query:查询对象
// 参数 $data:待更新的数据数组
// 返回 false 时,默认行为将不会再执行
[_ACTION]_save_filter($query, array &$data)

// 更新后置操作
// 参数 $result:更新结果,成功为 true,失败为 false
// 返回 false 时,默认行为将不会再执行
[_ACTION]_save_result(bool $result)

实际应用案例

<?php
declare(strict_types=1);

namespace app\admin\controller;

use think\admin\Controller;
use think\admin\model\SystemUser;

/**
 * 系统用户管理
 * @class User
 * @package app\admin\controller
 */
class User extends Controller
{
    /**
     * 修改数据状态
     * @auth true 
     */
    public function state()
    {
        // 实际项目中的安全检查示例
        $this->_checkInput();
        
        SystemUser::mSave($this->_vali([
            'status.in:0,1'  => '状态值范围异常!',
            'status.require' => '状态值不能为空!',
        ]));
    }

    /**
     * 检查输入变量(实际项目中的私有方法示例)
     */
    private function _checkInput()
    {
        // 防止删除系统超级账号
        if (in_array('10000', str2arr(input('id', '')))) {
            $this->error('系统超级账号禁止删除!');
        }
    }

    /**
     * 更新前置回调(在更新前进行数据过滤)
     * @param $query 查询对象
     * @param array $data 待更新的数据
     */
    protected function _save_filter($query, array &$data)
    {
        // 可以修改查询条件
        $query->where('is_deleted', 0);
        
        // 可以修改待更新的数据
        if (isset($data['status'])) {
            // 添加额外的数据
            $data['update_at'] = date('Y-m-d H:i:s');
        }
    }

    /**
     * 更新后置回调(通用回调)
     * @param bool $result 更新结果
     */
    protected function _save_result(bool $result)
    {
        if ($result) {
            sysoplog('系统用户管理', '更新用户状态成功');
        }
    }

    /**
     * 当一个控制器存在多个 save 操作时,可以指定回调前缀
     * @param bool $result 更新结果
     */
    protected function _state_save_result(bool $result)
    {
        // 可以根据 $result 状态返回结果 
        // 失败 $this->error(MESSAGE);
        // 成功 $this->success(MESSAGE);
    }
}

如果是在 `ThinkAdmin` 后台基于 `admin.js` 的情况下,可使用 `data-action` 来与 `SaveHelper` 配合使用。

## 🔧 前端配合使用

**自动状态更新**
```html
<!-- 使用 data-action 属性自动更新状态 -->
<button data-action="{:url('save')}" 
        data-value="id#{{d.id}};status#1" 
        data-confirm="确认要启用这条记录吗?"
        class="layui-btn layui-btn-sm">启用</button>

批量状态更新

<!-- 批量更新多条记录 -->
<button data-action="{:url('save')}" 
        data-value="id#1,2,3;status#1" 
        data-confirm="确认要批量启用这些记录吗?"
        class="layui-btn layui-btn-sm">批量启用</button>

前端提交格式说明 前端提交上来的内容 data-value 支持多组数据,以英文分号 ; 分隔,键与值以英文 # 分隔,如:data-value="id#1;status#0;"

格式说明:

  • 单条记录:data-value="id#1;status#0"
  • 多条记录:data-value="id#1,2,3;status#1"

SaveHelper 深入应用

1. 数据库事务处理

<?php
declare(strict_types=1);

namespace app\admin\controller;

use think\admin\Controller;
use think\admin\model\SystemUser;
use think\facade\Db;

class User extends Controller
{
    /**
     * 批量更新用户状态(使用事务)
     * @auth true
     */
    public function batchState()
    {
        $data = $this->_vali([
            'ids.require' => '用户ID不能为空!',
            'status.require' => '状态值不能为空!',
            'status.in:0,1' => '状态值范围异常!',
        ]);
        
        // 开启数据库事务
        Db::startTrans();
        try {
            // 批量更新用户状态
            SystemUser::mk()
                ->whereIn('id', str2arr($data['ids']))
                ->where('is_deleted', 0)
                ->update(['status' => $data['status']]);
            
            // 记录操作日志
            sysoplog('系统用户管理', "批量更新用户状态[{$data['status']}]");
            
            // 提交事务
            Db::commit();
            $this->success('批量更新成功!');
        } catch (\Exception $e) {
            // 回滚事务
            Db::rollback();
            $this->error('批量更新失败:' . $e->getMessage());
        }
    }
}

2. 复杂业务逻辑处理

protected function _save_filter(array &$data)
{
    // 业务规则验证
    if (!empty($data['id'])) {
        $user = SystemUser::mk()->find($data['id']);
        if (empty($user)) {
            $this->error('用户不存在!');
        }
        
        // 状态变更时的特殊处理
        if ($user['status'] != $data['status']) {
            // 禁用用户时,清除登录会话
            if ($data['status'] == 0) {
                $this->app->session->delete("user_{$data['id']}");
            }
            
            // 记录状态变更日志
            sysoplog('系统用户管理', "用户[{$user['username']}]状态变更为[{$data['status']}]");
        }
    }
}

protected function _save_result(bool $result)
{
    if ($result) {
        // 触发用户状态变更事件
        $this->app->event->trigger('PluginAdminUserStatusChange', [
            'id' => input('id'),
            'status' => input('status'),
        ]);
    }
}

3. 条件限制和权限控制

public function save()
{
    // 非超级管理员只能更新自己的数据
    $where = [];
    if (!AdminService::isSuper()) {
        $where['uuid'] = AdminService::getUserId();
    }
    
    SystemUser::mSave(
        $this->_vali([
            'status.require' => '状态值不能为空!',
            'status.in:0,1' => '状态值范围异常!',
        ]),
        'id',
        $where
    );
}

4. 性能优化建议

// 批量更新时,使用 whereIn 而不是循环更新
// ❌ 不推荐:循环更新
foreach ($ids as $id) {
    SystemUser::mk()->where('id', $id)->update(['status' => 1]);
}

// ✅ 推荐:批量更新
SystemUser::mk()
    ->whereIn('id', $ids)
    ->update(['status' => 1]);

// 使用索引字段进行查询
SystemUser::mk()
    ->where('id', $id)  // id 是主键,有索引
    ->where('status', 1)  // 如果 status 有索引,查询更快
    ->update(['status' => 0]);
  • 多字段更新:data-value="id#1;status#1;name#test"
最近更新:
Contributors: 邹景立, Anyon