5

ビジネスロジックがコントローラーではなくモデルに属していることをどこでも読みましたが、制限はどこにありますか? 私は個人会計アプリケーションをいじっています。

Account
Entry
Operation

操作を作成するとき、対応するエントリが作成され、アカウントにリンクされている場合にのみ有効です。

o=Operation.new({:description=>"b33r", :user=>current_user, :date=>"2008/09/15"})
o.entries.build({:account_id=>1, :amount=>15})
o.valid? #=>false
o.entries.build({:account_id=>2, :amount=>-15})
o.valid? #=>true

基本的な操作の場合にユーザーに表示されるフォームは、エントリの詳細を非表示にするために簡素化されました。アカ​​ウントは、ユーザーが要求した操作の種類に応じて 5 つのデフォルトから選択されます (アカウントの初期化 -> エクイティからアカウントへ、資産の支出 - >費用、収益の獲得 -> 資産、負債の借入 -> 資産、負債の支払い -> 負債 ...) デフォルト値から作成されたエントリが必要です。

また、より複雑な操作 (2 つ以上のエントリ) を作成できるようにしたいと考えています。この 2 番目のユース ケースでは、追加の複雑さが明らかになる別のフォームを使用します。

最適なフォームはどれですか? 今のところ、SimpleOperationController で上記のコードを使用するか、Operation クラスで新しいメソッドを定義して、Operation.new_simple_operation(params[:operation]) を呼び出せるようにします。

Operation クラスから Entry オブジェクトを実際に作成して操作することは、関心の分離を壊していませんか?

私は自分のねじれた会計原則についてアドバイスを求めているわけではありません :)

編集 - 私は自分自身をはっきりと表現していないようです。検証についてはあまり気にしていません。私は、作成ロジックコードがどこに行くべきかについてもっと心配しています:

コントローラーでの操作がspendと呼ばれると仮定すると、spendを使用する場合、paramsハッシュには金額、日付、説明が含まれます。借方勘定と貸方勘定は、呼び出されたアクションから派生しますが、その後、すべてのオブジェクトを作成する必要があります。あったほうがいいでしょうか

#error and transaction handling is left out for the sake of clarity
def spend
  amount=params[:operation].delete(:amount)#remove non existent Operation attribute
  op=Operation.new(params[:operation])
  #select accounts in some way
  ...
  #build entries
  op.entries.build(...)
  op.entries.build(...)
  op.save
end

または、上記を次のようにする Operation のメソッドを作成します

def spend
  op=Operation.new_simple_operation(params)
  op.save
end

これは間違いなく、はるかに薄いコントローラーとより太いモデルを提供しますが、モデルは他のモデルのインスタンスを作成して保存します。これが私の問題です。

4

5 に答える 5

6

しかし、モデルは私の問題がある他のモデルのインスタンスを作成して保存します。

これの何が問題なのですか?

「ビジネス ロジック」で、Operation には有効なエントリのセットが必要であると規定されている場合、Operation クラスが Entry オブジェクトを認識して処理するのに何の問題もありません。

これをやりすぎて、EntryHtmlFormBuilder など、知る必要のないものをモデルに操作させた場合にのみ問題が発生します :-)

于 2008-09-15T21:19:57.783 に答える
2

仮想属性 (詳細はこちらこちら) がこれに大いに役立ちます。パラメータ全体をモデルに戻すことで、コントローラ内の処理が単純になります。これにより、フォームを動的に構築し、エントリ オブジェクトを簡単に構築できます。

class Operation
  has_many :entries

  def entry_attributes=(entry_attributes)
    entry_attributes.each do |entry|
      entries.build(entry)
    end
  end

end

class OperationController < ApplicationController
  def create
    @operation = Operation.new(params[:opertaion])
    if @operation.save
      flash[:notice] = "Successfully saved operation."
      redirect_to operations_path
    else
      render :action => 'new'
    end
  end
end

すべてが有効でない場合、保存は失敗します。これにより、検証が行われます。各エントリは独立しており、「作成」時にすべてのエントリをチェックする必要があるため、おそらく操作で検証をオーバーライドする必要があります。

class Operation
  # methods from above
  protected
    def validate
      total = 0
      entries.each { |e| t += e.amount }
      errors.add("entries", "unbalanced transfers") unless total == 0
    end
end

これで、金額がオフであり、問​​題を修正する必要があることをユーザーに知らせるエラー メッセージが表示されます。ここで非常に凝って、問題について具体的に説明することで多くの価値を追加できます。たとえば、どれだけずれているかを伝えます。

于 2008-09-15T21:08:43.927 に答える
0

このロジックを特定のモデルに埋め込むことに懸念がある場合は、それらをオブザーバー クラスに入れてみませんか。これにより、関連付けられたアイテムを作成するためのロジックが監視対象のクラスから分離されます。

于 2008-09-19T17:16:16.983 に答える
0

私の見方では、コントローラーはエンドユーザーのビューを反映し、リクエストをモデルの操作と応答に変換すると同時に、フォーマットも行う必要があります。あなたの場合、デフォルトのアカウント/エントリを使用した単純な操作と、ユーザーが選択したエントリとアカウントを使用したより複雑な操作を表す2種類の操作があります。フォームはユーザー ビュー (異なるフィールドを持つ 2 つのフォーム) を反映する必要があり、一致するコントローラーには 2 つのアクションが必要です。ただし、コントローラーには、データの操作方法に関連するロジックはなく、受信と応答の方法のみが必要です。フォームから適切なデータを取り込み、必要に応じて 1 つ以上のオブジェクトを作成する Operation クラスにクラス メソッドを用意するか、AR モデルではないがモデルをまたぐビジネス ロジックを持つサポート クラスにこれらのクラス メソッドを配置します。境界。個別のユーティリティ クラスの利点は、各モデルが 1 つの目的に集中し続けることです。欠点は、ユーティリティ クラスが存在する場所が定義されていないことです。私はそれらを lib/ に置きましたが、Rails はモデル ヘルパーの場所を指定していません。

于 2008-09-15T19:35:14.183 に答える
0

各エンティティがそれ自体を検証し、エンティティが相互に依存してその状態を関連付けられたエントリの状態に委譲するという観点で考えるのは簡単です。あなたの場合、例えば:

class Operation < ActiveRecord::Base
  has_many :entries
  validates_associated :entries
end

validates_associated は、関連付けられた各エンティティが有効かどうかをチェックします (この場合、操作が有効であるためにはすべてのエントリが有効である必要があります)。

モデルの階層全体を全体として検証しようとするのは非常に魅力的ですが、あなたが言ったように、それが最も簡単に行われる場所はコントローラーであり、ビジネスを処理するというよりも、要求と応答のルーターとして機能する必要があります。論理。

于 2008-09-15T16:23:46.700 に答える