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
我有一个 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()
:
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