SNSっぽい関連を実装してみる

  • 各Userは0人以上の友達がいる
  • 一方が友達リストに入れると、もう一方からも友達になる(現実には片想いってのもありますが、ここでは双方向ってことで。)

という、mixiでいうマイミクシィみたいなの。

今回は、has_many :throughを使ってやってみました。
ちょっと付け加えれば関連にタグをつけたりも出来ると思います。

migration

class AuthenticatedUser < ActiveRecord::Migration
  def self.up
    # ユーザテーブル
    create_table :users do |t|
      t.column  :userid,              :string
    end
    add_index :users, :userid
   # コネテーブル
    create_table :connections do |t|
      t.column :from_id,  :integer
      t.column :to_id, :integer
    end
    add_index :connections, :from_id
    add_index :connections, :to_id
  end

  def self.down
    drop_table :connections
    drop_table :users
  end
end

user.rb

class User < ActiveRecord::Base
  has_many  :connections,
            :foreign_key => 'from_id',
            :dependent => :destroy
  has_many  :friends,
            :through => :connections,
            :source => :to
  validates_presence_of     :userid
  validates_uniqueness_of   :userid
end

connection.rb

class Connection < ActiveRecord::Base
  belongs_to  :from,
              :class_name => 'User',
              :foreign_key => 'from_id'
  belongs_to  :to,
              :class_name => 'User',
              :foreign_key => 'to_id'
  
  # User.friendsへの操作で双方向のリンクを行えるように。
  after_save :create_reverse_link
  after_destroy :destroy_reverse_link
  def create_reverse_link
    reverse_connection =  Connection.find(:first,
      :conditions => [
          "from_id = :to_id AND to_id = :from_id",
          {
            :to_id    => self.to_id,
            :from_id  => self.from_id
          }
        ])
    if reverse_connection.nil?
      reverse_connection = Connection.new(:from_id => self.to_id, :to_id => self.from_id)
      raise "Create connection failure!" unless reverse_connection.save
    end
  end
  def destroy_reverse_link
    Connection.destroy_all("from_id = #{self.to_id} AND to_id = #{self.from_id}")
  end
end