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

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

building a class method combining two classes with same parent class

问题

以下是翻译好的部分:

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

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

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

  1. 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
  2. if valid_promo
  3. api_price = valid_promo.price_promo
  4. else
  5. api_price = cartitem.article.sell_price
  6. 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." 目标是编写高效的模型方法。

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

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

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

  1. has_many :promoitems, through: :articles
  2. 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

  1. class Promoitem
  2. belongs_to :shop
  3. belongs_to :article, optional: true
  4. class Cartitem
  5. belongs_to :article
  6. belongs_to :cart
  7. class Cart
  8. belongs_to :shop
  9. class Article
  10. has_many :cartitems
  11. has_many :promoitems

An existing controller method executes logic as follows:

  1. 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
  2. if valid_promo
  3. api_price = valid_promo.price_promo
  4. else
  5. api_price = cartitem.article.sell_price
  6. 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.

  1. class Cartitem
  2. # scope is bad
  3. 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) }
  4. def valid_promo
  5. self.promoitems.price_promo
  6. end
  7. def api_price
  8. if self.valid_promo
  9. self.promoitems.price_promo
  10. else
  11. self.article.sell_price
  12. end
  13. end

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

  1. has_many :promoitems, through: :articles
  2. 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”建立的,使得直接查询非常尴尬。

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

  1. class Cartitem < ApplicationRecord
  2. scope :with_promo_pricing, -> {
  3. promo_table = PromoItem.arel_table
  4. promo_join = Arel::Nodes::OuterJoin.new(promo_table,
  5. promo_table
  6. .create_on(
  7. promo_table[:article_id].eq(arel_table[:article_id])
  8. .and(promo_table[:shop_id].eq(Cart.arel_table[:shop_id])
  9. ))
  10. joins(:cart, :article)
  11. .joins(promo_join)
  12. .select(arel_table[Arel.star],
  13. Arel::Nodes::NamedFunction.new('ISNULL',
  14. [promo_table[:price_promo],
  15. Article.arel_table[:sell_price]]
  16. ).as('final_price')
  17. )
  18. .where(promo_items: {start_date: ..Date.today, end_date: Date.today..})
  19. }
  20. def api_price
  21. attributes['final_price'] || self.class.where(id: self.id).with_promo_pricing.final_price
  22. end
  23. end

这应该会产生以下SQL:

  1. SELECT
  2. cartitems.*,
  3. ISNULL(promoitems.price_promo,articles.sell_price) AS final_price
  4. FROM
  5. cartitems
  6. INNER JOIN carts ON carts.id = cartitems.cart_id
  7. INNER JOIN articles ON articles.id = cartitems.article_id
  8. LEFT OUTER JOIN promoitems ON promoitems.article_id = cartitems.article_id
  9. AND carts.shop_id = promoitems.shop_id
  10. WHERE
  11. promoitems.start_date <= '2023-03-07' AND
  12. promoitems.end_date >= '2023-03-07'

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

  1. @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:

  1. class Cartitem &lt; ApplicationRecord
  2. scope :with_promo_pricing, -&gt; {
  3. promo_table = PromoItem.arel_table
  4. promo_join = Arel::Nodes::OuterJoin.new(promo_table,
  5. promo_table
  6. .create_on(
  7. promo_table[:article_id].eq(arel_table[:article_id])
  8. .and(promo_table[:shop_id].eq(Cart.arel_table[:shop_id])
  9. ))
  10. joins(:cart, :article)
  11. .joins(promo_join)
  12. .select(arel_table[Arel.star],
  13. Arel::Nodes::NamedFunction.new(&#39;ISNULL&#39;,
  14. [promo_table[:price_promo],
  15. Article.arel_table[:sell_price]]
  16. ).as(&#39;final_price&#39;)
  17. )
  18. .where(promo_items: {start_date: ..Date.today, end_date: Date.today..})
  19. }
  20. def api_price
  21. attributes[&#39;final_price&#39;] || self.class.where(id: self.id).with_promo_pricing.final_price
  22. end
  23. end

This should produce the following SQL

  1. SELECT
  2. cartitems.*,
  3. ISNULL(promoitems.price_promo,articles.sell_price) AS final_price
  4. FROM
  5. cartitems
  6. INNER JOIN carts ON carts.id = cartitems.cart_id
  7. INNER JOIN articles ON articles.id = cartitems.article_id
  8. LEFT OUTER JOIN promoitems ON promoitems.article_id = cartitems.article_id
  9. AND carts.shop_id = promoitems.shop_id
  10. WHERE
  11. promoitems.start_date &lt;= &#39;2023-03-07&#39; AND
  12. 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.

  1. @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:

确定