più join utilizzando activerecord nelle rotaie
-
03-07-2019 - |
Domanda
Sto creando un piccolo servizio di microblogging in stile Twitter in cui gli utenti possono seguire altri utenti e ricevere un feed dei loro messaggi
Ho i seguenti modelli:
class Follow < ActiveRecord::Base
belongs_to :follower, :class_name => "User"
belongs_to :followee, :class_name => "User"
end
class User < ActiveRecord::Base
has_many :follows, :foreign_key => 'follower_id',
:class_name => 'Follow'
has_many :followers, :through => :follows
has_many :followed, :foreign_key => 'followee_id',
:class_name => 'Follow'
has_many :followees, :through => :followed
has_many :messages
end
class Message < ActiveRecord::Base
belongs_to :user
end
Per ottenere un feed per l'utente corrente, voglio eseguire la seguente query SQL:
SELECT * FROM follows JOIN users JOIN messages WHERE follows.follower_id = current_user.id AND follows.followee_id = users.id AND users.id = messages.user_id;
Qual è il modo ActiveRecord corretto per farlo?
Soluzione
Non sono sicuro di quello che stai cercando, ma ecco il mio suggerimento:
Presumo che tu abbia altri scopi per quella classe Follow, altrimenti non ne vedo lo scopo.
Il modo "corretto" " (cioè il mio modo completamente soggettivo) di farlo sarebbe effettivamente qualcosa del genere:
class User < ActiveRecord::Base
has_and_belongs_to_many :followers, :foreign_key => 'followed_id',
:class_name => 'User', :association_foreign_key => 'follower_id',
:include => [:messages]
has_and_belongs_to_many :follows, :foreign_key => 'follower_id',
:class_name => 'User', :association_foreign_key => 'followed_id'
has_many :messages
end
class Message < ActiveRecord::Base
belongs_to :user
end
Quindi creare la seguente tabella:
create_table :users_users, :id => false do |t|
t.integer :followed_id
t.integer :follower_id
end
E sei pronto:
followed = User.find :first
follower = User.find :last
followed.followers << follower
followed.followers.first.messages
followed.followers.first.followers.first.messages # etc...
Ma da quello che lo faccio, vuoi mostrare tutti i messaggi di tutti i follower allo stesso tempo.
Ciò dovrebbe essere possibile aggiungendo
has_and_belongs_to_many :followed_messages, :foreign_key => 'follower_id',
:class_name => 'Message', :association_foreign_key => 'followed_id'
alla classe Utente , ma non so quanto sarebbe corretto in questo modo. O potrebbe essere possibile ottenere con estensioni di associazione, ma lì non posso davvero dare alcun esempio.
Aggiornamento:
Modificando: class_name, lo assocerà al Message.id
, non ci ho pensato, quindi non sarà corretto in questo modo.
Quindi l'unico "simpatico" l'opzione è di passare attraverso la classe User come nel primo esempio. Le uniche altre opzioni che posso vedere sono le estensioni di associazione (di cui non posso darti un esempio) o forse usando un'istruzione finder.
has_many :followed_messages, :class_name => 'Message',
:finder_sql => 'select * from messages where user_id in(select followed_id from users_users where follower_id = #{id})'
Probabilmente devi personalizzare quell'istruzione sql per far funzionare tutto, ma almeno dovresti ottenere l'immagine :)
Altri suggerimenti
La disposizione di Keijro funzionerebbe meglio, anche se se hai bisogno della tabella Follow, puoi eseguire la query SQL che hai specificato come segue:
Follow.all(:joins => { :messages, :users }, :conditions => { "follows.follower_id" => current_user.id, "follows.followee_id" => "users.id", "users.id" => "messages.user_id"} )