2014-09-22 19 views
9

Quindi ho qualche problema a capire come eseguire una chiamata mysql in stile feed e non so se si tratta di un problema eloquente o di un problema mysql. Sono sicuro che sia possibile in entrambi e ho solo bisogno di aiuto.Laravel Eloquent Join vs Inner Join?

Quindi ho un utente e vanno alla loro pagina di feed, in questa pagina mostra cose dai loro amici (voti di amici, commenti di amici, aggiornamenti di stato di amici). Quindi dire che ho Tom, Tim e Taylor come i miei amici e ho bisogno di ottenere tutti i loro voti, commenti, aggiornamenti di stato. Come faccio a fare questo? Ho una lista di tutti gli amici per numero di identificazione, e ho tabelle per ciascuno degli eventi (voti, commenti, aggiornamenti di stato) che hanno l'ID memorizzato in esso per ricollegarsi all'utente. Quindi, come posso ottenere tutte queste informazioni contemporaneamente in modo che possa visualizzarle in un feed sotto forma di.

Tim ha commentato: "Cool"

Taylor ha detto "Woot primo aggiornamento di stato ~!"

Taylor Votato "concorso Migliore mai"

Modifica @damiani Così, dopo aver fatto il modello cambia Ho codice come questo, e lo fa ritornare le righe corrette

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

ma vorrei tutto per succedere in una volta, questo perché mysql che ordina migliaia di record in ordine è 100 volte più veloce di php che prende 3 liste, unendole e poi facendole. Qualche idea?

+0

Come stai memorizzando questo 'elenco di tutti gli amici per numero ID' .... si trova in una tabella' amici' o è un elenco separato da virgole in una colonna nella tabella 'utenti'? In quest'ultimo caso, normalizzare il database in modo da poter utilizzare le relazioni del modello (hasManyThrough()) –

+1

È possibile creare una vista nel db e un 'modello Eloquent per esso. Quindi prendi tutti gli eventi che desideri. Altrimenti potresti usare le relazioni 'polimorfiche': laravel.com/docs/eloquent#polymorphic-relations –

+0

per favore non aggiungere la risposta giusta alla domanda originale, diventa tutto sfocato quindi – challet

risposta

17

Sono sicuro che ci sono altri modi per ottenere questo risultato, ma una soluzione potrebbe essere quella di utilizzare join attraverso il Query Costruttore.

Se si dispone di tavoli impostare qualcosa di simile:

users 
    id 
    ... 

friends 
    id 
    user_id 
    friend_id 
    ... 

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

Nella tua utente modello:

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

Nella tua Amico modello:

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

Poi , per raccogliere tutto il numero di voti per gli amici del utente con l'id di 1, si potrebbe eseguire questa query:

$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 

eseguire lo stesso join per le tabelle comments e status_updates. Se desideri che voti, commenti e status_updates siano inclusi in un elenco cronologico, puoi unire le tre raccolte risultanti in una e quindi ordinare la raccolta unita.


Modifica

Per ottenere voti, i commenti e gli aggiornamenti di stato in una query, si potrebbe costruire ogni query e quindi il sindacato dei risultati. Purtroppo, questo non sembra funzionare se si usa l'Eloquente hasMany rapporto (see comments for this question per una discussione di questo problema) e quindi abbiamo dovuto modificare per le query da utilizzare where invece:

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

A questo punto, però, la nostra query diventa un po 'pelosa, quindi una relazione polimorfica con una tabella aggiuntiva (come DefiniteIntegral suggerisce di seguito) potrebbe essere un'idea migliore.

+0

Forse sto facendo qualcosa di sbagliato, ma se eseguo questo codice finisco per ottenere tutte le informazioni per l'utente con ID 1, piuttosto che le informazioni degli amici. – CMOS

+0

Con alcuni aggiustamenti, alla fine ho funzionato, quindi hai detto se volevo ricevere commenti, aggiornamenti di stato, ecc. Dovrei eseguire chiamate separate, c'è un modo per ottenerle tutte insieme? – CMOS

+0

Ho modificato la risposta per includere il codice che avrebbe ottenuto tutti e 3 in una volta, usando 'union'. – damiani

2

Probabilmente non è quello che vuoi sentire, ma una tabella "feeds" sarebbe un ottimo intermediario per questo tipo di transazione, offrendoti un modo denormalizzato di ruotare su tutti questi dati con una relazione polimorfica.

Si potrebbe costruire in questo modo:

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

costruire il modello di alimentazione in questo modo:

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

Poi tenerlo aggiornato con qualcosa di simile:

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

È potrebbe rendere quanto sopra molto più generico/robusto - questo è solo a scopo dimostrativo.

A questo punto, i vostri elementi del feed sono davvero facili da recuperare tutto in una volta:

<?php 

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

Nota anche ... tecnicamente, si potrebbe fare questo con una query di unione, se sei moralmente contrario alla tabella aggiuntiva. Personalmente, penso che la denormalizzazione sia una tecnica potente per situazioni come questa ... la versione UNION sarà molto più critica. – DefiniteIntegral