前言

這篇文章你可以視為「如何在多對多關係中,記錄額外的資訊」。其實一般想要在M:N之間的關係記錄額外的資訊,應該是會透過has_many :through比較恰當,不過因為當初我在寫好友名單時,先用了上一篇文章的寫法,所以才找到本篇文章要談的作法。

簡單來說,如果要記錄的資訊只有一個欄位,用本篇作法是快又有效;否則,我認為還是用has_many :through應該比較方便。後者的作法下次再談。

正文開始...

常見的社群網站,建立好友之間的關係不外乎是加入好友、並通知對方,似乎比較少有可以替好友分群組、加上描述的功能,其中丁丁大站就是可以替好友們加上描述以及分組的。這篇文章就是教你如何像丁丁大站一樣,用簡單的程式建立好友關係並加上簡單的描述。

首先一樣是你必須有一張記錄Friendship的Table,偷用上一篇的Migration(這樣上一篇瞬間變得毫無意義了-.-,不過我的想法是,一般人應該只會用到上一篇的作法。)。注意,與上一篇不同的是,這裡多了description欄位

class AddFriendship < ActiveRecord::Migration
  def self.up
    create_table :friendships, :id => false do |t|
      t.column :user_id, :integer, :null => false
      t.column :friend_id, :integer, :null => false
      t.column :description :string
    end
  end

  def self.down
    drop_table :friendships
  end
end

接下來一樣是定義model,一樣是拿上一篇的程式,多了三行程式:

  • attr_accessor :description,由於ActiveRecord的Class在mapping到資料庫時,是直接對應到table的column,但目前users table沒有description這個column,因此我們在此給User這個class一個屬性,等一下在建立好友關係時就可以一併連好友描述都塞到friendships裡面。
  • :insert_sql,因為除了ActiveRecord會幫我們找到foreign_key所屬的欄位之外,我們還需要塞入額外的資訊,因此自訂語法。
  • after_find的定義,由於ActiveRecord撈出資料後,User這個model本身沒有description欄位,所以我們在撈出好友關係的時候,再把description寫到剛剛建立好的屬性。
class User < ActiveRecord::Base
  attr_accessor :description
  has_and_belongs_to_many :friends,
    :class_name => "User",
    :join_table => "friendships",
    :association_foreign_key => "friend_id",
    :foreign_key => "user_id",
    :insert_sql => 'INSERT into friendships (user_id, friend_id, description) VALUES (#{id}, #{record.id}, 

如此一來,你就可以用下列的程式來建立、取得好友及描述

u = User.create(:name => "deduce")
k = User.create(:name => "punk")
u.description = "Rails愛好者:deduce"
k.friends <<  u unless k.friends.include?(u)

# 輸出好友描述
k.friends.each do |friend|
  puts friend.description
end

註:description的字串丟進去之前要記得先處理,避免發生不必要的問題,例如SQL injection之類的。

References

#{record.description}

如此一來,你就可以用下列的程式來建立、取得好友及描述

u = User.create(:name => "deduce")
k = User.create(:name => "punk")
u.description = "Rails愛好者:deduce"
k.friends <<  u unless k.friends.include?(u)

# 輸出好友描述
k.friends.each do |friend|
  puts friend.description
end

註:description的字串丟進去之前要記得先處理,避免發生不必要的問題,例如SQL injection之類的。

References

)' # 要特別注意引號的使用,尤其description通常都是string,所以用單引號框起來 def after_find self.description = self["description"] # 說實在這邊我也不是很確定寫法是否正確XD應該說可以work,但不知道有沒有更好的寫法:p end end

如此一來,你就可以用下列的程式來建立、取得好友及描述

u = User.create(:name => "deduce")
k = User.create(:name => "punk")
u.description = "Rails愛好者:deduce"
k.friends <<  u unless k.friends.include?(u)

# 輸出好友描述
k.friends.each do |friend|
  puts friend.description
end

註:description的字串丟進去之前要記得先處理,避免發生不必要的問題,例如SQL injection之類的。

References