2

このスコープまたはメソッドをどのように作成すればよいか混乱しています。私には次の関連があります。

モデル

class User
  has_many :prices
  has_many :products, :through => :prices
  has_many :subscriptions, :foreign_key => :subscriber_id
end

class Product
  has_many :prices
  has_many :users, :through => :prices
end

class Price
  # Table columns => :product_id, :cost, :user_id
  belongs_to :user
  belongs_to :product
  belongs_to :store
  has_many :subscriptions, :as => :subscribable
end

class Subscription
  # Table columns => :product_id, :cost, :subscriber_id, :subscribable_id
  # :subscribable_type
  belongs_to :subscriber, :class_name => "User"
  belongs_to :subscribable, :polymorphic => true
  validates_uniqueness_of :subscribable_id, :scope => 
                        [ :subscriber_id, :subscribable_type]
end

したがって、メソッドは次のようになります。

class Price

def self.lower_price
  if self.product_id == self.subscription.product_id
     if self.cost < self.subscription.cost
     end
  end
end

end

このメソッドが行うことを想定しているのは、サブスクリプションフィールドと比較して、より低いかどうかを確認しながら、と同じにUserProducts属するのより低い価格のみを表示することです。ProductSubscriptionprice

私はこれを正しくやっていますか?何を修正する必要がありますか?


編集

class Price < ActiveRecord::Base
  scope :for_product, lambda { |product_id| where( :product_id => product_id) }
  scope :cheaper, lambda { |cost| where(["prices.cost < :cost", { :cost => cost } ] ) }
end

class Subscription < ActiveRecord::Base

  def cheaper_prices
    Price.for_product(product_id).cheaper(cost)
  end
end

PrivatePagesController:

def watch
 @prices = Price.cheaper_prices.paginate(:page => params[:page], :per_page => 20).order('purchase_date DESC')
end

This gives me the error:

NoMethodError in PrivatePagesController#watch

undefined method `cheaper_prices' for #<Class:0x6f99210>
4

3 に答える 3

1

まず、UserProduct テーブルにわかりやすい名前を付ける必要があります。リンクテーブル以外のセマンティクスが何であるかはわかりません。

Product または UserProduct へのサブスクライブについても、いくつかの混乱があります。ポリモーフィック アソシエーションが表示されますが、多少の混乱があると思われます。

Subscription には product_id があることに気付きました。これにより、Subscription は、subscribable に属するのではなく、またはそれに加えて、Product に属していることがわかります。

そのため、最初にデザインをクリーンアップする必要がある場合があります。

ただし、それがあなたが望むものであるという信念に基づいてあなたの条件を受け入れることができると仮定すると、SQLで望むのは

SELECT cheaper.*
FROM user_products
  INNER JOIN subscriptions ON subscribable_type = 'UserProduct'
                           AND subscriptions.subscribable_id = user_products.id
  INNER JOIN user_products cheaper ON cheaper.product_id = subscriptions.product_id
WHERE cheaper.price < user_products.price

これにより、全体的に見つけられるすべての安い価格のレポートが得られます。特定の user_products レコードのすべての安い価格について、特定の ID に対するものであるという条件を含める必要があります。

次に、これを ActiveRecord で機能させるために、select をクラスのテーブルに配置したいので、SQL を次のように変換しましょう。

SELECT user_products.*
FROM user_products
  INNER JOIN subscriptions ON user_products.product_id = subscriptions.product_id
  INNER JOIN user_products target ON subscribable_type = 'UserProduct'
                           AND subscriptions.subscribable_id = target.id
WHERE user_products.price < target.price
  AND target.id = ?

これで、ActiveRecord を呼び出す準備が整いました。

ActiveRecord が関連付けから結合を形成できるかどうかはわかりません。Rails 2.3 API には文字列が必要であることは知っています。したがって、スコープは次のようになります。

 class UserProduct
   ...

   #defines UserProduct.cheaper(user_product_id)
   scope :cheaper, lambda do |user_product_id|
     join(%Q{INNER JOIN subscriptions
             ON user_products.product_id = subscriptions.product_id
             INNER JOIN user_products target
             ON subscribable_type = 'UserProduct'
             AND subscriptions.subscribable_id = target.id}).
    where("cheaper.price < user_products.price").
    where(["target.id = :target_id", { :target_id => user_product_id } ] )
  end

  #defines user_product.cheaper
  def cheaper
    UserProduct.cheaper(id)
  end
...
于 2012-04-11T22:06:51.800 に答える
1

あなたは、ユーザーが見つけた価格を入力し、より安い価格を見つけるために購読する Web サイトを作成していると思いました。UserProduct エンティティの名前を Price に変更します。

サブスクライバーが製品をサブスクライブするか、価格をサブスクライブするかはあいまいです。それを片付ければ、ポリモーフィックな関連付けが単純化される可能性があります。彼らが特定の価格の製品を購読するとしましょう。次に、次のものが必要です。

class Price
  # Table columns => :product_id, :price, :user_id
  belongs_to :finder, :class_name => "User"
  belongs_to :product
  belongs_to :store

  scope for_product, lambda { |product_id| where(:product_id => product_id)
  scope cheaper, lambda { |price| where([ "prices.price < :price", {:price => price} ] }
end

class Subscription
  # Table columns => :product_id, :price, :subscriber_id
  belongs_to :subscriber, :class_name => "User"
  belongs_to :product
  validates_uniqueness_of :subscribable_id, :scope => 
                        [ :subscriber_id, :subscribable_type]

  def cheaper
    Price.for_product(product_id).cheaper(price)
  end
end
于 2012-04-12T10:37:58.630 に答える
0

あなたが持っているすべての製品にサブスクリプションがあると仮定できる場合、これはうまくいくはずです

  scope :lower_priced_user_products, self.joins("join subscriptions on subscriptions.product_id = user_products.product_id").where("user_products.product_id < subscriptions.product_id")

これは読み取り専用レコードを返します。読み取り/書き込みアクセスが必要な場合は、UserProduct.find(rec.id) を使用してレコードをリロードする必要があります。

それがどうなるか教えてください。

于 2012-04-11T22:04:48.630 に答える