在Laravel Eloquent中,为什么有些SQL参数没有绑定



问题:

我注意到Laravel7.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']);

附加要点:

我认为这可能与关系的localkey(第三个参数)是'id'有关,那么这些值就没有绑定。

我的假设是绑定是为了防止sql注入,文档似乎证实了这一点。如果是这样的话,那么为什么不需要绑定关系所在模型的id?我认为仍然存在SQL注入的问题。

我没有看到任何人从我周围的搜索中讨论这个,(Stackloverflow,Laracasts,Laravel docs)

(我在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中引入的更改。可以在此处找到初始拉取请求。在那里,您可以找到更多对功能进行更新的拉取请求。

当需要急切地加载大量记录(成千上万)时,这是一种性能增强。它没有数千个绑定参数,而是将原始id直接放入查询中。最初,它是为了解决MySQL PDO错误,但实际上所有数据库驱动程序都可以从没有数千个绑定参数中受益。

它没有引入SQL注入漏洞的原因是:

  1. 只有当id是整数时,它才会用原始值替换绑定,并且
  2. 在将所有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  IlluminateDatabaseEloquentModel  $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()函数,它显示键在添加到原始查询之前是int强制转换的(https://github.com/laravel/framework/blob/7.x/src/Illuminate/Database/Query/Builder.php#L961-L985):

/**
* Add a "where in raw" clause for integer values to the query.
*
* @param  string  $column
* @param  IlluminateContractsSupportArrayable|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;
}

最新更新