Nouveau pour les deux Ruby et Rails mais je suis éduqué maintenant (ce qui ne signifie apparemment rien, haha).
J'ai deux modèles, Event et User joints via une table EventUser
class User < ActiveRecord::Base
has_many :event_users
has_many :events, :through => :event_users
end
class EventUser < ActiveRecord::Base
belongs_to :event
belongs_to :user
#For clarity's sake, EventUser also has a boolean column "active", among others
end
class Event < ActiveRecord::Base
has_many :event_users
has_many :users, :through => :event_users
end
Ce projet est un calendrier dans lequel je dois garder une trace des personnes qui s'inscrivent et rayent leur nom pour un événement donné. Je pense que le plusieurs à plusieurs est une bonne approche, mais je ne peux pas faire quelque chose comme ça:
u = User.find :first
active_events = u.events.find_by_active(true)
Étant donné que les événements n'ont PAS réellement ces données supplémentaires, le modèle EventUser en a. Et pendant que je pouvais faire:
u = User.find :first
active_events = []
u.event_users.find_by_active(true).do |eu|
active_events << eu.event
end
Cela semble être contraire à "la Rails façon". Quelqu'un peut-il m'éclairer, cela me dérange depuis longtemps ce soir (ce matin)?
Que diriez-vous d'ajouter quelque chose comme ça dans votre modèle d'utilisateur?
has_many :active_events, :through => :event_users,
:class_name => "Event",
:source => :event,
:conditions => ['event_users.active = ?',true]
Après cela, vous devriez pouvoir obtenir des événements actifs pour un utilisateur simplement en appelant:
User.first.active_events
Milan Novota a une bonne solution - mais :conditions
est désormais obsolète et le :conditions => ['event_users.active = ?',true]
bit ne semble tout simplement pas très Rails de toute façon. Je préfère quelque chose comme ça:
has_many :event_users
has_many :active_event_users, -> { where active: true }, class_name: 'EventUser'
has_many :active_events, :through => :active_event_users, class_name: 'Event', :source => :event
Après cela, vous devriez toujours pouvoir obtenir des événements actifs pour un utilisateur simplement en appelant:
User.first.active_events
Même si votre u.events n'appelle pas explicitement la table user_events, cette table est toujours incluse dans le SQL implicitement en raison des jointures nécessaires. Ainsi, vous pouvez toujours utiliser ce tableau dans vos conditions de recherche:
u.events.find(:all, :conditions => ["user_events.active = ?", true])
Bien sûr, si vous prévoyez de faire beaucoup cette recherche, assurez-vous de lui donner une association distincte comme le suggère Milan Novota, mais il n'y a pas exigence pour vous de le faire de cette façon
Eh bien, plus de responsabilités sont placées dans le modèle User
que nécessaire, et il n'y a aucune bonne raison de le faire.
Nous pouvons d'abord définir la portée dans le modèle EventUser
parce que où elle appartient réellement, comme:
class EventUser < ActiveRecord::Base
belongs_to :event
belongs_to :user
scope :active, -> { where(active: true) }
scope :inactive, -> { where(active: false) }
end
Maintenant, un utilisateur peut avoir les deux types d'événements: les événements actifs ainsi que les événements inactifs, nous pouvons donc définir la relation dans le modèle User
comme suit:
class User < ActiveRecord::Base
has_many :active_event_users, -> { active }, class_name: "EventUser"
has_many :inactive_event_users, -> { inactive }, class_name: "EventUser"
has_many :inactive_events, through: :inactive_event_user,
class_name: "Event",
source: :event
has_many :active_events, through: :active_event_users,
class_name: "Event",
source: :event
end
La beauté de cette technique est que la fonctionnalité d'être un événement actif ou inactif appartient au modèle EventUser
, et si à l'avenir la fonctionnalité doit être modifiée, elle ne sera modifiée qu'en un seul endroit: EventUser
model, et les changements seront reflétés dans tous les autres modèles.