We've got our basic has_many through: for Users and Companies.
class User < ActiveRecord::Base
has_many :company_users
has_many :companies, through: company_users
end
class CompanyUser < ActiveRecord::Base
belongs_to :user
belongs_to :company
validates_uniqueness_of :company_id, scope: :user_id
end
class Company < ActiveRecord::Base
has_many :company_users
has_many :users, through: company_users
end
Then we have this CompanyMethods lib that adds a default_scope to User (the klass.method_defined(:companies) is only called on User).
module CompanyMethods
def self.extended(klass)
klass.class_eval do
default_scope do
c_ids = Authorization.current_company_ids
includes(:companies).where(companies: { id: c_ids })
end
end
end
end
class User < ActiveRecord::Base
extends CompanyMethods
So when we call User.find_by_email('michael@widgetworks.com') we get back only users that are scoped by Authorization.current_company_ids.
Here's the sql:
SELECT DISTINCT `users`.id FROM `users` LEFT OUTER JOIN `company_users` ON `company_users`.`user_id` = `users`.`id` LEFT OUTER JOIN `companies` ON `companies`.`id` = `company_users`.`company_id` WHERE `companies`.`id` IN (4) AND `users`.`email` = 'michael@widgetworks.com' LIMIT 1
All this is fine and dandy for a lot of instances. But here's where it starts to get funky.
When another object, for example a CreditCard calls an association that goes through User, the companies.id scope gets called on the child object.
class CreditCard < ActiveRecord::Base
has_one :user, through: :user_profile
has_many :orders, through: :user
end
class UserProfile < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
extends CompanyMethods
has_many :orders
end
Here's the sql that it generates:
SELECT `orders`.* FROM `orders` INNER JOIN `users` ON `orders`.`user_id` = `users`.`id` INNER JOIN `user_profiles` ON `users`.`id` = `user_profiles`.`user_id` WHERE `orders`.`company_id` IN (4) AND `companies`.`id` IN (4) AND `user_profiles`.`id` = 47717
credit_card.orders throws an error because of it's SQL query is calling "companies.id IN (4)" on reservation when it should only be calling it on user.
I've been able to get around the problem with a hack of instead of using a through association, I just wrote a method named reservations on CreditCard.
class CreditCard < ActiveRecord::Base
has_one :user, through: :user_profile
def orders
user.orders
end
end
That pretty much fixes the problem, but it's not a great solution.
Aucun commentaire:
Enregistrer un commentaire