Модельные отношения Rails в зависимости от количества вложенных отношений - PullRequest
0 голосов
/ 06 мая 2010

Я собираю систему сообщений для приложения rails, над которым я работаю. Я создаю его аналогично системе Facebook, поэтому сообщения сгруппированы в темы и т. Д.

Мои родственные модели:

  • MsgThread - главный контейнер нити
  • Сообщение - каждое сообщение / ответ в теме
  • Recipience - связывается с пользователем, чтобы определить, какие пользователи должны подписаться на эту тему
  • Чтение - определяет, прочитал ли пользователь конкретное сообщение

Мои отношения выглядят как

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, чтобы вернуть это, но я хотел бы посмотреть, есть ли более простой способ в первую очередь. Могу ли я передать какой-то вложенный хеш в условия вместо строки?

Есть идеи? Также, если с моей структурой что-то не так для этой функции, дайте мне знать! Спасибо !!

UPDATE:
В то время как я все еще буду благодарен за информацию о лучших возможностях, если они существуют, сейчас я начал работать над этим:

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 ]

1 голос
/ 06 мая 2010

Добавить 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 использует LEFT OUTER JOIN для :include. Следовательно, проверка 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
1 голос
/ 06 мая 2010

Возможно, вы захотите взглянуть на Arel , который может помочь в сложных SQL-запросах. Я верю (не цитируйте меня), это уже запечено в Rails3.

...