2015-06-16 10 views
6

Ho tre modelli: Utente, Commento e Voto positivo. User-to-Comment ha una relazione uno-a-molti, Commento-a-Up ha una relazione uno-a-molti e User-to-Up ha una relazione uno-a-molti.Prevenire il richiamo del modello upvote per ogni commento

Voglio fare qualcosa di simile al upvoting fatto su Stackoverflow. Pertanto, quando si aumenta/diminuisce la freccia, la freccia viene evidenziata e rimane evidenziata anche se si aggiorna la pagina o si torna alla pagina giorni/settimane dopo.

Attualmente sto facendo questo:

<% if Upvote.voted?(@user.id, comment.id) %> 
    <%= link_to '^', ... style: 'color: orange;'%> 
<% else %> 
    <%= link_to '^', ... style: 'color:black;'%> 
<% end %> 

in cui il metodo voted? assomiglia a questo:

def self.voted?(user_id, comment_id) 
    find_by(comment_id: comment_id, user_id: user_id).present? 
    end 

Quindi, se ho 10 commenti su una pagina, questo verrà caricato un upvote dal mio database 10 volte, solo per verificare se esiste!

Ci deve essere un modo migliore per farlo, ma penso che il mio cervello abbia smesso di funzionare, quindi non posso pensare a nessuno.

+1

Probabilmente non dovrebbe essere chiamata votato sul 'Upvote 'classe. Se pensi all'azione del voto, questo non è qualcosa che fa il 'commento', ma piuttosto l'utente. Vorrei spostare il metodo nella classe 'User'. – kobaltz

+0

In una nota a margine, invece di fare 'find_by(). Presente?', Potrebbe essere meglio fare 'esiste?()' –

risposta

7

Supponendo di avere rapporti impostati correttamente

# user.rb 
class User 
    has_many :upvotes 
end 

possiamo caricare i commenti, l'utente corrente e le sue upvotes:

# comments_controller.rb 
def index 
    @comments = Comment.limit(10) 
    @user = current_user 
    user_upvotes_for_comments = current_user.upvotes.where(comment_id: @comments.map(&:id)) 
    @upvoted_comments_ids = user_upvotes_for_comments.pluck(:comment_id) 
end 

e quindi modificare if condizione in vista:

# index.html.erb 
<% if @upvoted_comments_ids.include?(comment.id) %> 
    <%= link_to '^', ... style: 'color: orange;'%> 
<% else %> 
    <%= link_to '^', ... style: 'color:black;'%> 
<% end %> 

Richiederà solo 2 query DB. Spero che sia d'aiuto.

+0

Se si utilizza anche un'associazione "has_many through" e definire user -> upvotes - -> commenti, è quindi possibile eseguire lo stesso con un join e un singolo colpo al db. –

+0

Puoi anche salvare te stesso una mappa e ridurre la dimensione della query del database facendo '@commenti = Comment.limit (10) .pluck (: id)' – williamcodes

+0

@williamcodes gli oggetti commenti devono essere visualizzati nella pagina, quindi non possiamo usare 'pluck' – hedgesky

2

Se sei limitato a N commenti per pagina, allora probabilmente si può fare questo in due query utilizzando i limit e offset metodi per restituire il 1 °, 2 °, ... ith serie di N commenti per pagina ith, qualcosa come (sintassi può essere spento, Ruby non è la mia lingua principale)

comment_ids = 
    Comments.select("comment_id") 
      .where(user_id: user_id) 
      .order(post_date/comment_id/whatever) 
      .offset(per_page * (page_number - 1)) // assumes 1-based page index 
      .limit(per_page) 

Questo vi dà un elenco di comment_ids che può essere utilizzato per interrogare Upvote:

upvoted_comments = 
    Upvotes.select("comment_id") 
      .where(user_id: user_id, comment_id: comment_ids) 

Se si ordina lo comment_ids con una colonna che esiste anche in Upvote (ad es. se si ordina in base a comment_id), è possibile sostituire la query di serie Upvote con una query di intervallo.

Metti il ​​upvoted_comments in un hash e sei a posto - se il comment_id è nell'hash, allora è stato messo in vendita, altrimenti no.

0

io non sono sicuro che questo permetterà di evitare le query in eccesso in questo stato, ma forse si potrebbe includere le upvotes quando recuperare i commenti:

@comments = Comment.includes(:upvotes).where(foo: 'bar').limit(10) 

Poi nella vista:

<%= 
    link_color = comment.upvotes.map(&:user_id).include?(@user.id) ? 'orange' : 'red' 
    link_to '^', ...style: "color: #{link_color}" 
%> 
+0

Sarei nella stessa situazione colpendo il mio DB 10 volte, interrogando per l'esistenza di un oggetto. –

+0

Hai ragione. Scusate! Ho frainteso. Rivedrò il mio suggerimento .. –

+0

Mi è piaciuta la tua modifica alla vista, è più pulita di quella che avevo. –

4

Possiamo fallo nel seguente modo se vuoi che sia gestito da una singola query.

Consente assicurarsi che i rapporti sono propri

# user.rb 
class User < ActiveRecord::Base 
    has_many :comments 
    has_many :upvotes 
end 

# comment.rb 
class Comment < ActiveRecord::Base 
    belongs_to :user 
    has_many :upvotes 
end 

# upvote.rb 
class Upvote < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :comment 
end 

Poi nel controller

def index 
    current_user = User.first # current_user may come from devise or any authentication logic you have. 
    @comments = Comment.select('comments.*, upvotes.id as upvote').joins("LEFT OUTER JOIN upvotes ON comments.id = upvotes.comment_id AND upvotes.user_id = #{current_user.id}") 
end 

E in vista

# index.html.erb 
<% @comment.each do |comment| %> 
    <% link_color = comment.upvote ? 'orange' : 'black' %> 
    <%= link_to '^', ...style: "color: #{link_color}" %> 
<% end %> 
# And all of your logics ;) 
Problemi correlati