How to model an internal emailing system using self-referential has_many :through associations
I am currently working on a flat sharing site. One of the requirements is that users should be able to email each other within the site, a sort of internal emailing system. When one user emails another user that user has an email sent to their actual email address saying that they have an email waiting for them on the site. The intention being to keep them locked in to the site for as long as possible, and keep those adsense clicks coming in, and to provide a useful service of course! In order to do this a user model needs to be able to get all users who have emailed them and also carry through associated email details.
Anyway, here is the migration and model code to do this:
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.column 'username', :string
t.column 'forename', :string
t.column 'surname', :string
t.column 'email', :string
end
end
def self.down
drop_table :users
end
end
class CreateEmails < ActiveRecord::Migration
def self.up
create_table :emails do |t|
t.column 'sender_id', :integer, :null => false
t.column ‘receiver_id’, :integer, :null => false
t.column ’subject’, :string
t.column ‘body’, :text
t.column ‘created_at’, :datetime
end
add_index(’emails’, ’sender_id’)
add_index(’emails’, ‘receiver_id’)
end
def self.down
drop_table :emails
end
end
The important thing to notice in the User model below is that the “users_who_emailed_me” goes through “emails_as_receiver” with :source => :sender and “users_whom_i_have_emailed” goes through “emails_as_sender” with :source => :receiver. It took me a while to figure that out.
class User < ActiveRecord::Base
has_many :emails_as_sender,
:foreign_key => ’sender_id’,
:class_name => ‘Email’
has_many :emails_as_receiver,
:foreign_key => ‘receiver_id’,
:class_name => ‘Email’
has_many :users_who_emailed_me,
:through => :emails_as_receiver,
:source => :sender
has_many :users_whom_i_have_emailed,
:through => :emails_as_sender,
:source => :receiver
end
class Email < ActiveRecord::Base
belongs_to :sender,
:foreign_key => ’sender_id’,
:class_name => ‘User’
belongs_to :receiver,
:foreign_key => ‘receiver_id’,
:class_name => ‘User’
end
I adapted this code from an excellent article on the excellent blog of Josh Susser which you can find here. If fact this is pretty much the same. However, after reading his post I did not notice the way a couple of the joins worked, which I have tried to point out here using possibly a more relevant example.
UPDATE:
I have now packaged all of this code up into a plugin. For install instructions read this post.
Trackbacks
Use this link to trackback from your own site.
[…] a site, I find that it would be nice if users could email each other within the site. In my previous post I explained how to set up the models and relationships for this. Today I’ve gone one step […]