10

laravelクエリビルダーを使用して、そのように結合を複製しようとしています:

LEFT JOIN content_userdata
ON content_id = content.id
AND user_id = $user_id

Eloquentを拡張するモデルで次の関数を使用して、追加の「オン」を実行できることを発見しました

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

しかし、これは 2 つの問題を引き起こします。まず、 $user_id 変数を関数に取得できません。次に、上記で行ったようにテスト目的で (int "10" に) ハードコーディングしても、Laravel はそれを ` で囲みます。つまり、必要なときに列名として解釈されることを意味します。そうではありません:

left join `content_userdata`
on `content_id` = `content`.`id`
and `user_id` = `10`

だから今、私は2つの問題を抱えています。

  1. クエリ スコープを使用している場合、結合関数に $user_id を取得できません
  2. 変数は常に列名として解釈されるため、変数を結合に送信できません。

なぜ私はこれをしたいのですか? 1つの対応は、それをどこに置くことかもしれないことに気づきました。ただし、 content_userdata テーブルにはコンテンツに対するユーザーの評価などのものが含まれているため、結合が必ずしも結果を返すとは限らないため (したがって、左の結合)、この方法で実行しようとしています。where を使用すると、content_userdata テーブルに何もない結果は返されませんが、結合に入れることができるかのように、左結合のために返されます。

とにかくLaravelでこれを達成する方法はありますか?代替手段がない場合は、Laravelを完全に変更することは明らかですが、私が考えることができる唯一の代替手段は、別のクエリでユーザーデータを取得することです.

4

5 に答える 5

7

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

この例では、変数をクロージャに渡す必要がないことに注意してください。別の方法として、この部分を完全に生で書いてみることもできます。

更新:テイラーが追加されまし joinWhereleftJoinWhere...関数結合がある場合は、クロージャー内から->whereandを使用するだけです。->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 が文字列ではなく整数であっても、引用符で囲むために使用されます。これにより、MySQL が列名として解釈する ` を使用して Laravel が自動的に停止します。

パフォーマンス:注目すべきことの 1 つは、文字列ではなく整数を使用すると、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

最初の問題: Halfの答えとして、クロージャに PHP 構文を使用する必要があります。AND user_id = $user_id2番目の問題については、クエリの一部は JOIN 句ではなく WHERE 句に属していると思います。これは、この結合関係の両方ではなく、1 つのテーブルにのみ依存しているためです。次のようなサブクエリを使用する必要があると思います。

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 に答える