You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何阻止用户重复添加同一Widget ID?MySQL校验函数问题修复

问题描述

编写MySQL校验函数check_u_widgets用于避免用户重复添加同一Widget,需求是:不同用户可添加相同Widget,但同一用户不能重复添加同一Widget。但当前函数存在逻辑错误:只要用户添加过任意Widget,就无法再添加其他Widget。

数据表示例

widget_id  user_id
1          12
5          12
6          10
9          2
1          2
1          10

当前错误函数代码

public function check_u_widgets($user_id, $widget_id)
{
  global $db, $system;
  $result = $db->query(sprintf("SELECT * FROM users_widgets WHERE user_id = %s", secure($user_id) )) or _error("SQL_ERROR_THROWEN");
  if ($result->num_rows > 0) {
    $wid = $result->fetch_assoc();
    if($wid['widget_id'] == $widget_id) {
      throw new Exception(__("Sorry, It seem's that this widget is already assigned"));
    }
  }
  return false;
}
解决方法

问题根源

原函数仅查询当前用户的第一条Widget记录并对比,存在两个核心问题:

  1. 无法覆盖用户已添加多个Widget的场景,若要添加的Widget不是查询返回的第一条记录,函数检测不到重复
  2. 若调用方误将函数返回false判定为“不可添加”,会导致用户无法添加新的Widget

修正后的校验函数

正确逻辑是直接查询user_id与widget_id的组合是否存在,而非查询用户所有记录:

public function check_u_widgets($user_id, $widget_id)
{
    global $db, $system;
    // 精准查询当前用户是否已添加该Widget
    $result = $db->query(sprintf(
        "SELECT 1 FROM users_widgets WHERE user_id = %s AND widget_id = %s",
        secure($user_id),
        secure($widget_id)
    )) or _error("SQL_ERROR_THROWEN");
    
    if ($result->num_rows > 0) {
        throw new Exception(__("Sorry, It seems that this widget is already assigned"));
    }
    
    return false;
}

优化点说明

  • 新增widget_id查询条件,直接定位目标记录,避免无效数据查询
  • 使用SELECT 1替代SELECT *,仅需判断记录存在性,提升查询性能
  • 覆盖所有重复场景,确保同一用户无法重复添加同一Widget

更可靠的数据库层面约束

为彻底避免重复数据,建议给表添加(user_id, widget_id)联合唯一索引,从数据库层面拦截重复插入:

ALTER TABLE users_widgets ADD UNIQUE INDEX idx_user_widget (user_id, widget_id);

简化后的插入函数(可选)

添加唯一索引后,可直接尝试插入数据,捕获数据库重复键错误(错误码1062):

public function add_u_widget($user_id, $widget_id)
{
    global $db, $system;
    try {
        $db->query(sprintf(
            "INSERT INTO users_widgets (user_id, widget_id) VALUES (%s, %s)",
            secure($user_id),
            secure($widget_id)
        )) or _error("SQL_ERROR_THROWEN");
    } catch (Exception $e) {
        // 捕获重复键错误
        if ($db->errno === 1062) {
            throw new Exception(__("Sorry, It seems that this widget is already assigned"));
        }
        // 其他错误重新抛出
        throw $e;
    }
}

优势

  • 数据库约束避免了并发插入时的竞态条件,比应用层校验更可靠
  • 简化应用层逻辑,无需额外查询校验

内容的提问来源于stack exchange,提问作者Simon Lewis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 18:15:38