Rails模型关系取决于嵌套关系的数量

时间:2010-05-06 18:07:01

标签: ruby-on-rails messaging orm

我正在为我正在开发的rails应用程序组建一个消息传递系统。 我正在以与facebook系统类似的方式构建它,因此消息被分组为线程等。

我的相关模型是:

  • MsgThread - 主题的主要容器
  • 消息 - 线程中的每条消息/回复
  • 收件人 - 与用户联系以定义哪些用户应订阅此帖子
  • 读取 - 确定用户是否已阅读特定消息

我的人际关系看起来像是

class User < ActiveRecord::Base
    #stuff...
    has_many :msg_threads, :foreign_key => 'originator_id' #threads the user has started
    has_many :recipiences
    has_many :subscribed_threads, :through => :recipiences, :source => :msg_thread #threads the user is subscribed to
end

class MsgThread < ActiveRecord::Base
    has_many :messages
    has_many :recipiences
    belongs_to :originator, :class_name => "User", :foreign_key => "originator_id"
end

class Recipience < ActiveRecord::Base
    belongs_to :user
    belongs_to :msg_thread
end

class Message < ActiveRecord::Base
    belongs_to :msg_thread
    belongs_to :author, :class_name => "User", :foreign_key => "author_id"
end

class Read < ActiveRecord::Base
   belongs_to :user
   belongs_to :message
end

我想在用户中创建一个新的选择器,如:

has_many :updated_threads, :through => :recipiencies, :source => :msg_thread, :conditions => {THREAD CONTAINS MESSAGES WHICH ARE UNREAD (have no 'read' models tying a user to a message)}

我正在考虑编写一个带有多个连接的长条件,或者可能写一个给模型一个updated_threads方法来返回它,但我想先看看是否有更简单的方法。我能够将某种嵌套哈希传递给条件而不是字符串吗?

有什么想法吗?此外,如果我的结构存在根本性的错误,请告诉我!谢谢!

更新
虽然如果它们存在,我仍然会欣赏更好的可能性,这就是我现在所做的工作:

class User < ActiveRecord::Base
    # stuff...
    def updated_threads
        MsgThread.find_by_sql("
            SELECT msg_threads.* FROM msg_threads
            INNER JOIN messages ON messages.msg_thread_id = msg_threads.id
            INNER JOIN recipiences ON recipiences.msg_thread_id = msg_threads.id
            WHERE (SELECT COUNT(*) FROM `reads` WHERE reads.message_id = messages.id AND reads.user_id = #{self.id}) = 0
            AND (SELECT COUNT(*) FROM recipiences WHERE recipiences.user_id = #{self.id} AND recipiences.msg_thread_id = msg_threads.id) > 0
        ")
    end
end

似乎工作正常!

还要检查是否读取了特定的线程(和消息):

class Message < ActiveRecord::Base
    # stuff...
    def read?(user_id)
        Read.exists?(:user_id => user_id, :message_id => self.id)
    end
end

class MsgThread < ActiveRecord::Base
    # stuff...
    def updated?(user_id)
        updated = false
        self.messages.each { |m| updated = true if !m.read?(user_id)  }
        updated
    end
end

有任何改善这方面的建议吗?

2 个答案:

答案 0 :(得分:1)

您可能需要查看Arel,这有助于复杂的SQL查询。我相信(不要引用我)这已经融入了Rails3。

答案 1 :(得分:1)

将named_scope添加到MsgThread模型:

class MsgThread < ActiveRecord::Base
  named_scope :unread_threads, lambda { |user|
    {
    :include => [{:messages=>[:reads]}, recipiencies],
    :conditions => ["recipiences.user_id = ? AND reads.message_id IS NULL",
                     user.id],
    :group => "msg_threads.id"
    }}    
end

注意: Rails使用:include的LEFT OUTER JOIN。因此IS NULL检查有效。

现在您可以执行以下操作:

MsgThread.unread_threads(current_user)

第二部分可以写成:

class Message
  has_many :reads
  def read?(usr)
    reads.exists?(:user_id => usr.id)
  end
end

class MsgThread < ActiveRecord::Base
  def updated?(usr)
    messages.first(:joins => :reads, 
                   :conditions => ["reads.user_id = ? ", usr.id]
    ) != nil
  end
end