neon
neon

Reputation: 2821

Custom scope on has_many, :through association (Rails 4)

CONTEXT:

In my setup Users have many Communities through CommunityUser, and Communities have many Posts through CommunityPost. If follows then, that Users have many Posts through Communities.

User.rb

has_many :community_users
has_many :communities, through: :community_users
has_many :posts, through: :communities

Given the above User.rb, Calling "current_user.posts" returns posts with one or more communities in common with current_user.

QUESTION:

I'm looking to refine that association so that calling "current_user.posts" returns only those posts whose communities are a complete subset of the current_user's communities.

So, given a current_user with community_ids of [1,2,3], calling "current_user.posts" would yield only those posts whose "community_ids" array is either 1, [2], [3], [1,2], [1,3], [2,3], or [1,2,3].

I've been researching scopes here, but can't seem to pinpoint how to accomplish this successfully...

Upvotes: 13

Views: 8994

Answers (2)

Andrew Hacking
Andrew Hacking

Reputation: 6366

You don't show the model and relationship definitions for for Community or CommunityPost so make sure you have a has_many :community_posts and a has_many :posts, through: :community_posts on your Community model and a belongs_to :community and a belongs_to :post on CommunityPost. If you don't need to track anything on ComunityPost you could just use a has_and_belongs_to_many :posts on Community and a join table communities_posts containing just the foreign keys community_id and post_id.

Assuming you have the relationships setup as I describe you should be able to just use current_user.posts to get a relation that can be further chained and which returns all posts for all communities the user is associated with when you call .all on it. Any class methods (such as scope methods) defined your Post model will also be callable from that relation so that is where you should put your refinements unless those refinements pertain to the Community or CommunityPost in which case you would put them on the Community or CommunityPost models respectively. Its actually rare to need an AR relationship extension for scopes since usually you also want to be able to refine the model independently of whatever related model you may use to get to it.

Upvotes: 0

Richard Peck
Richard Peck

Reputation: 76774

Nice question...

My immediate thoughts:

--

ActiveRecord Association Extension

These basically allow you to create a series of methods for associations, allowing you to determine specific criteria, like this:

#app/models/user.rb
has_many :communities, through: :community_users
has_many :posts, through: :communities do
   def in_community
       where("community_ids IN (?)", user.community_ids)
   end
end

--

Conditional Association

You could use conditions in your association, like so:

#app/models/user.rb
has_many :posts, -> { where("id IN (?)", user.community_ids) }, through: :communities #-> I believe the model object (I.E user) is available in the association

--

Source

I originally thought this would be your best bet, but looking at it more deeply, I think it's only if you want to use a different association name

Specifies the source association name used by has_many :through queries. Only use it if the name cannot be inferred from the association. has_many :subscribers, through: :subscriptions will look for either :subscribers or :subscriber on Subscription, unless a :source is given.


Being honest, this is one of those questions which needs some thought

Firstly, how are you storing / calling the community_ids array? Is it stored in the db directly, or is it accessed through the ActiveRecord method Model.association_ids?

Looking forward to helping you further!

Upvotes: 12

Related Questions