Rails チュートリアル　第14章　まとめ

関連付けについて
UserとMicropostの関連付けは、Micropostモデルがuser_idを持っていたので、has_manyとbelongs_toを使えば簡単に実装できたが、Relationshipモデルでは重複を防ぐために、user_idを持たないので、より深い理解と工夫が必要になる

Relation => User の紐づけ

```relationship.rb
class Relationship < ApplicationRecord

 #belongs_to: user => user_id == @user.idが紐付く
 #Microと違い、user_idがないので、これができない
 #ここでやりたいのは
 #              => follower_id == @user.id
 #              => followed_id == @user.id
  belongs_to :follower, class_name: "User"
#:followerと書くと、follow_idとFollowクラス(@follower.id?)を紐づけることを意味する
#だから、クラスだけ定義してあげる
  belongs_to :followed, class_name: "User"
end
```
このように実装すると、

```rb:

@relationship = Relationship.create(follower_id: 1,followed_id:2) 
@relationship.follower 
# follower_idとuser.idを紐づけたUser(follower)の集合体が結果が返ってくる、
# followedも同様
```
次に、User => Relation


```rb:user.rb
class User < ApplicationRecord
  has_many :microposts, dependent: :destroy
  has_many :active_relationships, class_name:  "Relationship",
#class_nameを後から定義すれば、Relationshipクラスを見てくれる
         foreign_key: "follower_id"
#自分のid(@user.id)と何を紐づけるのかを指定する,defaultではuser_idなので
#今回は指定する必要がある、図を見るとわかりやすい
# 以上の設定で => @user.active_relationshipsを扱える
```

```rb:
@user.active_relationships 
#とすると、@user.id とrelationshipを紐づけたrelationshipの集合体が返ってくる
@user.active_relationships.first
# 集合の最初、id1がid2をフォローした(follower_id 1 followed_id 2)という情報が返ってくる
@user.active_relationships.first.followed
#そのフォローされたユーザーの情報が返ってくる
```
```rb:
@user.active_relationships.map(&:followed)
# フォローされたユーザーの集合体なら、こうする
```
ここまでで関連付けは成功だが、コードが長いので短縮して書く方法がある
この結果は、上のmapと同じになる

```rb:
has_many :following,
through: "active_relationships" #ac-というメソッドを経由して
source: "followed" #followedメソッドを実行　という意味になる
```

belongs_toもhas_manyもメソッドを定義していると考える
.active_relationship .followerとか




following関連のテスト
まず便利メソッドを作る

```app/models/user.rb
#引数のuserをフォローする
  def follow(other_user)
    following << other_user
# == self.active_relationships.create(followed_id: other_user.id)
#自分のidとfollower_idは紐づいているので、ここではother_user.idとfollowed_idをひもづける
  end

  # ユーザーをフォロー解除する
  def unfollow(other_user)
    active_relationships.find_by(followed_id: other_user.id).destroy
#followメソッドで登録したidをfind_byで取得して、削除する
  end

  # 現在のユーザーがフォローしてたらtrueを返す
  def following?(other_user)
    following.include?(other_user)
#followingとすると、Userモデルの集合体が返ってくる
  end

```

```rb: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) #archerをフォローしてないはず
    michael.follow(archer) #archerをフォローする
    assert michael.following?(archer) #今度はarcherをフォローできているはず
    assert archer.followers.include?(michael) #逆にarcherはmicheにフォローされているはず
    michael.unfollow(archer) #フォローを外す
    assert_not michael.following?(archer) #上と同様
  end
end

```

フォロワーについて
自分を中心に考えると、user.idとfollowed_idが紐付く(=になる)ことで自分をフォローしているfollowerの情報が引っ張ってこれる、図をイメージする
foreign_keyがどことどこを紐付けているのかに着目すると良い

```ruby:
@user = User.second # 二番目のUserの情報を取得
@user.pasive_relationships #自分をフォローしているユーザーがいるという情報を取得できる
@user.pasive_relationships.map(&:follower) #自分をフォローしているユーザー情報を取得
@user.follower #has_many throughを使えば、上と同じ意味に
```

関連付けまとめ

```ruby:
@user.following 
# user.idとfollower_idが紐付いていて、自分がフォローしているユーザーの集合体が取得できる
@user.folloer
# user.idとfolloed_idが紐づいていて、自分をフォローしているユーザの集合が取得できる
```


