Reputation: 166
This should be easy I think...
A Organisation has an Owner and Members - both are Users.
This is setup by the organisation using class_name: "User", like so:
class Organisation < ApplicationRecord
belongs_to :owner, class_name: "User", foreign_key: "owner_id"
has_many :organisations_users
has_many :members, class_name: "User", through: :organisations_users, source: :user
end
This works but I need an all_members function (or scope) so I can get back both owner and members in one array (or ActiveRecord object). I thought this would be trivial but it's actually not.
I've tried:
def all_members
members << owner
end
this of course is not what I want at all... this adds the owner to the staff every time I call it.
def all_members
[owner, members]
end
this sort of works but returns a nested array which is hard to access properly.
scope :all_members, joins(:members).merge(:owner)
this doesn't work at all. Probably nonsense.
def all_members
members_array = members.dup
members_array << owner
end
This still permanently alters the members to include the owner?!!
Help! (Thanks)
Upvotes: 0
Views: 196
Reputation: 176412
If getting an Array back is sufficient, then you can simply use:
def all_members
[owner] + members
end
If you need a relation, then the simplest (but not most efficient) approach would be:
def all_members
User.where(id: [owner] + members.ids)
end
It's not the most efficient as the SQL that gets generated may include a fairly large IN
statement that cannot be efficiently cached by the database parser. Still, if the numbers are in the range of a few dozens, it would be enough even that relation trick.
Upvotes: 1
Reputation: 29328
If the order is not necessarily important then we can get this into a single query like so:
If Rails 5
def all_members
# not sure if you could just do
# owner.or(members) since they are both User
# but it seems unlikely due to the join table
User.where(id: self.owner_id).or(
User.where(id: organisation_users.select(:user_id))
)
end
All versions of Rails (less than and including Rails 5) we can use Arel
to build the more complex query
def all_members
user_table = User.arel_table
User.where(
users_table[:id].eq(self.owner_id).or(
users_table[:id].in(
organisation_users.select(:user_id).arel
)
)
)
end
these will both result in the following SQL
SELECT users.*
FROM users
WHERE
(users.id = [YOUR_OWNER_ID] OR
users.id IN (
SELECT organisations_users.user_id
FROM organisations_users
WHERE organisations_users.organisation_id = [YOUR_ORGANISATION_ID]
))
the end result will be an ActiveRecord::Relation
of User
objects.
Upvotes: 1