web-dev-qa-db-ja.com

Laravel Eloquent Join vs Inner Join?

そのため、フィードスタイルのmysql呼び出しを行う方法を理解するのに苦労していますが、それが雄弁な問題なのかmysqlな問題なのかわかりません。私は両方でそれが可能であると確信しています、そして、私はちょうどいくらかの助けを必要としています。

だから私にはユーザーがいて、彼らは彼らのフィードページに行く。このページでは、彼らは友人からのものを表示している(友人の投票、友人のコメント、友人のステータスの更新)。だから、私は友達としてトム、ティム、テイラーを持っていると言って、彼らの投票、コメント、ステータスの更新をすべて取得する必要があります。これについてどうすればいいですか? Id番号ごとのすべての友人のリストがあり、ユーザーにリンクするためにIdが保存されている各イベント(投票、コメント、ステータスの更新)のテーブルがあります。それでは、どのようにしてすべての情報を一度に取得して、フィードの形で表示できるのでしょうか。

ティムは「クール」とコメントしました

テイラー・サイード「初めてのステータス更新〜!」

テイラーが「史上最高の競争」に投票

@damianiを編集するモデルの変更を行った後、次のようなコードがあり、正しい行が返されます

$friends_votes = $user->friends()->join('votes', 'votes.userId', '=', 'friend.friendId')->orderBy('created_at', 'DESC')->get(['votes.*']);
$friends_comments = $user->friends()->join('comments', 'comments.userId', '=', 'friend.friendId')->orderBy('created_at', 'DESC')->get(['comments.*']);
$friends_status = $user->friends()->join('status', 'status.userId', '=', 'friend.friendId')->orderBy('created_at', 'DESC')->get(['status.*']);

しかし、mysqlが数千のレコードを順番にソートするのは、phpが3つのリストを取得してマージしてから実行するよりも100倍速いためです。何か案は?

21
CMOS

これを実現する方法は他にもあると思いますが、1つの解決策はクエリビルダーでjoinを使用することです。

次のようなテーブルを設定している場合:

users
    id
    ...

friends
    id
    user_id
    friend_id
    ...

votes, comments and status_updates (3 tables)
    id
    user_id
    ....

Userモデル:

class User extends Eloquent {
    public function friends()
    {
        return $this->hasMany('Friend');
    }
}

あなたのFriendモデル:

class Friend extends Eloquent {
    public function user()
    {
        return $this->belongsTo('User');
    }
}

次に、IDが1のユーザーの友人のすべての投票を収集するには、次のクエリを実行できます。

$user = User::find(1);
$friends_votes = $user->friends()
    ->with('user') // bring along details of the friend
    ->join('votes', 'votes.user_id', '=', 'friends.friend_id')
    ->get(['votes.*']); // exclude extra details from friends table

joinテーブルとstatus_updatesテーブルに対して同じcommentsを実行します。投票、コメント、およびstatus_updatesを1つの時系列リストに含める場合、結果の3つのコレクションを1つにマージしてから、マージされたコレクションをソートできます。


編集

1つのクエリで投票、コメント、およびステータスの更新を取得するには、各クエリを作成し、結果を結合できます。残念ながら、Eloquent hasMany関係( この問題に関するコメントを参照 その問題の議論について)を使用する場合、これは機能しないようですので、使用するクエリに変更する必要があります代わりにwhere

$friends_votes = 
    DB::table('friends')->where('friends.user_id','1')
    ->join('votes', 'votes.user_id', '=', 'friends.friend_id');

$friends_comments = 
    DB::table('friends')->where('friends.user_id','1')
    ->join('comments', 'comments.user_id', '=', 'friends.friend_id');

$friends_status_updates = 
    DB::table('status_updates')->where('status_updates.user_id','1')
    ->join('friends', 'status_updates.user_id', '=', 'friends.friend_id');

$friends_events = 
    $friends_votes
    ->union($friends_comments)
    ->union($friends_status_updates)
    ->get();

ただし、この時点では、クエリが少し毛並みを増しているため、多相的な関係と追加のテーブル(DefiniteIntegralが以下に示すように)を使用することをお勧めします。

34
damiani

おそらくあなたが聞きたいものではありませんが、「フィード」テーブルはこの種のトランザクションの優れた仲介者となり、ポリモーフィックな関係でこれらすべてのデータにピボットする非正規化された方法を提供します。

次のようにビルドできます。

<?php

Schema::create('feeds', function($table) {
    $table->increments('id');
    $table->timestamps();
    $table->unsignedInteger('user_id');
    $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
    $table->morphs('target'); 
});

次のようにフィードモデルを作成します。

<?php

class Feed extends Eloquent
{
    protected $fillable = ['user_id', 'target_type', 'target_id'];

    public function user()
    {
        return $this->belongsTo('User');
    }

    public function target()
    {
        return $this->morphTo();
    }
}

次に、次のように最新の状態に保ちます。

<?php

Vote::created(function(Vote $vote) {
    $target_type = 'Vote';
    $target_id   = $vote->id;
    $user_id     = $vote->user_id;

    Feed::create(compact('target_type', 'target_id', 'user_id'));
});

上記をはるかに汎用的/堅牢にすることができます。これは、単にデモンストレーションを目的としています。

この時点で、フィードアイテムを一度にすべて取得するのは非常に簡単です。

<?php

Feed::whereIn('user_id', $my_friend_ids)
    ->with('user', 'target')
    ->orderBy('created_at', 'desc')
    ->get();
3