模型关联回调

Model associations callbacks

我想在我的应用程序中添加评论部分。更具体地说,用户可以为商店留下评论,然后商店可以回复该评论。但我不确定模型关联和审查 table 迁移是否正确。

class User < ActiveRecord::Base
  has_many :reviews
end

class Review < ActiveRecord::Base
  belongs_to :user
end

class ReviewReply < ApplicationRecord
  belongs_to :user, optional: true
  belongs_to :review, optional: true
end


class Shop < ActiveRecord::Base
  has_many :reviews
end

class CreateReviews < ActiveRecord::Migration[6.0]
  def change
    create_table :reviews do |t|
      t.text :body
      t.integer :rating
      t.references :shop, null: false, foreign_key: true
      t.references :user, null: false, foreign_key: true

      t.timestamps
    end
  end
end

在您的迁移中,reviewable 的多态关系设置不正确 - reviewable_type 上的唯一索引将阻止添加多条记录,最好使用

t.references :reviewable, polymorphic: true

在现代 rails 中,它默认在两列上添加非唯一索引(您可以使用 index: true 明确表示,但无论如何这都不同于单独在每一列上添加两个单独的索引)

而且您很可能希望您的唯一索引包含用户 ID,以便每个用户都可以对每个可审核的内容进行一次审核:

t.index [:reviewable_type, :reviewable_id, :user_id], unique: true, name: 'idx_unique_user_review'

同时包含 shopreviewable 的原因尚不清楚,但这取决于您的应用程序和目标。如果商店本身是 reviewable(如 has_many :reviews, as: :reviewable 所建议)- 那么 shop 参考是无用的。但事实上,如果您不打算扩展对商店以外的其他内容的评论 - 现在使用非多态参考会更容易。

在大型应用程序中,相关事物最好有共同的名称前缀,因此 ReviewReply 模型名称更好。 belongs_to :review 估计不是可选的,什么都不回复很奇怪。而且它很可能有 belongs_to :shop(也不是可选的)而不是 user,因为商店是回复的人。

如果它是一个真正的应用程序(= 不仅仅是您正在玩弄的东西),我会更改一些东西:

  • 我会从 ReviewReply 中的两个关联中删除 optional: true,因为如果他们没有作者或没有关联,回复就没有意义(数据方面)评论。
  • 我还将 review_replies 中的这两列设置为 null: false
  • 您应该考虑添加一些删除级联(在模型中添加 dependent: :some_action 或在数据库列上使用 on_delete: :some_action – 我推荐后者):
    • 删除评论时删除评论回复?
    • 删除用户后删除评论回复? (或者只是将其设置为 NULL,然后在 UI 中显示“已删除的用户”?)
    • 在删除商店时删除评论?
    • 删除用户时删除评论? (或者只是将其设置为 NULL,然后在 UI 中显示“已删除的用户”?)

架构:

1。计算评分

控制台:

rails g migration add_rating

迁移:

  def change
    add_column :shops, :average_rating, :integer, default: 0, null: false
    add_column :reviews, :rating, :integer, default: 0, null: false
  end

user.rb

has_many :reviews

shop.rb

  has_many :reviews

  def update_rating
    if reviews.any? && reviews.where.not(rating: nil).any?
      update_column :average_rating, reviews.average(:rating).round(2).to_f
    else
      update_column :average_rating, 0
    end
  end

review.rb

  belongs_to :user
  belongs_to :shop
  has_one :review_reply

  after_save do
    unless rating.nil? || rating.zero?
      shop.update_rating
    end
  end

  after_destroy do
    shop.update_rating
  end

review_reply.rb

  belongs_to :review

2。回复评论

views/reviews/show.html.erb:

<% unless @review.review_reply.present? %>
  <%= link_to "Write a Reply", new_review_reply_path(review_id: @review.id) %>
<% end %>

views/review_replies/form.html.erb

= f.input :review_id, input_html: {value: params[:review_id]}, as: :hidden