74 votes

Scope with join on :has_many :through association

class Users < ActiveRecord::Base
  has_many :meetings, :through => :meeting_participations
  has_many :meeting_participations
end

class Meetings < ActiveRecord::Base
  has_many :users, :through => :meeting_participations
  has_many :meeting_participations
end

class MeetingParticipations < ActiveRecord::Base
  belongs_to :user
  belongs_to :meeting

  scope :hidden, where(:hidden => true)
  scope :visible, where(:hidden => false)
end

hidden est une colonne booléenne supplémentaire dans la table d'association m2m. Étant donné que certains Users instance current_user Je veux faire

current_user.meetings.visible

qui récupérera une collection de Meetings pour lequel l'utilisateur est un participant où le hidden La colonne est false . Le résultat le plus proche que j'ai obtenu est l'ajout du champ d'application suivant à l'élément Meetings classe

scope :visible, joins(:meeting_participations) & MeetingParticipation.visible

En scope filtre les Meetings contre la MeetingParticipations mais il n'y a pas de jointure/condition par rapport à la table MeetingParticipations tableau relatif à current_user .

Le problème est que si current_user y another_user sont tous deux participants pour certains Meetings une instance Meetings dans l'ensemble des résultats sera renvoyée pour chaque participant qui a hidden fixé à false . Si current_user a true fixé pour hidden pour tous Meetings si another_user participe à l'une ou l'autre de ces mêmes réunions avec hidden fixé à false , ces Meetings apparaîtra dans le Meetings.visible l'ensemble des résultats.

Est-il possible d'avoir un champ d'application, comme je l'ai mentionné ci-dessus, qui se joindrait correctement au champ d'application User instance ? Si ce n'est pas le cas, quelqu'un peut-il me recommander une solution ?

102voto

Andrei Erdoss Points 423

Voici ma solution à votre problème :

class User < ActiveRecord::Base
  has_many :meeting_participations
  has_many :meetings, :through => :meeting_participations do
   def visible
     where("meeting_participations.visible = ?", true)
   end
  end
end

@user.meetings.visible

0 votes

Merci pour cette réponse, qui est sans aucun doute la plus simple.

2 votes

Quelqu'un a-t-il une suggestion pour ajouter un chargement rapide dans ce contexte ? Par exemple, si une réunion appartient à un lieu et que je veux les charger avec empressement ?

0 votes

Je suppose donc que cela fonctionne : includes([:assoc1, :assoc2]).where....geez Je pensais avoir essayé cela avant de commenter.

70voto

Paul Pettengill Points 1485

Dans Rails 4, vous pouvez spécifier la portée définie à l'origine dans l'objet enfant dans l'association elle-même. En bref : vous n'avez pas besoin de connaître les aspects internes du modèle MeetingParticipation dans le modèle User.

class User < ActiveRecord::Base
  has_many :meeting_participations
  has_many :meetings, :through => :meeting_participations
  has_many :visible_participations, -> { visible }, :class_name => 'MeetingParticipation'
  has_many :visible_meetings, :source => :meeting, :through => :visible_participations
end

class Meeting < ActiveRecord::Base
  has_many :meeting_participations
  has_many :users, :through => :meeting_participations
end

class MeetingParticipation < ActiveRecord::Base
  belongs_to :user
  belongs_to :meeting

  scope :hidden, -> { where(:hidden => true) }
  scope :visible, -> { where(:hidden => false) }
end

Cela vous permettrait de le faire : user1.visible_meetings y user2.visible_meetings avec différents ensembles de résultats

7 votes

J'aime bien cette solution, mais pour qu'elle fonctionne, je dois aussi ajouter :source à l'élément :visible_meetings has_many :visible_meetings, class_name : 'Meeting', through : :visible_participations, source : :meeting

0 votes

Faut-il remplacer "-> { visible }" par le symbole "-> { :visible }" ou le code ci-dessus fonctionne-t-il tel quel ?

2 votes

@chaostheory désolé pour la réponse tardive, mais en l'état, ce n'est pas un symbole dans ce contexte.

10voto

thisismydesign Points 751

La façon la plus propre et la plus associative de procéder est la suivante :

has_many :visible_meetings, -> { merge(MeetingParticipations.visible) },
  :source => :meeting, :through => :meeting_participations

En termes plus génériques, si vous avez une chaîne has_many vous pouvez définir le champ d'application de l'association intermédiaire ( through ) en fusionnant les champs d'application. Requiert probablement Rails 4+.

Sinon, il faudrait le faire en créant une association intermédiaire (probablement indésirable), comme indiqué dans la réponse de @Paul Pettengill.

7voto

woto Points 364
current_user.meetings.merge(MeetingParticipations.visible)

4voto

Mirror318 Points 307

En voici un exemple :

Meeting.joins(:meeting_participations).where(meeting_participation: { hidden: false, user_id: current_user.id })

C'est une excellente chose car vous pouvez en faire un champ d'application, une fonction ou simplement l'appeler n'importe où. Vous pouvez également ajouter toutes les restrictions que vous souhaitez au hash.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X