I run into a case where I had User.search
method and I wanted the GroupMember
model be searchable by the user’s attributes. The most DRY way to accomplish this in Rails 3 is to merge scopes. In the User model:
#!ruby
# user.rb
class User < ActiveRecord::Base
has_many :memberships, :class_name => "GroupMember", :foreign_key => "user_id"
def self.search(search)
if search.present?
query = []
params = []
%w(uid email name).each do |field|
# The field name must be fully qualified to merge scopes
query << "#{self.table_name}.#{field} LIKE ?"
params << "%#{search}%"
end
query = query.join(" OR ")
where(query, *params)
else
scoped
end
end
end
NB! It’s important to have the User’s field names fully qualified so that they won’t be applied to the GroupMember table. And in the GroupMember model:
#!ruby
# group_member.rb
class GroupMember < ActiveRecord::Base
belongs_to :user
belongs_to :group
def self.search(search)
if search.present?
# We search GroupMembers by the user attributes
scoped.joins(:user).merge(User.search(search))
else
scoped
end
end
end
Now it’s possible to search for GroupMembers by the User attributes:
#!ruby
group = Group.find 1
group.group_members.search('david')
This results in SQL query:
#!sql
SELECT "group_members".* FROM "group_members" INNER JOIN "users"
ON "users"."id" = "group_members"."user_id" WHERE "group_members"."group_id" = 1
AND (users.uid LIKE '%david%' OR users.email LIKE '%david%'
OR users.name LIKE '%david%')