在 Laravel Eloquent 为什么有些 SQL 参数没有绑定?
In Laravel Eloquent why are some SQL parameters not bound in?
问题:
我注意到 Laravel 7.x 中的有趣行为,其中急切加载的关系并不总是具有绑定。这是预期的行为吗?为什么会这样?
代码:
实际查询 Laravel 运行:
select top 100 * from task_view
select id, name from task_view where active = ? and student_id in (?, ?, ?)
select id, name from task_view where active = ? and teacher_id in (1 ,2 ,3)
模特关系:
public function studentTasks()
{
return $this->hasMany(StudentTasks::class, 'student_id', 'id');
}
public function teacherTasks()
{
return $this->hasMany(TeacherTasks::class, 'teacher_id', 'teacher_id');
}
调用代码:
TaskView::query()->with(['studentTasks', 'teacherTasks']);
加分:
我认为这可能与关系(第 3 个参数)的 localkey
是 'id'
那么值不受约束有关。
我的假设是绑定是为了防止 sql 注入,Docs 似乎证实了这一点。如果是这样,那么为什么不需要绑定关系所在模型的 ID?我假设那里仍然存在 SQL 注入问题。
我四处搜索,(Whosebug、Laracasts、Laravel 文档)没有看到有人讨论过这个问题
(我在 AppServiceProvider:boot
中使用以下代码打印出查询)
$counter = 0;
\DB::listen(function ($query) use (&$counter) {
echo 'count: '.++$counter.PHP_EOL;
// echo memory_get_usage();
echo $query->sql.PHP_EOL;
echo implode(',', $query->bindings).PHP_EOL;
});
这是 Laravel 5.7.14 中引入的更改。初始拉取请求 can be found here。从那里您可以找到更多对功能进行更新的拉取请求。
它是在需要急切加载大量记录(数千条)时作为性能增强而完成的。它没有数千个绑定参数,而是将原始 ID 直接放在查询中。最初它是为了解决 MySQL PDO 错误,但实际上所有数据库驱动程序都可以受益于没有数千个绑定参数。
之所以没有引入SQL注入漏洞是因为:
- 它仅在 id 为整数时将绑定替换为原始值,并且
- 它在将所有 ID 添加到查询之前通过整数转换运行所有 ID。
这是最终决定是使用参数还是使用原始 ID 的函数 (https://github.com/laravel/framework/blob/7.x/src/Illuminate/Database/Eloquent/Relations/Relation.php#L310-L323):
/**
* Get the name of the "where in" method for eager loading.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @return string
*/
protected function whereInMethod(Model $model, $key)
{
return $model->getKeyName() === last(explode('.', $key))
&& in_array($model->getKeyType(), ['int', 'integer'])
? 'whereIntegerInRaw'
: 'whereIn';
}
这里是 whereIntegerInRaw()
函数,它显示在添加到原始查询 (https://github.com/laravel/framework/blob/7.x/src/Illuminate/Database/Query/Builder.php#L961-L985) 之前 int
转换键:
/**
* Add a "where in raw" clause for integer values to the query.
*
* @param string $column
* @param \Illuminate\Contracts\Support\Arrayable|array $values
* @param string $boolean
* @param bool $not
* @return $this
*/
public function whereIntegerInRaw($column, $values, $boolean = 'and', $not = false)
{
$type = $not ? 'NotInRaw' : 'InRaw';
if ($values instanceof Arrayable) {
$values = $values->toArray();
}
foreach ($values as &$value) {
$value = (int) $value;
}
$this->wheres[] = compact('type', 'column', 'values', 'boolean');
return $this;
}
问题:
我注意到 Laravel 7.x 中的有趣行为,其中急切加载的关系并不总是具有绑定。这是预期的行为吗?为什么会这样?
代码:
实际查询 Laravel 运行:
select top 100 * from task_view
select id, name from task_view where active = ? and student_id in (?, ?, ?)
select id, name from task_view where active = ? and teacher_id in (1 ,2 ,3)
模特关系:
public function studentTasks()
{
return $this->hasMany(StudentTasks::class, 'student_id', 'id');
}
public function teacherTasks()
{
return $this->hasMany(TeacherTasks::class, 'teacher_id', 'teacher_id');
}
调用代码:
TaskView::query()->with(['studentTasks', 'teacherTasks']);
加分:
我认为这可能与关系(第 3 个参数)的 localkey
是 'id'
那么值不受约束有关。
我的假设是绑定是为了防止 sql 注入,Docs 似乎证实了这一点。如果是这样,那么为什么不需要绑定关系所在模型的 ID?我假设那里仍然存在 SQL 注入问题。
我四处搜索,(Whosebug、Laracasts、Laravel 文档)没有看到有人讨论过这个问题
(我在 AppServiceProvider:boot
中使用以下代码打印出查询)
$counter = 0;
\DB::listen(function ($query) use (&$counter) {
echo 'count: '.++$counter.PHP_EOL;
// echo memory_get_usage();
echo $query->sql.PHP_EOL;
echo implode(',', $query->bindings).PHP_EOL;
});
这是 Laravel 5.7.14 中引入的更改。初始拉取请求 can be found here。从那里您可以找到更多对功能进行更新的拉取请求。
它是在需要急切加载大量记录(数千条)时作为性能增强而完成的。它没有数千个绑定参数,而是将原始 ID 直接放在查询中。最初它是为了解决 MySQL PDO 错误,但实际上所有数据库驱动程序都可以受益于没有数千个绑定参数。
之所以没有引入SQL注入漏洞是因为:
- 它仅在 id 为整数时将绑定替换为原始值,并且
- 它在将所有 ID 添加到查询之前通过整数转换运行所有 ID。
这是最终决定是使用参数还是使用原始 ID 的函数 (https://github.com/laravel/framework/blob/7.x/src/Illuminate/Database/Eloquent/Relations/Relation.php#L310-L323):
/**
* Get the name of the "where in" method for eager loading.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @return string
*/
protected function whereInMethod(Model $model, $key)
{
return $model->getKeyName() === last(explode('.', $key))
&& in_array($model->getKeyType(), ['int', 'integer'])
? 'whereIntegerInRaw'
: 'whereIn';
}
这里是 whereIntegerInRaw()
函数,它显示在添加到原始查询 (https://github.com/laravel/framework/blob/7.x/src/Illuminate/Database/Query/Builder.php#L961-L985) 之前 int
转换键:
/**
* Add a "where in raw" clause for integer values to the query.
*
* @param string $column
* @param \Illuminate\Contracts\Support\Arrayable|array $values
* @param string $boolean
* @param bool $not
* @return $this
*/
public function whereIntegerInRaw($column, $values, $boolean = 'and', $not = false)
{
$type = $not ? 'NotInRaw' : 'InRaw';
if ($values instanceof Arrayable) {
$values = $values->toArray();
}
foreach ($values as &$value) {
$value = (int) $value;
}
$this->wheres[] = compact('type', 'column', 'values', 'boolean');
return $this;
}