web-dev-qa-db-ja.com

Rails 4)でhas_many関連付けスコープに引数を渡すにはどうすればよいですか?

Rails 4では、has_manyのような関係:

class Customer < ActiveRecord::Base
  has_many :orders, -> { where processed: true }
end

だからいつでもcustomer.orders処理済みの注文のみを受け取ります。

しかし、where条件を動的にする必要がある場合はどうなりますか?スコープラムダに引数を渡すにはどうすればよいですか?

たとえば、顧客がマルチテナント環境で現在ログインしているアカウントにのみ注文が表示されるようにしたいです。

ここに私が持っているものがあります:

class Customer < ActiveRecord::Base
  has_many :orders, (account) { where(:account_id => account.id) }
end

しかし、私のコントローラーまたはビューでは、どのようにして正しいアカウントを渡すのですか?私が行うときに上記のコードを配置すると:

customers.orders

IDが1のアカウントのすべての注文を一見勝手に受け取ります。

32
Rob Sobers

その方法は、has_manyスコープに追加の拡張セレクターを定義することです。

class Customer < ActiveRecord::Base
   has_many :orders do
      def by_account(account)
         # use `self` here to access to current `Customer` record
         where(:account_id => account.id)
      end
   end
end

customers.orders.by_account(account)

このアプローチについては、Rails Association ページのAssociation Extensionヘッドで説明しています。

ネストされたメソッドのCustomerレコードにアクセスするには、selfオブジェクトにアクセスするだけで、現在のCustomerレコードの値が必要です。

Rails(約5.1)のモデルのスコープを他のモデルhas_manyスコープとマージできますたとえば、2つのモデルで次のように同じコードを記述できます。

class Customer < ApplicationRecord
   has_many :orders
end

class Order < ApplicationRecord
   scope :by_account, ->(account) { where(account_id: account.id) }
end

customers.orders.by_account(account)

定義したクラスのインスタンスを渡します。あなたのケースでは、顧客を渡して、アカウントを取得します。

APIから http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

オーナーオブジェクトへのアクセス

クエリを作成するときに所有者オブジェクトにアクセスできると便利な場合があります。所有者はパラメーターとしてブロックに渡されます。たとえば、次の関連付けは、ユーザーの誕生日に発生するすべてのイベントを検索します。

class User < ActiveRecord::Base
  has_many :birthday_events, ->(user) { where starts_on: user.birthday }, 
    class_name: 'Event'
end

あなたの例ではそれは:

class Customer < ActiveRecord::Base
  has_many :orders, ->(customer) { where(account_id: customer.account.id) }
end
24
Brad

私はこれが古いことを知っていますが、まだ答えが受け入れられなかったので、その点について私の見解を追加しても誰にも害はないと考えました。

問題は、スコープをhas_many関係に渡すときはいつでも、所有者クラスのインスタンスを引数として渡すことは、可能性だけでなく、only引数を渡す可能性であるということです。つまり、これ以上引数を渡すことはできません。これがalwaysが所有者クラスのインスタンスになります。

@RobSobers、あなたが

「アカウントIDが1のすべての注文を一見恣意的に取得します。」

これは任意ではなく、関係を呼び出したidcustomerを使用してすべての注文を取得します。あなたのコードは次のようなものだったと思います

Customer.first.orders(@some_account_which_is_ignored_anyway)

has_manyリレーションは引数を受け入れるようには意図されていなかったようです。

個人的には、私は@МалъСкрылевtheのソリューションを好みます。

0
Misu