How to model an internal emailing system using self-referential has_many :through associations

Posted by matt on June 02, 2007

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.

Comments

Leave a response

  1. matt-beedle.com » acts_as_emailable Tue, 05 Jun 2007 20:33:14 BST

    […] 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 […]

Comments