ruby-on-rails - ejemplos - ruby on rails que es
Alcance con join on: has_many: a través de asociación (6)
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
es una columna booleana extra dentro de la tabla de asociación m2m. Dada la instancia de algunos Users
current_user
, quiero hacer
current_user.meetings.visible
que recuperará una colección de Meetings
para las cuales el usuario es un participante donde la columna hidden
es false
. Lo más cercano que he conseguido es agregar el siguiente alcance a la clase Meetings
scope :visible, joins(:meeting_participations) & MeetingParticipation.visible
El scope
filtra las Meetings
comparación con la tabla MeetingParticipations
, sin embargo, no hay join / condition en la tabla MeetingParticipations
relacionada con current_user
.
El problema con esto es que si current_user
y another_user
son ambos participantes para alguna instancia de Meetings
, se devolverá un registro de Meetings
en el conjunto de resultados para cada participante que tenga el conjunto hidden
como false
. Si current_user
tiene true
conjunto para hidden
para todas las Meetings
, si another_user
es un participante en cualquiera de esas mismas reuniones con hidden
set en false
, esas Meetings
aparecerán en el conjunto de resultados Meetings.visible
.
¿Es posible tener un alcance como he mencionado anteriormente que se unirá de forma adecuada a la instancia del User
? Si no, ¿alguien puede recomendar una solución para esto?
Aquí hay un trazador de líneas:
Meeting.joins(:meeting_participations).where(meeting_participation: { hidden: false, user_id: current_user.id })
Esto es genial porque puede hacer un alcance fuera de él, una función fuera de él, o simplemente llamarlo a cualquier lugar. También puede agregar más restricciones que desee al hash.
En Rails 4, puede especificar el alcance originalmente definido en el objeto hijo en la asociación misma. Corto: no tiene que conocer las partes internas del modelo MeetingParticipation dentro del modelo de Usuario.
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
Esto le permitiría hacer: user1.visible_meetings
y user2.visible_meetings
con diferentes conjuntos de resultados
Esta es mi solución para su problema:
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
Me parece que no es sensato usar un alcance en Meeting para su propósito. Una reunión en sí misma no tiene visibilidad, pero la participación sí lo tiene. Así que sugeriría una extensión de la asociación dentro del Usuario:
class User < ActiveRecord::Base
has_many :meetings, :through => :meeting_participations do
def visible
ids = MeetingParticipation.
select(:meeting_id).
where(:user_id => proxy_owner.id, :visible => true).
map{|p| p.meeting_id}
proxy_target.where("id IN (?)", ids)
end
end
...
end
Espero que esto ayude.
También podrías hacer:
current_user.meeting_participations.visible.map(&:meeting)
current_user.meetings.merge(MeetingParticipations.visible)