Followed users

現在就要來處理 Relationship 關聯:followingfollowers

這是我們首次使用 has_many :throuth:一個使用者透過 relationships 可以 following 多名使用者。

預設行為中,使用 has_many :throuth,Rails 會去尋找和關聯名稱單數形式相對應的 foreign key,換句話說,程式碼會像這樣:

has_many :followeds, through: :active_relationships

Rails 會發現關聯名稱是「followeds」,然後使用單數形式「followed」,因此會在 relationships table 中獲得一組 followed_id 組成的集合。

但是之前提過,「user.followeds」是很怪的語法,所以我們會使用「user.following」取代。

基本上,Rails 允許我們覆蓋預設設定,在這個例子中,可以使用 source 參數(如 Listing 12.8 所示)告訴 Rails,「嘿, following 陣列是由 followed_id 組成的」。

Listing 12.8: 在 User model 中增加 following 關聯設定

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
  has_many :following, through: :active_relationships, source: :followed
  .
  .
  .
end

Listing 12.8 的關聯設定可以讓我們充分利用 Active Record 和陣列的功能。例如,可以使用 include? 方法檢查使用者關注的對象有沒有某個使用者,或是透過關聯查詢某個使用者:

user.following.include?(other_user)
user.following.find(other_user)

在很多情況下,我們可以把 following 當成陣列使用,Rails 會很聰明的知道如何有效率的處理,例如以下的程式碼:

following.include?(other_user)

看起來好像是先把關注的使用者全部從資料庫裡找出來,再調用 include? 方法,但其實為了提高效率,Rails 會直接在資料庫層直接執行相關的操作。和之前在 Section 11.2.1 一樣,使用 user.microposts.count,都是直接在資料庫裡操作。

為了處理關注使用者的操作,我們要定義兩種輔助方法:followunfollow。這樣我們就可以寫出例如,user.follow(other_user)。接著還要定義 following? 布林值方法,用來檢查一名使用者是否關注了另一名使用者。

目前的情況,正好適合拿來先寫測試,因為我們還要好一陣子才會開始開發網站介面,這期間我們如果只專注在開發層面的應用程式碼,到時候在客戶端發現問題就比較難處理了。

在這個例子中,可以先為 User model 寫個簡短的測試:

  1. 使用 following? 方法來確保某個使用者沒有關注另一名使用者
  2. 再用 follow 方法去關注另一名使用者
  3. 接著再用 following? 方法確認關注有成功
  4. 最後使用 unfollow 方法解除關注,並確認操作有成功

測試程式碼如 Listing 12.9 所示:

Listing 12.9: 測試關注使用者的幾個功能 Red

test/models/user_test.rb

require 'test_helper'

class UserTest < ActiveSupport::TestCase
  .
  .
  .
  test "should follow and unfollow a user" do
    michael = users(:michael)
    archer  = users(:archer)
    assert_not michael.following?(archer)
    michael.follow(archer)
    assert michael.following?(archer)
    michael.unfollow(archer)
    assert_not michael.following?(archer)
  end
end

參考 Table 12.1,我們可以使用 following 關聯定義 followunfollowfollowing?,如 Listing 12.10 所示(注意,只要可能,就會省略 self 變數):

Listing 12.10: 定義輔助方法 for 關注使用者

app/models/user.rb

class User < ActiveRecord::Base
  .
  .
  .
  def feed
    .
    .
    .
  end

  # Follows a user.
  def follow(other_user)
    active_relationships.create(followed_id: other_user.id)
  end

  # Unfollows a user.
  def unfollow(other_user)
    active_relationships.find_by(followed_id: other_user.id).destroy
  end

  # Returns true if the current user is following the other user.
  def following?(other_user)
    following.include?(other_user)
  end

  private
  .
  .
  .
end

現在執行測試,應該會通過:

Listing 12.11: Green

$ bundle exec rake test