RoR - 检查视图中has_many polimorphic引用的存在

时间:2015-11-17 20:25:04

标签: ruby-on-rails ruby ruby-on-rails-3

我正在制作一个包含用户,提交内容和评论的应用。我需要提交和注释都有一个Like按钮,所以经过一些研究后我终于创建了一个Like模型,使用了对用户的引用和对Submission和Comment的polimorphic引用。由于我是RoR的新手,我分享了我的代码,所以如果出现问题你可以纠正我:

guser.rb:

class Guser < ActiveRecord::Base
  has_many :likes, dependent: :destroy
end

like.rb:

class Like < ActiveRecord::Base
  belongs_to :guser
  belongs_to :likeable, polymorphic: true
end

submission.rb:

class Submission < ActiveRecord::Base
  belongs_to :guser
  has_many :comments
  ... 
  has_many :likes, as: :likeable
  has_many :likers, through: :likes, source: :user
end

comment.rb:

class Comment < ActiveRecord::Base
  ...  
  has_many :likes, as: :likeable
  has_many :likers, through: :likes, source: :user
end

迁移create_likes:

class CreateLikes < ActiveRecord::Migration
  def change
    create_table :likes do |t|
      t.integer :likeable_id
      t.string  :likeable_type
      t.references :guser, index: true, foreign_key: true
      t.timestamps null: false
    end
  end
end

现在我的问题是:如果用户尚未提交提交/评论,我只需要显示相似按钮。所以现在我需要做这样的事情:

<%if current_guser and not current_guser.likes.map(&:submission_id).include? submission.id %>
      <%= link_to(image_tag("https://upload.wikimedia.org/wikipedia/commons/thumb/7/7f/Green_equilateral_triangle_point_up.svg/1182px-Green_equilateral_triangle_point_up.svg.png", :width => 8, :height => 8), { :controller => "likes", :submission_id => submission.id,:action => "create"}, method: :post) %>

但现在我需要检查likeable_type是否提交,而likeable_id是否与提交ID匹配。并且,在评论视图中,我必须做同样的评论。我怎么做这个检查?对于具有多个属性的地图,是否有任何包含函数?

谢谢!

2 个答案:

答案 0 :(得分:0)

这个怎么样?

<% if current_guser and not current_guser.likes.
        map{|x| [x.likeable_id, x.likeable_type]).
        include? [submission.id, 'Submission'] %>

或者,您可以搜索完整的对象:

<% if current_guser and not current_guser.likes.map(&:likeable).
      include?(submission) %>

请注意,对于后一个选项,您可能希望在控制器操作中使用includes,以确保避免N+1个查询:

@user_likes = Like.includes(:likeable).where(guser_id: current_guser.id)

您可能需要考虑的另一个选项是使用哈希查找(O(1)来查找键)而不是每次查看时都运行多个include?语句(O(N)一把钥匙)。 E.g:

# app/models/guser.rb
class Guser < ActiveRecord::Base
  # @return [Hash] of the structure { ['Submission', 5] => <Like object> }
  def self.lookup_for_user(guser_id)
    # With `pluck` no active record objects are created, saving some overhead
    arr = where(guser_id: guser_id).pluck(:likeable_type, :likeable_id).map{|x| [x, true]}
    Hash[arr]
  end
end

# app/controllers/your_controller.rb
def your_action
  @user_likes_lookup = Like.lookup_for_user(current_guser.id)
end

然后你在视图中需要做的就是:

<% if current_guser and not @user_likes_lookup[['Submission', submission.id]] %>

最后请注意,随着系统的增长和每个用户的喜欢次数变大,这些都不是非常高效的方法,因为您正在加载一个人所做的所有喜欢。您可能希望考虑限制数据库查询以仅提取您在页面上呈现的提交和注释ID。

例如,您可以将Guser::lookup_for_user更改为:

# app/models/guser.rb
class Guser < ActiveRecord::Base
  # @return [Hash] of the structure { ['Submission', 5] => <Like object> }
  def self.lookup_for_user(guser_id, likeable_objects_to_find=[])
    arr = where(guser_id: guser_id, likeable: likeable_objects_to_find).pluck(:likeable_type, :likeable_id).map{|x| [x, true]}
    Hash[arr]
  end
end

然后在你的控制器中:

# app/controllers/your_controller.rb
def your_action
  @submissions = # ... || []
  @comments = # ... || []
  @user_likes_lookup = Like.lookup_for_user(current_guser.id, @submissions + @comments)
end

答案 1 :(得分:0)

我通过将以下方法添加到可爱模型(在我的案例中为帖子或评论)解决了同样的问题:

  def is_liked_by user
    if defined? user
      self.likes.find_by(user: user) # likes referes to the relation declaration: has_many :likes, :as => :likeable, :dependent => :destroy
    else 
      false
    end
  end

您应该根据自己的用户模型自定义它。

在您的视图中使用此功能检查是否有人喜欢帖子:

unless likeable.is_liked_by current_user
  # do your magic
else
  # do some other stuff (like unlike :))
end