【问题标题】:handling double username in mysql and php在 mysql 和 php 中处理双重用户名
【发布时间】:2012-07-22 01:19:42
【问题描述】:

我正在编写自己的 API 实现。这个 API 的资源之一是/user,我可以将POST 一个JSON 字符串传递给这个,解析它并将它发送到一个UserService,它有一个addUser 方法。现在显然我不能允许两个用户拥有相同的用户名。到目前为止,这是我必须要做的。

protected function handlePost() {
    $user = json_decode($this->getRequest()->getRequestData(), true);

    try {
        $createdUserId = $this->userService->addUser(
            $user['username'],
            $user['password'],
            $user['typeId'],
            $user['companyId']
        );
    } catch (PDOException $e) {
        $this->getResponse()->setStatus(102);
    }

    if ($createdUserId)
        $this->getResponse()->setStatus(101);

    $this->getResponse()->sendResponse();
}

这个函数在 HTTP 请求被解析后被调用。如您所见,我在关联数组中获得了 $user 对象。然后我将这些值用作$this->userService->addUser 方法的参数。

这是addUser() 方法:

public function addUser($username, $password, $type, $companyId) {
    $sth = $this->dbh->prepare('INSERT INTO app_user
                                (username, password, typeId, companyId)
                                VALUES (?, ?, ?, ?)');
    $userAdded = $sth->execute(array($username, md5($password), $type, $companyId));

    return ($userAdded)
        ? $this->dbh->lastInsertId()
        : null;
}

现在,如果 $sth->execute 由于某种原因失败,我会返回 null(并且不会返回状态码 101 或 102,我知道这一点)。但是如果用户名已经存在,PDO 会抛出异常,我在上面的 handlePost() 方法中捕获了该异常。

我的问题当然是已经被占用的用户名并不例外,不应该这样处理,但我真的不知道应该如何处理?

我必须能够区分触发的唯一约束和触发的其他一些(可能是并发问题),以便我可以将适当的状态代码发送回 API 的使用者。

一个明显的解决方案是检查用户名是否存在于单独的查询中,但出于显而易见的原因,我更愿意限制对数据库的调用。

我怎样才能优雅地解决这个问题?如果我必须使用异常,是否有特定 PDO 异常的列表,这样我就不必使用通用的PDOException

【问题讨论】:

  • 告诉我是否正确 - 您想在不使用 SQL 的情况下检查用户是否存在于数据库中?
  • 您的代码表明您正在编写 API,但您的文本表明您正在实现 API。我很困惑!
  • @Yehonatan 不不,我知道这是不可能的:p,我只想知道是否有办法检查它,而不必使用异常来处理它并将查询数量限制为DB最好是1。
  • @Adnan,我正在自己编写消费者和提供者部分。
  • 两个选项,做另一个查询,或者将用户名设置为唯一键并检测插入失败。

标签: php pdo


【解决方案1】:

您是否足够关注性能(即最小化对数据库的查询),以至于在尝试插入之前您不会只检查数据库中用户名的存在吗?您可能会发现这种逻辑比尝试捕获异常并根据最后一个查询或类似查询影响的行数来确定是否确实存在异常更容易。

我还要补充一点,如果你想让它不是例外,你可以在插入中添加 ON DUPLICATE KEY 逻辑。

【讨论】:

  • 理论上你是对的,但是由于 PHP 有一个无共享架构,你无法避免 2 个请求并行运行,尝试插入相同的用户名,除非你创建一个全局机制来锁定2 个 PHP 请求之间的用户名。
  • @BenjaminDubois,使用这种方法,我不能将检查用户名和插入(如果尚未找到)放入事务中吗?
  • @pEkvo 是的!存储过程是单个事务:) 但您需要直接用 SQL 编写它,然后用 PHP 调用它。
  • @BenjaminDubois,好的,谢谢您的帮助。我会接受这个答案,因为它是我最想要的,但是在检查 PDO 文档后,您自己的答案也很有帮助,所以我会投票赞成 :)
  • 在同一时间使用相同用户名的两个请求的可能性可能非常小,除非您谈论的是极高容量的服务。在这种情况下,您是对的,您可能首先不想进行两个单独的查询(因此我对性能的初步评论)。
【解决方案2】:

如果您未在 PDO 构造函数中使用 ERRMODE_EXCEPTION,则 PDO 不应发送违反完整性约束的异常。

在这种情况下,您必须调用方法 PDOStatement::errorCode() 或 PDOStatement::errorInfo()

获取错误的详细信息并查看它是违反完整性约束还是真正的问题。

【讨论】:

  • 我现在启用了 ERRMODE_EXCEPTION 模式,你会说不鼓励在生产中使用它吗? (这显然会迫使我改变我的方法)
  • @pEkvo 是的,对我来说,你不应该在 prod 中使用 ERRMODE_EXCEPTION:当发生大错误时(比如无法访问数据库服务器),PDO 将继续发送异常,但不是因为违反 SQL 约束,这在 API 中并不例外......
猜你喜欢
  • 1970-01-01
  • 2011-06-14
  • 1970-01-01
  • 1970-01-01
  • 2011-06-27
  • 2016-10-17
  • 2013-08-12
  • 1970-01-01
  • 2013-06-07
相关资源
最近更新 更多