Join Laravel 4雄辩的查询生成器-复杂的变量连接

Join Laravel 4雄辩的查询生成器-复杂的变量连接,join,laravel,left-join,laravel-4,eloquent,Join,Laravel,Left Join,Laravel 4,Eloquent,我正在尝试使用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',

我正在尝试使用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`
现在我有两个问题

  • 使用查询作用域时,我无法将$user\u id放入join函数
  • 即使可以,我也不能向联接发送变量,因为它总是将其解释为列名
  • 我为什么要这样做? 我意识到一种回应可能是把它放在一个地方。但是,我尝试这样做,因为连接可能不一定返回任何结果(因此是左连接),因为content_userdata表包含用户对某一内容的评分之类的内容。如果我使用where-then,则不会返回content_userdata表中没有任何内容的结果,如果我可以将其放入连接中,则会由于左连接而返回结果


    在拉雷维尔是否有实现这一目标的方法,如果没有,还有什么替代方案,显然,完全改变ditching 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的限制

    你为什么不利用人际关系呢?这就是一个计划的全部意义

    像这样的东西

    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());
    

    两者都会产生不同的结果,但你会明白我自己设法解决了这个问题,在底部有一个注释说明为什么它不是完全最优的,但下面是如何做到这一点:

    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查询的速度会快得多,如果使用引号将其解释为字符串。我现在不担心这一点,但我想如果其他人使用它作为解决方案,我应该提到这一点。

    您的第一个问题是:您应该使用PHP闭包语法作为问题的答案。 关于第二个问题,我认为查询的
    部分和user_id=$user_id
    不属于联接子句,而是属于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\u id
    变量是安全的,因为我们使用
    \DB:raw
    方法。

    在接受的答案中,仅在查询
    DB::raw
    部分添加引号不会完全保护它免受sql注入的影响。只需在用户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)));
    }
    
    请注意,在本例中,您不必将变量传递到闭包中。或者你可以试着写这部分


    更新
    joinWhere
    leftJoinWhere
    。。。如果您有一个函数连接,只需在闭包中使用
    ->where
    ->或where

    为修复第1点而欢呼,但第2点又如何呢?这是一个Laravel限制?在这种情况下,您需要使用
    DB::raw($user\u id)
    )如果使用此方法,我担心SQL注入。有什么方法可以防止这种情况发生吗?对于Laravel 4,请使用
    DB::getPdo()->quote($variable)
    。对于Laravel3,使用
    DB::escape($variable)
    。使用
    PDO::quote
    无法实现参数化查询。相反,请看我的答案。干杯在使用hasOne时,您是否能够设置额外的where,例如:$this->where('user\u id',$user\u id)->hasOne('Userdata')。如果没有,我不确定这是如何解决由特定用户_id选择的实际问题的。是的-您可以包括关于关系的查询-该链接并不意味着您可以按照我所说的方式或在这种情况下有帮助的方式来做?我仍然不确定你是否阅读了这个问题或正确理解了这个问题,特别是“我为什么要这样做”之后的部分,尽管如此,我现在已经发布并接受了正确的答案。加入只是一种关系。雄辩者的关系为你处理所有的连接。您可以在连接中包含查询,就像上面我编辑的答案一样。它确实做了你想要的事情,包括返回没有关系的结果——从而解决了你的“为什么我要这样做”。我已经回答了在问题中使用where的可能性,解释了为什么它需要在连接中。我也明白了这一点,并回答了我自己的问题,但显然在8个小时内我是不允许接受的。谢谢大卫,我的答案相当老了。希望用户能看到这一点。您应该以不会引入SQL注入漏洞的方式更改答案。