4

Rails 3について私が学んだことが1つあるとすれば、何かをするのに苦労しているのなら、おそらくそれは間違っているのでしょう。だから私は助けを探しています。

多対多の関係にあるモデルがいくつかあります。

モデルに関連付けを問題なく作成できます。私の問題は、これらの関係を処理するコントローラーを構築する方法にあります。私がこれでどこに行くのかわからない場合は、例を挙げてみます。

例えば...

class Account < ActiveRecord::Base
    has_many :locations
end

class Contact < ActiveRecord::Base
    has_many :locations
end

class Location < ActiveRecord::Base
    has_and_belongs_to_many :accounts
    has_and_belongs_to_many :contacts
end

上記のモデルがあるとしましょう。これが私のリソースになります...

resources :accounts do
    resources :locations
end

resources :contacts do
    resources :locations
end

resources :locations do
    resources :accounts
    resources :contacts
end

したがって、これを少し短くするために、アカウントのすべての場所のリストが必要だとします。上記のルートは、おそらくアカウント/1/場所になります。したがって、locations#indexに着陸します。

うまくいけば、この時点で私の例を台無しにしていませんが、実際には複数のジョブがあるため、このアクションを構築するための最良の方法は何ですか...少なくともアカウント、連絡先、およびすべての場所の場所。

だから私はこのようなものになってしまいます...

class LocationController < ApplicationController
    def index
        if params[:account_id]
            @locations = Location.find_all_by_account_id(params[:account_id])
        elsif params[:contact_id]
            @locations = Location.find_all_by_contact_id(params[:account_id])
        else
            @locations = Location.all
        end

        respond_with @locations
    end
end

更新#1:明確にするために、モデルの関係を変更することを示唆するいくつかの回答を得ています。この時点で関係を変更できないレガシーシステムを使用しています。最終的にはデータベースと関係をクリーンアップすることが私の目標ですが、今のところできません。したがって、この構成で機能するソリューションを見つける必要があります。

4

3 に答える 3

6

現在のアプローチはDRYではなく、たとえば、インデックスに追加のスコープを課したい場合など、頭痛の種になります。たとえば、ページネーション、順序付け、またはフィールドによる検索。

別の方法を検討してください。if/elsif/ else条件が基本的に、送信find先のルックアップスコープを見つけるだけであることに注意してください。その責任をまさにそれを行う方法に移してみませんか?したがって、アクションを簡素化し、冗長なコードを削除します。

def index
  respond_with collection
end

def show
  respond_with resource
end

protected

# the collection, note you could apply other scopes here easily and in one place,
# like pagination, search, order, and so on.
def collection
  @locations ||= association.all
  #@locations ||= association.where(:foo => 'bar').paginate(:page => params[:page])
end

# note that show/edit/update would use the same association to find the resource
# rather than the collection
def resource
  @location ||= association.find(params[:id])
end

# if a parent exists grab it's locations association, else simply Location
def association
  parent ? parent.locations : Location
end

# Find and cache the parent based on the id in params. (This could stand a refactor)
#
# Note the use of find versue find_by_id.  This is to ensure a record_not_found
# exception in the case of a bogus id passed, which you would handle by rescuing
# with 404, or whatever.
def parent
  @parent ||= begin
    if id = params[:account_id]
      Account.find(id)
    elsif id = params[:contact_id]
      Contact.find(id)
    end
  end
end

彼女はinherited_resourcesは、このようなシナリオをきれいに処理するための優れた宝石です。Jose Valim(Railsの)によって書かれました。HABTMで動作するはずだと思いますが、正直なところ、これまでに試したことがあるかどうかはわかりません。

上記の例は、基本的にinherited_resourcesの動作方法ですが、ほとんどの場合、舞台裏で魔法のように動作し、必要な場合にのみメソッドを上書きします。HABTMで動作する場合(そうすべきだと思います)、現在のコントローラーを次のように書くことができます。

class LocationController < InheritedResources::Base
  belongs_to :contact, :account, :polymorphic => true, :optional => true
end
于 2011-09-08T04:33:03.990 に答える
4

同じリソースに到達するための複数の方法を提供するべきではありません。リソースからアソシエーションへの1対1のマッピングは想定されていません。

ルートファイルは次のようになります。

resources :accounts
resources :contacts
resources :locations

RESTの要点は、各リソースが一意のアドレスを持っていることです。特定の場所からのアカウント/連絡先のみを本当に公開したい場合は、次のようにします。

resources :locations do
    resources :accounts
    resources :contacts
end

ただし、ネストされたアカウント/場所と場所/アカウントルートの両方を提供することは絶対に避けてください。

于 2011-09-08T02:29:11.877 に答える
0

私の見方では、アカウントと連絡先は同様の動作をしているように見えるため、単一テーブル継承(STL)を使用し、ユーザーなどの別のリソースを使用するのが理にかなっています。

そうすればあなたはこれを行うことができます...

class User < ActiveRecord::Base
    has_many :locations
end
class Account < User
end

class Contact < User
end

class Location < ActiveRecord::Base
    has_and_belongs_to_many :user
end

リソースは同じままです...

resources :accounts do
    resources :locations
end

resources :contacts do
    resources :locations
end

resources :locations do
    resources :accounts
    resources :contacts
end

そうすれば、仕事の種類に関係なく、同じ方法で場所にアクセスできます。

class LocationController < ApplicationController
    def index
        if params[:user_id]
            @locations = Location.find_all_by_account_id(params[:user_id])
        else
            @locations = Location.find_all_by_id(params[:account_id])
        end

        respond_with @locations
    end
end

そうすれば、コードは再利用可能、スケーラブル、保守可能になり、私たちが言われた他のすべての優れたものは素晴らしいものになります。

それが役に立てば幸い!

于 2011-09-08T02:33:44.883 に答える