我正在构建电子商务应用程序,您可以在其中设置用户特定于产品的价格。如果未设置特定用户的特定产品价格,则会显示默认产品价格。
它可以正常工作,但是我对目前的解决方案不满意,因此我正在寻找更有效的解决方案。
表格:
型号:
class User < ApplicationRecord
has_many :prices
end
class Product < ApplicationRecord
has_many :prices
validates :name, presence: true
def self.with_user_prices(current_user)
Product.joins(
Product.sanitize_sql_array(['LEFT OUTER JOIN prices ON prices.user_id = ?
AND products.id = prices.product_id', current_user])
).select('products.*, prices.user_price')
end
end
class Price < ApplicationRecord
belongs_to :product
belongs_to :user
end
我如何在控制器中获得所有具有用户特定价格的产品:
@products = Product.with_user_prices(current_user)
我如何在视图中显示它们:
<% @products.each do |product| %>
<%= product.user_price ? product.user_price : product.regular_price %>
<% end %>
如您所见,我当前正在加入价格表,然后在视图中显示user_price(价格表)(如果存在),否则显示regular_price(产品表)。
我很乐意在一个查询中解决所有问题,仅根据current_user保留一个具有适当值的价格列
答案 0 :(得分:2)
您可以使用SQL COALESCE函数:
class Product < ApplicationRecord
# ...
def self.with_user_prices(user)
includes(:prices).where(prices: { user_id: user.id }).select(
'products.*, COALESCE(prices.user_price, products.regular_price) as price'
)
end
end
然后您可以通过以下方式简单地使用它:
<%= product.price %>
请注意,我使用Product.with_user_prices
简化了includes
方法,由于LEFT JOIN
上存在条件,它将生成SQL prices
查询。
答案 1 :(得分:1)
请不要将此答案标记为正确,因为我基本上只是将Marek的代码和您的代码扩展到我的代码中,因为经过几次尝试,我还是得出您已经完成的操作,但我只是将其放在此处它可以帮助任何人:
app / models / product.rb
class Product < ApplicationRecord
def self.with_user_prices(user)
joins(
sanitize_sql_array([
"LEFT OUTER JOIN prices on prices.product_id = products.id AND prices.user_id = ?", user.id
])
).select(
'products.*',
'COALESCE(prices.user_price, products.regular_price) AS price_for_user'
)
end
end
控制器:
@products = Product.with_user_prices(current_user)
视图:
<% @products.each do |product| %>
<%= product.price_for_user %>
<% end %>
未经测试,但您可以尝试以下方法吗? (不确定这是比您的方法有效还是无效)
app / models / product.rb
class Product < ApplicationRecord
has_many :prices
def price_for_user(user)
prices.includes(:user).where(
users: { id: user.id }
).first&.user_price || regular_price
end
end
控制器:
# will perform LEFT OUTER JOIN (to eager load both `prices` and `prices -> user`) preventing N+1 queries
@products = Product.eager_load(prices: :user)
视图:
<% @products.each do |product| %>
<%= product.price_for_user(current_user) %>
<% end %>