Rails 4 lets you scope a has_many
relationship like so:
class Customer < ActiveRecord::Base has_many :orders, -> { where processed: true } end
So anytime you do customer.orders
you only get processed orders.
But what if I need to make the where
condition dynamic? How can I pass an argument to the scope lambda?
For instance, I only want orders to show up for the account the customer is currently logged into in a multi-tenant environment.
Here's what I've got:
class Customer < ActiveRecord::Base has_many :orders, (account) { where(:account_id => account.id) } end
But how, in my controller or view, do I pass the right account? With the code above in place when I do:
customers.orders
I get all orders for account with an id of 1, seemingly arbitrarily.
The way is to define additional extending selector to has_many
scope:
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)
The approach is described in Association Extension
head in Rails Association
page.
To access the Customer
record in the nested method you just can access self
object, it should have the value of current Customer
record.
Sinse of rails (about 5.1) you are able to merge models scope with the othe model has_many scope of the same type, for example, you are able to write the same code as follows in the two models:
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)
You pass in an instance of the class you have defined. In your case, you would pass in a customer and then get the account.
From the API http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
Sometimes it is useful to have access to the owner object when building the query. The owner is passed as a parameter to the block. For example, the following association would find all events that occur on the user's birthday:
class User < ActiveRecord::Base has_many :birthday_events, ->(user) { where starts_on: user.birthday }, class_name: 'Event' end
In your example it would be:
class Customer < ActiveRecord::Base has_many :orders, ->(customer) { where(account_id: customer.account.id) } end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With