我正在尝试使用 laravel 查询生成器复制连接:
LEFT JOIN content_userdata
ON content_id = content.id
AND user_id = $user_id
我发现我可以在我的模型中使用扩展 Eloquent 的以下函数来执行额外的“on”操作
public function scopeJoinUserData($query, $user_id)
{
return $query->leftJoin('content_userdata', function($join)
{
$join->on('content_userdata_content_id', '=', 'content.content_id')->on('content_userdata_user_id', '=', 10);
});
}
但这会产生两个问题。首先,我无法将 $user_id 变量放入函数中,其次,即使我像上面所做的那样出于测试目的对其进行硬编码(到 int“10”),Laravel 也会将其包含在 ` 中,这意味着它应该被解释为列名不是这样的:
left join `content_userdata`
on `content_id` = `content`.`id`
and `user_id` = `10`
所以现在我有两个问题。
我为什么要这样做? 我意识到一个回应可能是将其放置在某个地方。然而,我试图这样做,因为连接可能不一定返回任何结果(因此是左连接),因为 content_userdata 表包含诸如用户对内容的评分之类的内容。如果我使用 where 那么 content_userdata 表中没有任何内容的结果将不会被返回,就好像我可以将其放入连接中一样,那么它们将由于左连接而被返回。
是否有办法在 Laravel 中实现这一目标,如果没有,还有什么替代方案,显然完全改变放弃 Laravel 是太过分了,但我能想到的唯一替代方案是在单独的查询中获取用户数据。
您需要使用
use
关键字将变量传递给闭包 - 它将变量导入范围。示例:
public function scopeJoinUserData($query, $user_id)
{
return $query->leftJoin('content_userdata', function($join) use ($user_id)
{
$join->on('content_userdata_content_id', '=', 'content.content_id')
->on('content_userdata_user_id', '=', DB::raw($user_id));
});
}
这是一个 PHP 语法相关问题,而不是 Laravel 限制!
在接受的答案中,仅在查询的
DB::raw
部分添加引号并不能完全保护它免受sql注入。只需在您的 user_id 中传递一些引号即可查看。要参数化,您可以执行以下操作:
public function scopeJoinUserData($query, $user_id)
{
return $query->leftJoin('content_userdata', function($join)
{
$join->on('content_userdata_content_id', '=', 'content.content_id')
->on('content_userdata_user_id', '=', DB::raw('?'));
}
->setBindings(array_merge($query->getBindings(),array($user_id)));
}
请注意,在此示例中,您不必将变量传递到闭包中。或者,您可以尝试编写这部分完全原始。
更新:Taylor 添加了
joinWhere
,leftJoinWhere
...如果您有函数 join,只需在闭包中使用 ->where
和 ->orWhere
。
我自己设法解决了这个问题,底部有一条注释说明了为什么它不是完全最佳的,但无论如何,这里是如何做到这一点的:
public function scopeJoinUserData($query, $user_id)
{
return $query->leftJoin('content_userdata', function($join) use ($user_id)
{
$join->on('content_userdata_content_id', '=', 'content.content_id')->on('content_userdata_user_id', '=', DB::raw('"'.$user_id.'"'));
});
}
请注意@Half Crazed 建议的“use ($user_id)”的使用。
DB::raw() 用于将 $user_id 括在引号中,即使它是整数而不是字符串。这将自动停止 Laravel 使用 ` ,这使得 MySQL 将其解释为列名。
性能:需要注意的一件事是,使用整数而不是字符串时,MySQL 查询会快得多,并且如果用引号引起来,会将其解释为字符串。我现在不担心这个,但我想如果其他人使用这个作为解决方案,我应该提到它。
为什么不直接利用关系呢?这就是像 Eloquent 这样的 ORM 的全部意义吗?
类似这样的东西;
class User extends Eloquent {
public function userdata()
{
return $this->hasOne('Userdata');
}
}
$result= User::find(1)->userdata();
编辑以表明您可以在人际关系中为所欲为
选项 1:
$place = new Place;
$array = $place->with(array('users' => function($query)
{
$query->where('user_id', $user_id);
}))->get();
var_dump($array->toArray());
或选项 2:
$place = new Place;
$array = $place->with('users')->where('user_id', $user_id)->get();
var_dump($array->toArray());
两者给出不同的结果 - 但你明白了
你的第一个问题:你应该使用 PHP 语法来闭包作为 Half 的答案。 关于你的第二个问题,我认为查询的
AND user_id = $user_id
部分不属于JOIN子句,而是属于WHERE子句,因为它只依赖于一个表,而不是这个连接关系中的两个表。我认为你应该使用这样的子查询:
public function scopeJoinUserData($query, $user_id)
{
return $query->leftJoin(\DB:raw("(SELECT * FROM content_userdata WHERE user_id = {$user_id}) AS t"), function($join)
{
$join->on('t.content_id', '=', 'content.content_id');
});
}
但是,如您所见,请确保
$user_id
变量是安全的,因为我们使用 \DB:raw
方法。