如何为多态关联执行有点复杂的ActiveRecord范围查询?

pat*_*ick 3 activerecord ruby-on-rails ruby-on-rails-3

所以,我有一个Notification多态的模型,我希望能够过滤掉notifiable_type Comment那里的通知comment.user == current_user.换句话说,我想要所有通知记录 - 除了那些引用当前用户发表的评论的记录.

class Notification

  belongs_to :notifiable, :polymorphic => true

  scope :relevant, lambda { |user_id|
    find(:all, :conditions => [
      "notifiable_type != 'Comment' OR (notifiable_type = 'Comment' AND " <<
        "comments.user_id != ?)",
      user_id ],
      :include => :comments
    )
  }

end
Run Code Online (Sandbox Code Playgroud)

我不明白的是我需要做什么才能获得评论?我需要告诉ActiveRecord外部加入评论模型notifiable_id.

Ada*_*sek 5

首先,不推荐使用带参数的lambda范围.改为使用类方法:

class Notification
  belongs_to :notifiable, polymorphic: true

  def self.relevant(user_id)
    # ...
  end
end
Run Code Online (Sandbox Code Playgroud)

我通常将范围功能移动到他们自己的模块中,但您可以将其保留在那里.

接下来,find(:all)不推荐使用:conditions.我们现在使用ActiveRelation查询.

遗憾的是,ActiveRecord::RelationAPI不够强大,无法满足您的需求,因此我们必须转而使用ARel.有点棘手,但出于安全原因,你肯定不想做字符串替换.

class Notification
  belongs_to :notifiable, polymorphic: true

  def self.relevant(user_id)
    n, c = arel_table, Comment.arel_table
    predicate = n.join(c).on(n[:notifiable_id].eq(c[:id]))

    joins( predicate.join_sql ).
    where{ ( notifiable_type != 'Comment' ) | 
      (( notifiable_type == 'Comment' ) & ( comments.user_id == my{user_id} ))
    }
  end
end
Run Code Online (Sandbox Code Playgroud)

我在这里使用了ARel和Squeel的组合.Squeel非常好,它应该是Rails的核心功能.我试着写那个没有Squeel的子句,但是我放弃了这么困难.

如果没有你的项目方便,很难测试这样的东西,但希望这至少应该让你更接近.