-----
サンプルデータを作り込む

```ruby:seed.rb

users = User.all
user  = users.first #最初のUser情報、Exampleさんを取得
following = users[2..50] #user2~50をフォローしている人に設定
followers = users[3..40] #user3~40をフォロワーに設定
following.each { |followed| user.follow(followed) }
#Examさんがuser2から一人ずつフォローしている
followers.each { |follower| follower.follow(user) }
#user3がExamさんをフォローしている、これをuser分繰り返している
```

何らかのルーティングの上に何らかのルーティングを作る

```ruby:
resources :users do
   # /users/:id/....と書きたいとき
    member do
      get :following, :followers
   # /users/:id/following に GETリクエストが送られる => followingアクション
   #同様
    end
  end
```

followingとfollower数を表示を実装
ここでしていることは複雑ではなくて、元からあったshow viewとhome viewにPartialを使って、統計情報を表すshared/statsファイルを作って、表示している
show action and home action  =>  (show view and home view) 

=> _stats_htmlの中で ------    countを使って、following34 ,follower33を表示
 リンクをクリックすると、/users/:id/following or followerにGETリクエストを送る

=> /users/:id/following or /users/:id/followerのルーティングが作動

=> following action and follower action　へ振り分ける
しかし、まだactionは実装していないのでError

Followボタンの実装 -難しい
followボタンのpartial(users/follow)とunfollowボタンのpartial(users/unfollow)を作り、さらにそれらを含めたボタンを切り替えるフォームのpartial(users/follow_form)を作る

```ruby:users/follow_form
<% unless current_user?(@user) %> #自分自身のページではないことを確認
  <div id="follow_form">
  <% if current_user.following?(@user) %> #すでにフォローしているかどうか
    <%= render 'unfollow' %>  #しているならunfollowを
  <% else %>
    <%= render 'follow' %> #していないならfollowを
  <% end %>
  </div>
<% end %>
```
show action => show view 
show viewで呼び出されるコマンド

```_follow.html.erb

<%= form_for(current_user.active_relationships.build) do |f| %>
# form_forの引数としてcurrent_user..としていることがよくわからない
# user.idとfollower_idはすでに紐づいている

  <div><%= hidden_field_tag :followed_id, @user.id %></div>
# 他人のshowアクションのページに行った時に送られるGETリクエスト(/users/:id)の
#:idの情報はactionで@userに入っているので、followed_idと紐づける
 
 <%= f.submit "Follow", class: "btn btn-primary" %>
# => form_forの内容は POST relationship/:idに送られる
<% end %>
```

```_unfollow.html.erb
<%= form_for(current_user.active_relationships.find_by(followed_id: @user.id),
             html: { method: :delete }) do |f| %>
#current_userと@user_idを使って、find_byをかけて該当する情報を探す

  <%= f.submit "Unfollow", class: "btn" %>
# => form_forの内容は DELETE relationship/:idに送られる
<% end %>
```

following, followed actionの実装
following, followed actionのviewを共通化したい
インスタンス変数を同じにして
どちらも`render "show_follow" `と指定することで、同じviewを使うことができる

Followボタンの実装２
Relationship controlloerを作成、createアクションとdestroyアクションを作成

```ruby:
class RelationshipsController < ApplicationController
  before_action :logged_in_user

  def create
    user = User.find(params[:followed_id])
#_follow.htmlで格納されているfollowed_idからフォローされるUser情報を取得
    current_user.follow(user) #現在ログインしているユーザで、そのUserをフォロー
    redirect_to user
  end

  def destroy
    user = Relationship.find(params[:id]).followed # ?? followedが分からない
    current_user.unfollow(user)
    redirect_to user
  end
end
```

SQLを使って、タイムラインをゴージャスに
今まではこのように実装していたので、自分の投稿だけがタイムラインに表示されていた

```ruby:user.rb
def feed
    Micropost.where("user_id = ?", self.id)
# ?に第二引数のself.idが代入される
end
```
今回は、さらに自分がフォローしているユーザーの投稿も合わせてタイムラインに表示したい

```ruby:user.rb
 def feed
    Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
# following_idsとすることで、フォローしたいユーザーのidを取得できる
 end
```