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のアカウントのすべての注文を一見勝手に受け取ります。
その方法は、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
私はこれが古いことを知っていますが、まだ答えが受け入れられなかったので、その点について私の見解を追加しても誰にも害はないと考えました。
問題は、スコープをhas_many
関係に渡すときはいつでも、所有者クラスのインスタンスを引数として渡すことは、可能性だけでなく、only引数を渡す可能性であるということです。つまり、これ以上引数を渡すことはできません。これがalwaysが所有者クラスのインスタンスになります。
@RobSobers、あなたが
「アカウントIDが1のすべての注文を一見恣意的に取得します。」
これは任意ではなく、関係を呼び出したid
のcustomer
を使用してすべての注文を取得します。あなたのコードは次のようなものだったと思います
Customer.first.orders(@some_account_which_is_ignored_anyway)
has_many
リレーションは引数を受け入れるようには意図されていなかったようです。
個人的には、私は@МалъСкрылевtheのソリューションを好みます。