User/relationship associations

在實作使用者 following 和 followers 之前,我們要先建立 users 和 relationships 的關聯。一個使用者有多個「關係」(has_many),因為一個「關係」牽涉到兩個使用者,所以「關係」同時屬於(belongs_to)該名使用者和被關注的使用者。

Section 11.1.3 建立 microposts 一樣,我們要透過關聯建立「關係」:

user.active_relationships.build(followed_id: ...)

此時,你可能會想把類似 Section 11.1.3 的程式碼加進應用裡,但有兩處不同。

首先,在 user/microposts 關聯的例子中,我們寫成:

class User < ActiveRecord::Base
  has_many :microposts
  .
  .
  .
end

這樣寫沒問題是因為在慣例中,Rails 會去尋找跟:micoposts symbol 對應的 Micropost model。不過現在的 model 是 Relationship,如果我們想寫成:

has_many :active_relationships

那就要告訴 Rails model 的 class name:Relationship。

其次,之前在 Micropost model 我們這樣寫:

class Micropost < ActiveRecord::Base
  belongs_to :user
  .
  .
  .
end

可以這樣寫是因為 microposts table 有一個 user_id 的屬性用來指出是哪一個 user(Section 11.1.1)。這種連接兩個 table 的屬性,稱為 foreign key,當指向 User model 的 foreign key 為 user_id 時,Rails 會自動取得關聯。

預設情況下,Rails 會尋找名稱為 <class>_id 的 foreign key,其中 <class> 是 model 的小寫 class name。

現在雖然我們還是在處理 users,但識別 user 的 foreign key 變成 follower_id,所以要告訴 Rails 這個改變。

所以關於 user/relationship 的關聯如以下程式碼:

app/models/user.rb

class User < ActiveRecord::Base
  has_many :microposts, dependent: :destroy
  has_many :active_relationships, class_name:  "Relationship",
                                  foreign_key: "follower_id",
                                  dependent:   :destroy
  .
  .
  .
end

因為刪除使用者也該同時刪除使用者擁有的「關係」,所以在關聯中加了 dependent: :destroy

app/models/relationship.rb

class Relationship < ActiveRecord::Base
  belongs_to :follower, class_name: "User"
  belongs_to :followed, class_name: "User"
end

雖然 followed 關聯在 Section 12.1.4 才會用到,但現在先增加上去比較容易理解。

建立上述關聯後,可以得到 Table 12.1 的方法:

Table 12.1: A summary of user/active relationship association methods.

Method Purpose
active_relationship.follower Returns the follower
active_relationship.followed Returns the followed user
user.active_relationships.create(followed_id: other_user.id) Creates an active relationship associated with user
user.active_relationships.create!(followed_id: other_user.id) Creates an active relationship associated with user (exception on failure)
user.active_relationships.build(followed_id: other_user.id) Returns a new Relationship object associated with user