I am implementing groups feature. I am stuck at searching members of a group.
Let say
Class Group
has_many :group_memberships
has_many :members, :source => :user, :through => :group_memberships
end
Class GroupMembership
belongs_to :group
belongs_to :user
end
Class User
has_many :group_memberships
has_many :groups, :through => :group_memberships
has_one :profile, :dependent => :destroy
end
Class Profile
belongs_to :user
end
How to search group members, with searchable fields in profile
Could something like below work for you?
group = Group.find(id)
users = group.users.joins(:profiles).where("profile.age>18")
You can do this with the help of eager loading -
just Specify Condition on Eager Loaded Associations like -
group = Group.last
users = group.users.includes(:profiles).where("profiles.name" => "xyz")
For more details refer -
http://guides.rubyonrails.org/active_record_querying.html#specifying-conditions-on-eager-loaded-associations
Related
sorry for the title I didn't really know what to put in there to be really clear. So I'll be clear in my explanation.
Users can create Groups and Links via form. The users can join Groups via Member where member has group_id and user_id in the table. I would like to be able to share the Users Link within the group.
So when a user creates a group, other users join this group. And for now when a user creates a link, it's only for himself but I want the user to be able to share (or not) the links he created with the groups he is part of. If the user is a member of several groups then the user can choose in which group he wants to share the link he created.
I have 4 models : Link.rb, User.rb, Member.rb and Group.rb. Here are my relations :
#Link.rb:
class Link < ApplicationRecord
belongs_to :user
end
#User.rb :
class User < ApplicationRecord
has_many :links, dependent: :destroy
has_many :members, :dependent => :destroy
has_many :groups, :through => :members
end
#Member.rb :
class Member < ApplicationRecord
belongs_to :user
belongs_to :group
validates :user_id, :presence => true
validates :group_id, :presence => true
validates :user_id, :uniqueness => {:scope => [:user_id, :group_id]}
end
#Group.rb :
class Group < ApplicationRecord
has_secure_token :auth_token
has_many :members, :dependent => :destroy
has_many :users, through: :members, source: :user
belongs_to :owner, class_name: "User"
def to_param
auth_token
end
end
What I tried :
I added a reference of group_id in the link table. And added belong_to :group in link.rb, and has_many :links, dependent: :destroy in group.rb.
In my new link form I added the current_user group_id in a select (to retrieve only the groups where the user is in) and on create the link is created with the group_id and the current_user id. That works.
Problem is that I have to enter a group_id which means I give no choice to the user and he has to give a group_id so basically he must share the link to the group. Which is not what I want.
What I thought about :
Maybe I should just go for the same idea as I did for members. Which means having a new table like grouplinks where I give the group_id, link_id and the user_id with relations in place so I can use the grouplink.id to share in my group or not. Is this a good option ? If yes what are the relations I should put in place ? Any other suggestion, maybe I'm completly wrong and there is something easy to do and I don't see it.
I will try to give a shot with some code I thought about :
#Link.rb:
class Link < ApplicationRecord
belongs_to :user
belongs_to :grouplink
end
#User.rb :
class User < ApplicationRecord
has_many :links, dependent: :destroy
has_many :grouplinks, dependent: :destroy
has_many :members, :dependent => :destroy
has_many :groups, :through => :members
end
#Grouplink.rb :
class Member < ApplicationRecord
belongs_to :user
belongs_to :group
belongs_to :link
end
#Group.rb :
class Group < ApplicationRecord
has_secure_token :auth_token
has_many :members, :dependent => :destroy
has_many :users, through: :members, source: :user
belongs_to :owner, class_name: "User"
has_many :groupslinks, :dependent => :destroy
has_many :links, through: :grouplinks
def to_param
auth_token
end
end
Could that work out ?
Thanks for your help.
It's better to create a join table with group_id and link_id. Since the Group Admin is the user who created the group, you don't even need to add user_id(member_id I suppose) since every member of that group can access the link. Get the user groups and add that link to that particular group and check the association.
class GroupLink
belongs_to :group
belongs_to :link
You can just do
#group = #user.groups.find(params[:group_id])
#group_link = #group.group_links.build(link_id: link_id)
#group_link.save
Let me know if you need anything else or clarify this.
This may be tough for me to explain, so if it's not clear just let me know so I can edit as needed!
I have the following example:
class User < ActiveRecord::Base
has_many :topics
has_many :memberships
end
class Topic < ActiveRecord::Base
belongs_to :user
end
#join model between User and Group
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :group
end
class Group < ActiveRecord::Base
has_many :memberships
has_many :members, :through => :memberships, :source => :user
has_many :topics, :through => :members
end
The problem I'm having is that I am trying to create a feed (#feed_topics) of all topics that are owned by all the members of the groups I am a member of, and I'm driving myself a little nuts.
Should I try to make this happen using associations, or make an instance method in my User model that has some ActiveRecord/SQL to union all the groups' members' topics into one ActiveRecord::Relation object?
My goal is to write current_user.feed_topics in my controller's action.
Sorry for not explaining earlier! The idea was to utilize 'Nested has_many_through's in order to get to your feed topics. This concept is documented here under the heading 'Nested Associations': http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html.
Let me know if this still is unclear (or if it doesn't work).
class User < ActiveRecord::Base
has_many :topics
has_many :memberships
has_many :groups, :through => :membership
has_many :group_members, :through => :groups, :source => :member
has_many :feed_topics, :through => :group_members, :source => :topic
end
So far these are the final versions of the models from the original question (topic and membership did not change):
class User < ActiveRecord::Base
has_many :topics
has_many :memberships
has_many :groups, :through => :memberships
has_many :feed_topics, :through => :groups, :source => :member_topics
end
class Group < ActiveRecord::Base
has_many :memberships
has_many :members, :through => :memberships, :source => :user
has_many :member_topics, :through => :members, :source => :topics
end
I am testing right now by adding more groups and members to see if it pulls in all the other members' topics of other groups.
EDIT: things seem to be working ok.
EDIT2: one little problem I had was seeing duplicate topics because a member was in multiple groups. I learned about :uniq => true and it saved the day.
I've started learning Rails recently and have completed the sample app at http://ruby.railstutorial.org. I am trying to add user-created groups to the sample social network. I've gotten stuck at how exactly I should model the group. I know there should be a user_id of who owns the group and there should be strings of the name and description. Aside from that I have no idea what the rest of the group data model should look like. Thank you in advance.
If you want a user to be in multiple groups your relations should be something like this
class Group < ActiveRecord::Base
belongs_to :owner, :class_name => "User"
has_many :memberships
has_many :members, :through => :memberships
end
class Membership < ActiveRecord::Base
belongs_to :member, :class_name => "User"
belongs_to :group
end
class User < ActiveRecord::Base
has_many :memberships
has_many :groups, :through => :memberships
has_many :owned_groups, :class_name => "Group", :foreign_key => "owner_id"
end
What about something like this?
class Group < ActiveRecord::Base
has_many :users
end
class User < ActiveRecord::Base
belongs_to :group
end
given two models:
User
has_many :group_members
has_many :groups, :through => :group_members
GroupMember
belongs_to :group
belongs_to :user
Group
has_many :group_members
has_many :users, :through => :group_members
has_many :group_invites
GroupInvite
belongs_to :user
Thanks to Rails, I can easily get all of a user's groups with:
groups = current_user.groups
I then take this list of groups and iterate through to create a JSON object like so:
groups.each do |group|
....
group.group_members.count
group.invites.first_code
etc etc . The problem is this results in a large number of queries because I haven't found a way to use includes(:) to query for all the group_members and invites for all the groups with one query versus a query for every each loop. Any suggestions on how you can use includes like so:
current_user.groups.includes(:group_members, :invites) ?
Try this:
GroupMember
belongs_to :group
belongs_to :user
Group
has_many :group_members
has_many :users, :through => :group_members
has_many :invites, :
has_many :invitees, :through => :invites, :source => :user
Invite
belongs_to :group
belongs_to :user
Now you can:
current_user.groups.includes(:invitees)
I've been stuck on this all day. I have a setup like the one below. I'm trying to define friends using the group_memberships association.
class User < ActiveRecord::Base
has_many :group_memberships
has_many :groups, :through => :group_memberships
has_many :friends # what goes here? <<
end
class GroupMembership < ActiveRecord::Base
belongs_to :user
belongs_to :role
belongs_to :group
end
class Role < ActiveRecord::Base
has_many :group_memberships
end
class Group < ActiveRecord::Base
has_many :group_memberships
has_many :users, :through > :group_memberships
end
I'd like to do this without creating a join table for friends, unless it's completely crazy to do it without.
The group_membership table contains user_id and group_id linking one user to one group.
I'd trying to get
#user.friends
to return users with common group_memberships using the group_id.
has_many :friends, :through => :group_memberships, :source => :group
Nothing I've tried works, but I'll chalk that up to my complete misunderstanding of the above code.
Unfortunately Rails doesn't let you nest has_many's more than 2 deep.. Forgetting about naming it friends for a moment (let's call it users instead), this would theoretically be what you'd want:
has_many :group_memberships
has_many :groups, :through => :group_memberships
has_many :users, :through => groups
Except that this doesn't work. If you try it you'll see this not-so-helpful error message which comes from this bit of code, specifically source_reflection.options[:through].nil?. That is, the through isn't allowed to have a through itself.
Instead, you may want to do something like this:
Solution 1
class User < ActiveRecord::Base
has_many :group_memberships
has_many :groups, :through => :group_memberships
def friends
groups.with_users.map(&:users).flatten.uniq.reject{|u| u == self}
end
end
class Group < ActiveRecord::Base
has_many :group_memberships
has_many :users, :through => :group_memberships
named_scope :with_users, :include => :users
end
Solution 2
Use the nested_has_many_through plugin that Radar mentioned. It looks like at least one fork of it on github has been updated to work on the latest Rails.
Solution 3 (just for kicks)
or, just for kicks, you could do it with one big SQL query:
class User < ActiveRecord::Base
has_many :group_memberships
has_many :groups, :through => :group_memberships
def friends
sql = <<-SQL
SELECT users.* FROM users, (
SELECT DISTINCT gm2.user_id AS user_id
FROM group_memberships gm, groups g, group_memberships gm2
WHERE gm.user_id = ? AND g.id = gm.group_id AND gm2.group_id = g.id AND gm2.user_id != ?
) AS user_ids
WHERE users.id = user_ids.user_id
SQL
User.find_by_sql([sql, id, id])
end
end
Use the nested_has_many_through plugin.
delegate :users, :to => 'group'