Assumptions
- This is a system for many users, not just one, so you need to specify the Owner on the Contact and Group records with a foreign key.
- A Contact can belong to multiple Groups, and a Group has multiple contacts, so this is a has_and_belongs_to_many relationship.
Migrations
Create Users table:
class CreateUsers < ActiveRecords::Migrations
def change
create_table :users do |t|
t.text :username
end
end
end
Create Contacts table. Has a foreign key for the User the Contact is owned by as well as the User that the Contact refers to.
class CreateContacts < ActiveRecord::Migrations
def change
create_table :contacts do |t|
t.references :owner
t.references :user
end
end
end
Create Groups table. Also has a foreign key for the User that the group belongs to.
class CreateGroups < ActiveRecord::Migrations
def change
create_table :groups do |t|
t.references :owner
t.text :name
end
end
end
Create the table for the has_and_belongs_to_many relationship between Contacts and Groups. Has two foreign keys; one for the Contact and the other for the Group.
class CreateContactsGroups < ActiveRecord::Migrations
def change
create_table :contacts_groups do |t|
t.references :contact
t.references :group
end
end
end
Models
The User model. A User has many Contacts and the foreign key on the contacts table that relates a contact to a user is the 'owner_id' column. Same thing for Groups.
class User
has_many :contacts, :foreign_key => 'owner_id'
has_many :groups, :foreign_key => 'owner_id'
end
The Contact model. A Contact belongs to an Owner and we are using the 'User' model for the Owner relationship. Also, a Contact refers to another User (or belongs to, Rails parlance, which is kind of confusing in this circumstance), but :user matches the name of the model, so we don't need to specify that like we do for :owner. Finally, the has and belongs to many relationship with Groups.
class Contact
belongs_to :owner, :class_name => 'User'
belongs_to :user
has_and_belongs_to_many :groups
end
The Group model. A Group belongs to an Owner and has and belongs to many Contacts.
class Group
belongs_to :owner, :class_name => 'User'
has_and_belongs_to_many :contacts
end
We don't need to create a Model for the contacts_groups table since we wont be accessing it directly and instead will only be accessing it through either a Contact or a Group.
Results
This would allow you to do stuff like the following:
@user.contacts # all of the user's contacts
@user.groups # all of the user's groups
@user.groups.find(id).contacts # all the contacts in a group
@user.contacts.find(id).groups # all of the groups that a contact belongs to
Adding Contacts to a Group
This is in response to @benrmatthews comment asking what the views would look to add a Contact to a Group.
There are actually a bunch of different ways to achieve this, but the general idea is that you need to pass the Contact id and Group id to a controller action that will then create an entry in the contact_groups table to indicate that the contact has been added to the group.
Your routes might look like this:
resources :contact_groups, only: [:create]
Then you will have a controller that looks something like this:
class ContactGroupsController < ApplicationController
def create
@contact = Contact.find(params[:contact_id])
@group = Group.find(params[:group_id])
@contact.groups << @group
end
end
Now all you need are forms which pass the contact_id
and group_id
to the controller action. Here's what that would look like:
# This is for adding the contact to a group
form_tag contact_groups_path do
hidden_field_tag :contact_id, @contact.id
select_tag :group_id, options_from_collection_for_select(Group.all, :id, :name)
submit_tag "Add to Group"
end
This creates a form with a hidden field that contains the contact_id
and a select field that will display a list of group names. When this form gets submitted, it will pass the contact_id
and group_id
to the controller action, which then has enough information to do it's thing.