###フォローしているユーザー
####Userモデルにfollowingの関連付けを追加する
app/models/user.rb
class User < ApplicationRecord
has_many :microposts, dependent: :destroy
# 他のモデルとの間に「1対多」のつながり
# has_many関連付けが使われている場合、
# 「反対側」のモデルでは多くの場合belongs_toが使われます。
# dependent: :destroy
# ユーザーが破棄された場合
# 、ユーザーのマイクロポストも同様に破棄される。
has_many :active_relationships, class_name: "Relationship",
# :active_relationsshitpsデータモデル 能動的関係(フォローしているがフォローはされていない関係)
# class_name: テーブル名 "Relationship"
foreign_key: "follower_id",
# foreign_key: 外部キー
# データベースの二つのテーブルを繋げる
# Micropostモデルとrelationshipsモデルを繋げる
dependent: :destroy
# ユーザーが削除されるとマイクロポストも削除される
has_many :following, through: :active_relationships, source: :followed
# :following followingデータモデル
# has_many :through関連付けは、他方のモデルと「多対多」のつながりを設定する場合によく使われます。
# この関連付けは、2つのモデルの間に「第3のモデル」(joinモデル)が介在する点が特徴です。
# フォローするには:active_relationshipsデータモデルが介在する
# source: following配列の元はfollowed idの集合である
.
.
.
end
###“following” 関連のメソッドをテストする
test/models/user_test.rb
require 'test_helper'
class UserTest < ActiveSupport::TestCase
def setup
@user = User.new(name: "Example User", email: "user@example.com",
password: "foobar", password_confirmation: "foobar")
end
.
.
.
test "should follow and unfollow a user" do
michael = users(:michael)
# テストユーザーを代入
archer = users(:archer)
assert_not michael.following?(archer)
# archerはmichaelをふぉろーしていないことを確認
michael.follow(archer)
# archerをフォロー
assert michael.following?(archer)
# フォローしているか?
michael.unfollow(archer)
assert_not michael.following?(archer)
end
end
###"following" 関連のメソッド
app/models/user.rb
class User < ApplicationRecord
.
.
.
# ユーザーをフォローする
def follow(other_user)
following << other_user
# followingは配列になっている
# << 指定された obj を自身の末尾に破壊的に追加します。
# following配列にothe_userを破壊的に追加させる
end
# ユーザーをフォロー解除する
def unfollow(other_user)
active_relationships.find_by(followed_id: other_user.id).destroy
# active_relationshiipsデータモデルからフォロワーをidを探す
# それを削除する
end
# 現在のユーザーがフォローしてたらtrueを返す
def following?(other_user)
following.include?(other_user)
# followingデータモデル
# 引数(othe r_user)を配列の名から探す
# それがあればtureを返す。
end
private
.
.
.
end
#####テスト
ubuntu:~/environment/sample_app (following-users) $ rails t
Running via Spring preloader in process 5103
Started with run options --seed 54684
63/63: [=============================] 100% Time: 00:00:07, Time: 00:00:07
Finished in 7.01682s
63 tests, 319 assertions, 0 failures, 0 errors, 0 skips
###演習
1.コンソールを開き、リスト 14.9のコードを順々に実行してみましょう。
>> michael.follow(archer)
(0.1ms) begin transaction
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
Relationship Create (1.6ms) INSERT INTO "relationships" ("follower_id", "followed_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["follower_id", 1], ["followed_id", 2], ["created_at", "2021-10-28 12:59:26.258244"], ["updated_at", "2021-10-28 12:59:26.258244"]]
(0.1ms) rollback transaction
Traceback (most recent call last):
3: from (irb):5
2: from (irb):5:in `rescue in irb_binding'
1: from app/models/user.rb:135:in `follow'
ActiveRecord::RecordNotUnique (SQLite3::ConstraintException: UNIQUE constraint failed: relationships.follower_id, relationships.followed_id)
>> michael.following?(archer)
User Exists? (0.2ms) SELECT 1 AS one FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? AND "users"."id" = ? LIMIT ? [["follower_id", 1], ["id", 2], ["LIMIT", 1]]
=> true
>> michael.follow(archer)
(0.1ms) begin transaction
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
Relationship Create (0.6ms) INSERT INTO "relationships" ("follower_id", "followed_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["follower_id", 1], ["followed_id", 2], ["created_at", "2021-10-28 13:00:30.403974"], ["updated_at", "2021-10-28 13:00:30.403974"]]
(0.1ms) rollback transaction
Traceback (most recent call last):
2: from (irb):7
1: from app/models/user.rb:135:in `follow'
ActiveRecord::RecordNotUnique (SQLite3::ConstraintException: UNIQUE constraint failed: relationships.follower_id, relationships.followed_id)
# 一意性がないらしい
>> michael.following?(archer)
User Exists? (0.2ms) SELECT 1 AS one FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? AND "users"."id" = ? LIMIT ? [["follower_id", 1], ["id", 2], ["LIMIT", 1]]
=> true
>> michael.unfollow(archer)
Relationship Load (0.2ms) SELECT "relationships".* FROM "relationships" WHERE "relationships"."follower_id" = ? AND "relationships"."followed_id" = ? LIMIT ? [["follower_id", 1], ["followed_id", 2], ["LIMIT", 1]]
(0.0ms) begin transaction
Relationship Destroy (6.1ms) DELETE FROM "relationships" WHERE "relationships"."id" = ? [["id", 1]]
(7.7ms) commit transaction
=> #<Relationship id: 1, follower_id: 1, followed_id: 2, created_at: "2021-10-28 06:05:06", updated_at: "2021-10-28 06:05:06">
>> michael.following?(archer)
User Exists? (0.2ms) SELECT 1 AS one FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? AND "users"."id" = ? LIMIT ? [["follower_id", 1], ["id", 2], ["LIMIT", 1]]
=> false
2.先ほどの演習の各コマンド実行時の結果を見返してみて、実際にはどんなSQLが出力されたのか確認してみましょう。
SELECT 1 AS one FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? AND "users"."id" = ? LIMIT ?
SELECT "relationships".* FROM "relationships" WHERE "relationships"."follower_id" = ? AND "relationships"."followed_id" = ? LIMIT ?
SELECT 1 AS one FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? AND "users"."id" = ? LIMIT ?
わからない。