在 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注入漏洞是因为:

  1. 它仅在 id 为整数时将绑定替换为原始值,并且
  2. 它在将所有 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;
}