web-dev-qa-db-fra.com

Portée avec rejoindre le: has_many: via l'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é une instance Users __ _current_user, je veux faire

current_user.meetings.visible

qui récupère une collection de Meetings pour laquelle l'utilisateur est un participant où la colonne hidden est false. Le plus proche que j'ai obtenu ajoute la portée suivante à la classe Meetings

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

La scope filtre la Meetings contre la table MeetingParticipations, mais il n'y a pas de jointure/condition contre la table MeetingParticipations associée à current_user.

Le problème avec ceci est que, si current_user et another_user sont tous deux participants pour une instance Meetings, un enregistrement Meetings dans le jeu de résultats sera renvoyé pour chaque participant pour lequel hidden est défini sur false. Si current_user a true défini pour hidden pour tout Meetings, si another_user est un participant à l'une de ces mêmes réunions avec hidden défini sur false, ces Meetings apparaîtront dans l'ensemble de résultats Meetings.visible.

Est-il possible d'avoir, comme je l'ai mentionné ci-dessus, une portée qui se joindra correctement à l'instance User? Si non, quelqu'un peut-il recommander une solution à cela?

60
deefour

Ceci est ma solution pour 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

78
Andrei Erdoss

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 éléments 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 faire: user1.visible_meetings et user2.visible_meetings avec différents ensembles de résultats

59
Paul Pettengill
current_user.meetings.merge(MeetingParticipations.visible)
7
woto

Je sais que cette question a reçu une réponse il y a quelque temps, mais je viens de rencontrer un problème similaire et je cherchais la meilleure façon de gérer cela. La solution acceptée est très simple, mais je pense qu’elle serait plus propre en déplaçant la portée de l’association de Users à Meeting comme il convient ci-dessous

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
  scope :hidden, -> { where('meeting_participations.hidden = ?', true) }
  scope :visible, -> { where('meeting_participations.hidden = ?', false) }
end

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

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

Avec cela, vous pouvez appeler current_user.meetings.hidden

De par sa conception, la réunion dicte maintenant ce qui la rend cachée/visible.

0
Abass Sesay

La manière propre et associative de le faire est:

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

En termes plus génériques: si vous avez une association chaînée has_many, vous pouvez définir l’association intermédiaire (through) en fusionnant la portée. Nécessite probablement Rails 4+.

Autrement, il faudrait créer une association de portée intermédiaire (probablement non désirée), comme le montre la réponse de Paul Pettengill.

0
thisismydesign

Voici un one liner:

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

C'est formidable car vous pouvez en faire une portée, une fonction ou simplement l'appeler n'importe où. Vous pouvez également ajouter d'autres restrictions à votre hachage.

0
Mirror318