开发者

Find by attribute of has_many

I must throw my hands up and declare I'm completely stumped on this one!

I have the following models:

Chat:
  has_many :messages

Message:
  belongs_to :chat, :counter_cache => true
  belongs_to :authorable, :polymorphic => true

User:
  has_many :messages, :as => :authorable
  has_many :chats, :through => :messages, :uniq => true

Guest:
  has_many :messages, :as => :authorable

I'm trying to write a named_scope on Chats to give me "unanswered" chats (where unanswered means there aren't any messages for that chat posted by a User)开发者_运维问答 - so far I've only managed to go round in a lot of circles!

Any help would be very much appreciated! fwiw I'm not especially attached to it being a named scope if it makes it easier (or even possible!)

Thanks, Ash


The key to named scopes is to place them in the model that they'll return. To get unanswered chats, your named scope should go in your chat model. Unfortunately you're not making it easy by looking for cases where the association is empty.

Doing that with a named scope involves a LEFT/RIGHT OUTER join and a GROUP_BY operator. It's not going to pretty and it's no better than writing your own SQL

You might find it easier to use a counter cache. However your polymorphic association may mean that a straight counter cache won't work either.

The question was a little unclear, are unanswered chats those with no messages what so ever or just those without messages with users (chats with messages authored only by guests are still considered unanswered?

If it's the former than a simple counter cache will do, other wise you'll have a little more work to do.

Common code for both cases:

Add a column to the chats table called message_count with this migration:

class AddCounterCache < ActiveRecord::Migration
  def self.up
    add_column :chats, :message_count, :integer, :default => 0
  end

  def self.down
    remove_column :chats, :message_count
  end
end

Then create the named scope in the Chat model.

class Chat < ActiveRecord::Base
  ...
  named_scope :unanswered, :conditions => {:message_count => 0}
end

Unique code for the case where an unanswered chat has 0 messages

class Message < ActiveRecord::Base
  belongs_to :chat, :counter_cache => true
end

Unique code for the case where an unanswered chat can have messages authored by guests, but not users:

We only want the counter cache to be updated in certain circumstances, so we need to override the method that ActiveRecord uses to increment the counter cache so that it only triggers when we want it to. Rails provides a handy way of renaming methods and wrapping them in others through ActiveSupport's alias_method_chain. So this code creates new methods that trigger the existing methods used to update the counter cache only in the cases where they are necessary. Then alias_method_chain is used to rename the methods so that our new methods are called in place of the ones supplied by ActiveRecord.

class Message < ActiveRecord::Base
  belongs_to :chat, :counter_cache => true

  def belongs_to_counter_cache_after_create_for_chat_with_users_only
    if authorable_type == "User"
      belongs_to_counter_cache_after_create_for_chat_without_users_only
    end
  end

  def belongs_to_counter_cache_before_destroy_for_chat_with_users_only
    if authorable_type == "User"
      belongs_to_counter_cache_before_destroy_for_chat_without_users_only
    end
  end

  alias_method_chain :belongs_to_counter_cache_before_destroy_for_chat, :users_only
  alias_method_chain :belongs_to_counter_cache_after_create_for_chat, :users_only

end

Once all that's done. Chat.unanswered will list all chats that meets your criteria. You also get the bonus of not needing a second query to fetch the number of messages in in chat.


you could define the named_scope on the has_many model itself.

Chat.messages.unanswered

Or, you could write the named_scope in SQL so you could do Chat.unanswered, but that feels like the wrong api to me.

more examples: http://blog.peelmeagrape.net/2008/6/21/named_scope-is-super-awsome-with-has_many

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