LoginSignup
0
0

More than 1 year has passed since last update.

railsチュートリアル第十四章 フォロワー

Posted at

フォロワー

受動的関係を使ってuser.followersを実装する

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 :passive_relationships, class_name:  "Relationship",
  # passive_relationsshipsデータモデル 受動的関係(フォローされている)
  # class_name: テーブル名 Relationsship
                                   foreign_key: "followed_id",
                                   # 外部キー  テーブル同士の紐づけに用いるカラムのこと。
                                   dependent:   :destroy
  has_many :following, through: :active_relationships, source: :followed
  # :following followingデータモデル 
  # has_many :through関連付けは、他方のモデルと「多対多」のつながりを設定する場合によく使われます。
  # この関連付けは、2つのモデルの間に「第3のモデル」(joinモデル)が介在する点が特徴です。
  # フォローするには:active_relationshipsデータモデルが介在する
  # source: following配列の元はfollowed idの集合である
  has_many :followers, through: :passive_relationships, source: :follower
  # folloersデータモデル
  # userとfollowersの間にはpasseive_relationsshipsが介在する
  # souce: 関連付けにおける「ソースの」関連付け名、つまり関連付け元の名前を指定します。
  # 関連づけ名とは関連付けたいデータモデルの名前
  #   この場合 :followerが関連付けられる
.
.
.
end

followersに対するテスト

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)
    # フォローしているか?
    assert archer.followers.include?(michael)
    # archerのフォロワーの中にmichaelは入っているか?
    michael.unfollow(archer)
    assert_not michael.following?(archer)
  end
end

テスト

ubuntu:~/environment/sample_app (following-users) $ rails t
Running via Spring preloader in process 8487
Started with run options --seed 30377

  63/63: [==============================] 100% Time: 00:00:08, Time: 00:00:08

Finished in 8.39715s
63 tests, 320 assertions, 0 failures, 0 errors, 0 skips

演習

1.
コンソールを開き、何人かのユーザーが最初のユーザーをフォローしている状況を作ってみてください。最初のユーザーをuserとすると、user.followers.map(&:id)の値はどのようになっているでしょうか?

>> user = User.first
  User Load (1.7ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
=> #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2021-10-27 07:03:00", updated_at: "2021-10-27 07:03:00", password_digest: [FILTERED], remember_digest: nil, admin: true, activation_digest: "$2a$12$i2cTpO7yNDagp6oMpVyIq.lyvQ1yEuoSJsOj7G9qZaX...", activated: true, activated_at: "2021-10-27 07:03:00", reset_digest: nil, reset_sent_at: nil>
>> user2 = User.second
  User Load (0.2ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? OFFSET ?  [["LIMIT", 1], ["OFFSET", 1]]
=> #<User id: 2, name: "Isaias Ebert", email: "example-1@railstutorial.org", created_at: "2021-10-27 07:03:01", updated_at: "2021-10-27 07:03:01", password_digest: [FILTERED], remember_digest: nil, admin: false, activation_digest: "$2a$12$2NxDk9cW24wyZiN3f4LI7OI3D6NSgj7mvZ/huoEFRbE...", activated: true, activated_at: "2021-10-27 07:03:00", reset_digest: nil, reset_sent_at: nil>
>> user3 = User.third
  User Load (0.1ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? OFFSET ?  [["LIMIT", 1], ["OFFSET", 2]]
=> #<User id: 3, name: "Freddy Hills Jr.", email: "example-2@railstutorial.org", created_at: "2021-10-27 07:03:01", updated_at: "2021-10-27 07:03:01", password_digest: [FILTERED], remember_digest: nil, admin: false, activation_digest: "$2a$12$LkEsBxgeobFCiJ3ssmWkZ.AxMinGX35D2x4yas988fL...", activated: true, activated_at: "2021-10-27 07:03:01", reset_digest: nil, reset_sent_at: nil>
>> user.active_relationships.create(followed_id: user2.id)
   (0.1ms)  begin transaction
  User Load (0.1ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  User Load (0.1ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
  Relationship Create (2.5ms)  INSERT INTO "relationships" ("follower_id", "followed_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["follower_id", 1], ["followed_id", 2], ["created_at", "2021-10-28 14:07:57.360763"], ["updated_at", "2021-10-28 14:07:57.360763"]]
   (7.1ms)  commit transaction
=> #<Relationship id: 2, follower_id: 1, followed_id: 2, created_at: "2021-10-28 14:07:57", updated_at: "2021-10-28 14:07:57">
>> user.active_relationships.create(followed_id: user3.id)                   
   (0.1ms)  begin transaction
  User Load (0.1ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  User Load (0.1ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 3], ["LIMIT", 1]]
  Relationship Create (1.6ms)  INSERT INTO "relationships" ("follower_id", "followed_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["follower_id", 1], ["followed_id", 3], ["created_at", "2021-10-28 14:08:02.784912"], ["updated_at", "2021-10-28 14:08:02.784912"]]
   (9.7ms)  commit transaction
=> #<Relationship id: 3, follower_id: 1, followed_id: 3, created_at: "2021-10-28 14:08:02", updated_at: "2021-10-28 14:08:02">
>> user.followers.map(&:id)
Traceback (most recent call last):
        1: from (irb):17
NoMethodError (undefined method `followers' for #<User:0x000055e039bd2a80>)
Did you mean?  follow
               following
>> user.followers.map(&:id)
Traceback (most recent call last):
        2: from (irb):18
        1: from (irb):18:in `rescue in irb_binding'
NoMethodError (undefined method `followers' for #<User:0x000055e039bd2a80>)
>> user.followers.count
Traceback (most recent call last):
        2: from (irb):19
        1: from (irb):19:in `rescue in irb_binding'
NoMethodError (undefined method `followers' for #<User:0x000055e039bd2a80>)
>> user.followers.map(&:id)
Traceback (most recent call last):
        2: from (irb):20
        1: from (irb):20:in `rescue in irb_binding'
NoMethodError (undefined method `followers' for #<User:0x000055e039bd2a80>)
>> user.following.map(&:id)
  User Load (0.3ms)  SELECT "users".* FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ?  [["follower_id", 1]]
=> [2, 3]

2.
上の演習が終わったら、user.followers.countの実行結果が、先ほどフォローさせたユーザー数と一致していることを確認してみましょう。

>> user.following.count
   (0.2ms)  SELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ?  [["follower_id", 1]]
=> 2

3.
user.followers.countを実行した結果、出力されるSQL文はどのような内容になっているでしょうか? また、user.followers.to_a.countの実行結果と違っている箇所はありますか? ヒント: もしuserに100万人のフォロワーがいた場合、どのような違いがあるでしょうか? 考えてみてください。

FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ?  [["follower_id", 1]]
=> 2

uesrsからrelationshipsに繋がれた。 フォローされているidカラムとフォローしているカラム

>> user.following.to_a.count
=> 2

SQL文が表示されない

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0