2

私のRailsアプリにはusers、たくさん持つことができる人がいpaymentsます。

class User < ActiveRecord::Base

  has_many :invoices
  has_many :payments

  def year_ranges
    ...
  end

  def quarter_ranges
    ...
  end

  def month_ranges
    ...
  end

  def revenue_between(range, kind)
    payments.sum_within_range(range, kind)
  end

end

class Invoice < ActiveRecord::Base

  belongs_to :user
  has_many :items
  has_many :payments

  ...

end

class Payment < ActiveRecord::Base

  belongs_to :user
  belongs_to :invoice

  def net_amount
    invoice.subtotal * percent_of_invoice_total / 100
  end  

  def taxable_amount
    invoice.total_tax * percent_of_invoice_total / 100
  end

  def gross_amount
    invoice.total * percent_of_invoice_total / 100
  end

  def self.chart_data(ranges, unit)
    ranges.map do |r| { 
      :range            => range_label(r, unit),
      :gross_revenue    => sum_within_range(r, :gross),
      :taxable_revenue  => sum_within_range(r, :taxable),
      :net_revenue      => sum_within_range(r, :net) }
    end
  end

  def self.sum_within_range(range, kind)
    @sum ||= includes(:invoice => :items)
    @sum.select { |x| range.cover? x.date }.sum(&:"#{kind}_amount")
  end

end

私の見解では、ユーザーが選択した GET パラメータに応じてdashboard、合計支払いをリストしています。rangesユーザーは、、、またはのいずれかを選択yearsできquartersますmonths

class DashboardController < ApplicationController

  def show  
    if %w[year quarter month].include?(params[:by])   
      @unit = params[:by]
    else
      @unit = 'year'
    end
    @ranges = @user.send("#{@unit}_ranges")
    @paginated_ranges = @ranges.paginate(:page => params[:page], :per_page => 10)
    @title = "All your payments"
  end

end

インスタンス変数 ( @sum) を使用すると、SQL クエリの数が大幅に削減されます。これは、データベースが同じクエリに対して何度もヒットすることがないためです。

ただし、問題は、ユーザーが自分の のいずれかを作成、削除、または変更しても、インスタンス変数paymentsに反映されないことです。@sumでは、どうすればリセットできますか?または、これに対するより良い解決策はありますか?

助けてくれてありがとう。

4

4 に答える 4

3

#selectこれはあなたの質問に付随するものですが、ブロックでは使用しないでください。

あなたがしていることは、すべての支払いを選択してから、関係を配列としてフィルタリングすることです。これを克服するためにArelを使用してください:

scope :within_range, ->(range){ where date: range }

これにより、SQL BETWEEN ステートメントが作成されます。結果のリレーションで を使用#sumすると、SQL SUM() ステートメントが構築されます。これは、すべてのレコードをロードするよりもおそらく効率的です。

于 2013-10-11T09:21:32.387 に答える
0

基本@sum的には、必要な値のキャッシュです。他のキャッシュと同様に、関連する値に何かが起こった場合は無効にする必要があります。

after_saveまたはafter_createフィルターを使用して、 を設定する関数を呼び出すことができます@sum = nil。また、キャッシュがカバーする範囲を保存し、新しい支払いまたは変更された支払いの日付までに無効化を決定することも役立つ場合があります。

class Payment < ActiveRecord::Base

  belongs_to :user

  after_save :invalidate_cache

  def self.sum_within_range(range)
    @cached_range = range
    @sum ||= includes(:invoice => :items)
    @sum.select { |x| range.cover? x.date }.sum(&total)
  end

  def self.invalidate_cache
    @sum = nil if @cached_range.includes?(payment_date)
end
于 2013-10-11T09:16:47.873 に答える