10

我正在尝试使用 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`

所以现在我有两个问题。

  1. 使用查询范围时,我无法将 $user_id 放入连接函数
  2. 即使可以,我也无法将变量发送到联接,因为它总是将其解释为列名

我为什么要这样做? 我意识到一种反应可能是将它放在哪里。但是我试图这样做,因为连接可能不一定会返回任何结果(因此是左连接),因为 content_userdata 表包含诸如用户对一段内容的评分之类的东西。如果我使用 where 则不会返回 content_userdata 表中没有任何内容的结果,如果我可以将其放入联接中,那么由于左联接,它们将被返回。

无论如何在 Laravel 中实现这一点,如果没有什么替代方案,显然完全改变放弃 Laravel 是最重要的,但我能想到的唯一替代方案是在单独的查询中获取用户数据。

4

5 回答 5

24

您需要使用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 限制!

于 2013-07-05T21:39:40.633 回答
7

DB::raw在接受的答案中,仅在查询部分周围添加引号并不能完全保护它免受 sql injection。只需在您的 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 补充说 joinWhereleftJoinWhere......如果你有一个函数连接,只需在闭包中使用->where->orWhere

于 2013-12-18T02:11:01.323 回答
5

我自己设法解决了这个问题,底部有一条说明为什么它不是完全最佳的,但无论如何,这里是如何做到的:

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 查询会快得多,并且如果它被包含在引号中,则会将其解释为字符串。我现在不担心这个,但我想如果其他人使用它作为解决方案,我应该提到它。

于 2013-07-07T11:49:23.240 回答
4

你为什么不只使用关系?这就是像 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());

两者都给出不同的结果 - 但你明白了

于 2013-07-07T03:10:13.003 回答
-1

你的第一个问题:你应该使用 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方法。

于 2013-07-07T15:30:15.907 回答