2014-09-22 31 views
9

So habe ich einige Probleme herauszufinden, wie man einen Feed-Stil mysql aufrufen, und ich weiß nicht, ob es ein eloquent Problem oder ein MySQL-Problem ist. Ich bin sicher, dass es in beiden möglich ist und ich brauche nur Hilfe.Laravel Eloquent Join vs. Inner Join?

So habe ich einen Benutzer und sie gehen auf ihre Feed-Seite, auf dieser Seite zeigt es Zeug von ihren Freunden (Freunde Stimmen, Freunde Kommentare, Freunde Status-Updates). Also sag ich Tom, Tim und Taylor als meine Freunde und ich muss alle ihre Stimmen, Kommentare, Statusupdates bekommen. Wie gehe ich vor? Ich habe eine Liste aller Freunde nach ID-Nummer, und ich habe Tabellen für jedes der Ereignisse (Abstimmungen, Kommentare, Statusaktualisierungen), in denen die ID gespeichert ist, um sie mit dem Benutzer zu verknüpfen. Wie kann ich all diese Informationen auf einmal erhalten, damit ich sie in einem Feed in Form von anzeigen kann.

Tim kommentiert "Cool"

Taylor Said "Woot ersten Status-Update ~!"

Taylor abgestimmt über „Bestes Wettbewerb aller Zeiten“

bearbeiten @damiani So, nachdem die Modelländerungen zu tun Ich habe Code wie diese, und es tut geben die richtigen Zeilen

$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.*']); 

Aber ich möchte sie alle auf einmal geschehen, dies liegt daran, dass mysql tausende Datensätze in der Reihenfolge sortiert 100x schneller ist, als php 3 Listen zu nehmen, sie zusammenzufassen und dann zu tun. Irgendwelche Ideen?

+0

Wie speichern Sie diese 'Liste aller Freunde nach ID-Nummer' .... befindet es sich in einer' friends' Tabelle oder ist es eine kommagetrennte Liste in einer Spalte in Ihrer 'users' Tabelle? Wenn letzteres, dann normalisieren Sie Ihre Datenbank, so dass Sie dann Modellbeziehungen verwenden können (hasManyThrough()) –

+1

Sie können eine Ansicht in Ihrer db und ein Eloquent 'Modell' dafür erstellen. Dann hol dir einfach alle Events, die du willst. Andernfalls könnten Sie 'polymorphe' Relationen verwenden: laravel.com/docs/eloquent#polymorphic-relations –

+0

Bitte fügen Sie nicht die richtige Antwort auf die ursprüngliche Frage, es wird alles verschwommen dann – challet

Antwort

17

Ich bin sicher, es gibt andere Wege, dies zu erreichen, aber eine Lösung wäre join über die Query zu verwenden, Erbauer.

Wenn Sie Tabellen in etwa so ein:

users 
    id 
    ... 

friends 
    id 
    user_id 
    friend_id 
    ... 

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

In Ihrem Benutzer Modell:

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

In Ihrem Freund Modell:

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

Dann um all das zu sammeln Stimmen für die Freunde des Benutzers mit der ID von 1, können Sie diese Abfrage ausführen:

$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 

Führen Sie die gleichen join für die comments und status_updates Tabellen. Wenn Sie möchten, dass Bewertungen, Kommentare und Statusaktualisierungen in einer chronologischen Liste enthalten sind, können Sie die resultierenden drei Sammlungen zu einem zusammenführen und dann die zusammengeführte Sammlung sortieren.


bearbeiten

Um Stimmen, Kommentare und Status-Updates in einer Abfrage, können Sie jede Abfrage aufbauen und dann Union die Ergebnisse. Leider scheint dies nicht zu arbeiten, wenn wir die Eloquent hasMany Beziehung (see comments for this question für eine Diskussion dieses Problems) verwenden, so müssen wir auf Anfragen ändern where stattdessen zu verwenden:

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

An diesem Punkt, obwohl, Unsere Abfrage wird ein bisschen haarig, so dass eine polymorphe Beziehung mit und eine zusätzliche Tabelle (wie DefiniteIntegral suggeriert) eine bessere Idee sein könnte.

+0

Vielleicht mache ich etwas falsch, aber wenn ich diesen Code ausführen, bekomme ich alle Informationen für den Benutzer mit ID 1, anstatt die Freunde Informationen. – CMOS

+0

Mit einigen Anpassungen habe ich das schließlich zum Laufen gebracht, also hast du gesagt, wenn ich Kommentare, Statusupdates usw. bekommen möchte, sollte ich getrennte Anrufe ausführen, gibt es eine Möglichkeit, alle auf einmal zu bekommen? – CMOS

+0

Ich bearbeitete die Antwort, um Code zu enthalten, der alle 3 auf einmal erhält, mit "Union". – damiani

2

Wahrscheinlich nicht das, was Sie hören möchten, aber eine "Feeds" -Tabelle wäre ein großartiger Vermittler für diese Art von Transaktion und gibt Ihnen eine denormalisierte Art, zu all diesen Daten mit einer polymorphen Beziehung zu schwenken.

Man könnte es so bauen:

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

das Futter Modell bauen wie so:

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

es dann wie bisher mit etwas halten:

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

Sie könnte das obige viel generischer/robuster machen - das ist nur zu Demonstrationszwecken.

An diesem Punkt sind Ihre Feed-Elemente einfach auf einmal abgerufen werden:

<?php 

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

Beachten Sie auch ... technisch, könnten Sie tun dies mit einer Gewerkschaftsanfrage, wenn Sie der zusätzlichen Tabelle moralisch entgegengesetzt sind. Ich persönlich denke, Denormalisierung ist eine mächtige Technik für Situationen wie diese ... die UNION-Version wird viel kniffliger sein. – DefiniteIntegral