构建一个类方法,将具有相同父类的两个类合并在一起。

huangapple go评论59阅读模式
英文:

building a class method combining two classes with same parent class

问题

以下是翻译好的部分:

"Two classes are related via a same parent" 双方通过相同的父类相关联

class Promoitem
  belongs_to :shop
  belongs_to :article, optional: true
class Cartitem
  belongs_to :article
  belongs_to :cart
class Cart
  belongs_to :shop
class Article
  has_many :cartitems
  has_many :promoitems

"An existing controller method executes logic as follows:" 现有的控制器方法执行如下逻辑:

valid_promo = Promoitem.where('shop_id = ? AND date_start <= ? AND date_end >= ? AND article_id = ?', @shop.id, Date.today, Date.today, cartitem.article_id ).first
if valid_promo
  api_price = valid_promo.price_promo
else
  api_price = cartitem.article.sell_price
end

"but that makes the controller fat and this logic could reside in the cartitem model definition." 但这使得控制器变得臃肿,这个逻辑可以存在于cartitem模型定义中。

"the goal is to write efficient model methods." 目标是编写高效的模型方法。

class Cartitem
  # scope is bad
  scope :price_promo, lambda { joins(:promoitems).where("date_start <= ? AND date_end >= ? AND article_id = ?", Date.today, Date.today, self.article_id) }
def valid_promo
  self.promoitems.price_promo
end
def api_price
  if self.valid_promo
    self.promoitems.price_promo
  else
    self.article.sell_price
  end

"The scope definition is wrong, as the relationship is via the article class." 作用域定义是错误的,因为关系是通过文章类的。

"Thus each class could have" 因此每个类都可以有

has_many :promoitems, through: :articles
has_many :cartitems, through: :articles

"But how does that translate into a proper scope that is effective at querying as the controller method - or is there a better way to do this?" 但如何将其转化为有效的查询作用域,类似于控制器方法 - 或者是否有更好的方法来实现这一目标?

英文:

Two classes are related via a same parent

class Promoitem 
  belongs_to :shop
  belongs_to :article, optional: true

class Cartitem 
  belongs_to :article
  belongs_to :cart

class Cart 
  belongs_to :shop

class Article
  has_many   :cartitems
  has_many   :promoitems

An existing controller method executes logic as follows:

valid_promo = Promoitem.where(&#39;shop_id = ? AND date_start &lt;= ? AND date_end &gt;= ? AND article_id = ?&#39;, @shop.id, Date.today, Date.today, cartitem.article_id ).first
if valid_promo
  api_price = valid_promo.price_promo
else
  api_price = cartitem.article.sell_price
end

but that makes the controller fat and this logic could reside in the cartitem model definition.
the goal is to write efficient model methods.

class Cartitem 
# scope is bad
  scope :price_promo, lambda { joins(:promoitems).where(&quot;date_start &lt;= ? AND date_end &gt;= ? AND article_id = ?&quot;, Date.today, Date.today, self.article_id) }

def valid_promo
  self.promoitems.price_promo
end

def api_price
  if self.valid_promo
    self.promoitems.price_promo
  else
    self.article.sell_price
  end 
end

The scope definition is wrong, as the relationship is via the article class. Thus each class could have

  has_many :promoitems, through: :articles 
  has_many :cartitems, through: :articles

But how does that translate into a proper scope that is effective at querying as the controller method - or is there a better way to do this?

答案1

得分: 1

你的关系非常尴尬,需要进行一些重组,因为这个陈述“...因为关系是通过文章类建立的”在技术上并不正确。实际上,这个关系是通过“article”和“cart => shop”建立的,使得直接查询非常尴尬。

话虽如此,在你目前的情况下,你应该能够使用以下内容:

class Cartitem < ApplicationRecord 
  scope :with_promo_pricing, -> { 
    promo_table = PromoItem.arel_table
    promo_join = Arel::Nodes::OuterJoin.new(promo_table,
                   promo_table
                     .create_on(
                         promo_table[:article_id].eq(arel_table[:article_id])
                         .and(promo_table[:shop_id].eq(Cart.arel_table[:shop_id])
                 ))
    joins(:cart, :article) 
      .joins(promo_join)
      .select(arel_table[Arel.star],
              Arel::Nodes::NamedFunction.new('ISNULL',
                [promo_table[:price_promo],
                Article.arel_table[:sell_price]]
              ).as('final_price')
            )
      .where(promo_items: {start_date: ..Date.today, end_date: Date.today..}) 
  } 
  def api_price
    attributes['final_price'] || self.class.where(id: self.id).with_promo_pricing.final_price
  end 
end 

这应该会产生以下SQL:

SELECT 
  cartitems.*,
  ISNULL(promoitems.price_promo,articles.sell_price) AS final_price
FROM 
  cartitems 
  INNER JOIN carts ON carts.id = cartitems.cart_id 
  INNER JOIN articles ON articles.id = cartitems.article_id
  LEFT OUTER JOIN promoitems ON promoitems.article_id = cartitems.article_id
    AND carts.shop_id = promoitems.shop_id
WHERE 
  promoitems.start_date <= '2023-03-07' AND 
  promoitems.end_date >= '2023-03-07'

你也可以在一个Cart上使用这个,我假设这应该是你的意图,例如:

@cart.cartitems.with_promo_pricing

希望这有所帮助。

英文:

Your relationship is very awkward and would greatly benefit from some restructuring because this statement "...as the relationship is via the article class." is not technically true. The relationship is actually via the "article" and the "cart => shop" making it very awkward to query directly.

That being said under your current circumstances you should be able to use the following:

class Cartitem &lt; ApplicationRecord 
  scope :with_promo_pricing, -&gt; { 
    promo_table = PromoItem.arel_table
    promo_join = Arel::Nodes::OuterJoin.new(promo_table,
                   promo_table
                     .create_on(
                         promo_table[:article_id].eq(arel_table[:article_id])
                         .and(promo_table[:shop_id].eq(Cart.arel_table[:shop_id])
                 ))
    joins(:cart, :article) 
      .joins(promo_join)
      .select(arel_table[Arel.star],
              Arel::Nodes::NamedFunction.new(&#39;ISNULL&#39;,
                [promo_table[:price_promo],
                Article.arel_table[:sell_price]]
              ).as(&#39;final_price&#39;)
            )
      .where(promo_items: {start_date: ..Date.today, end_date: Date.today..}) 
  } 
  def api_price
    attributes[&#39;final_price&#39;] || self.class.where(id: self.id).with_promo_pricing.final_price
  end 
end 

This should produce the following SQL

SELECT 
  cartitems.*,
  ISNULL(promoitems.price_promo,articles.sell_price) AS final_price
FROM 
  cartitems 
  INNER JOIN carts ON carts.id = cartitems.cart_id 
  INNER JOIN articles ON articles.id = cartitems.article_id
  LEFT OUTER JOIN promoitems ON promoitems.article_id = cartitems.article_id
    AND carts.shop_id = promoitems.shop_id
WHERE 
  promoitems.start_date &lt;= &#39;2023-03-07&#39; AND 
  promoitems.end_date &gt;= &#39;2023-03-07&#39;

You could also use this with a Cart which I assume has to be the intent e.g.

@cart.cartitems.with_promo_pricing

huangapple
  • 本文由 发表于 2023年3月7日 10:07:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/75657454.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定