Laravel 在数据库查询中将字符串转换为数字?

Laravel casts strings to numbers in DB queries?

我有一个 table 的 players 和一个名为 name 的字段(当然是一个字符串)。将玩家添加到数据库时,我使用自定义规则检查唯一性:

public function passes($attribute, $value)
{
    return !($this->players->where('name', $value)->count());
}

这在大多数情况下都有效。然而,我注意到,只要有可能,字符串就会被转换为数字。例如,如果有一个名为 "123" 的玩家,那么我无法添加一个名为 "0000123""123." 或其他带有前导或尾随零的此类变体的新玩家。或者,例如,如果数据库中有一个名为“9.87e2”的播放器,那么我就不能添加“987”。

作为完整性检查,我在函数内部记录了 gettype($value) 并确认它始终是一个字符串。然后我尝试在 SQL 沙箱中复制该问题,但一切都按预期进行。这是否意味着这是 Laravel 中的错误?

问题似乎不是转换问题,而是在 name 上使用 ->where() 时在结果 Collection 上使用 ->where() 与在数据库层上使用 ->where() 时的松散比较.比较:

$this->players->where('name', $value)->count();

在上面,$this->players 将是与 $this(我假设的另一个模型)关联的数据库中所有 Player 记录的 Collection。这相当于:

$players = collect([
  ['id' => 1, 'name' => '123'],
  ['id' => 2, 'name' => '234'],
  // ...
]);

$players->where('name', '123')->count();    // 1
$players->where('name', '000123')->count(); // 1 (That's odd...)
$players->where('name', '234')->count();    // 1
$players->where('name', '345')->count();    // 0
// ... Ect etc.

在上面的示例中,您可以看到 name 上的“松散”比较,使用 '00123''123' 两者 returns 1. 这是有问题的,因为数据库可以将 '000123''123' 作为不同的实体来处理,但是不能针对 Collection 进行查询。

为了解决这个问题,我们应该直接查询数据库,使用 players 方法 而不是 属性()表示方法,缺少()表示属性):

$query = $this->players()->where('name', $value); // Omitted the `count()` for now

此时,$query 是一个数据库查询(Builder 实例)。我们可以看到调用 ->toSql():

会执行的 SQL
dd($query->toSql());
// SELECT * FROM players WHERE related_id = ? AND name = ?

(将 related_id 替换为 $this 在原始问题中代表的任何内容)。

链接方法 ->count() 会将其作为 SELECT count(...) ... 查询执行,而不是计算 Collection.

的长度

所以,把它们放在一起;使用 ->players() 直接查询数据库,执行 ->count()->exists() 查询:

return !$this->players()->where('name', $value)->count();  // 0 or # > 1
// OR
return !$this->players()->where('name', $value)->exists(); // true or false