フォロー機能、完成版

#relationshipモデルの作成

$ rails g model Relationship follower_id:integer following_id:integer


#20160220133958_create_relationships.rb(マイグレーションファイル)にインデックスとか追加

```lang:20160220133958_create_relationships.rb

def change
    create_table :relationships do |t|
      t.integer :follower_id
      t.integer :following_id

      t.timestamps null: false
    end
    add_index :relationships, :follower_id
    add_index :relationships, :following_id
    add_index :relationships, [:follower_id, :following_id], unique: true
  end
end

```
 add_index :relationships, [:follower_id, :following_id], unique: true
これは一度フォローしたユーザーを２度フォローしてしまわないようにするための一意の設定。


#UserとRelationshipの関連付け

まず、フォローするのとフォローされるのとで能動的関係と受動的関係で分けることをする。
ここでは能動的関係をactive_relationshipと呼び、受動的関係をpassive_relationshipと呼ぶことにする。

userモデルが２つあり（本当は一つだけど仮想的に２
つと考えている）、その間にrelationshipモデルが挟まっていて、フォローしている人とされている人の関係を仲介している状態を常に想像すると良い。

#最初にactive_relationshipの方から進めていく

まずはユーザーAがfollowすること(following)のみを考える。（１対多）

ユーザーAはBさんCさんDさんなどのたくさんのユーザーをactive_relationshipsモデルを通してフォローできる。


```lang:user.rb

has_many :active_relationships,class_name:  "Relationship", foreign_key: "follower_id", dependent: :destroy

```


```lang:relationship.rb

 belongs_to :follower, class_name: "User"

```


一応説明を入れておくが、relationshipsテーブルには、following_idとfollower_idのペアがあって初めてrelationshipの関係が成立する。
今回は、
Aさんがfollowingしようとしているので、Aさんがrelationshipsテーブルにfollowing_idを登録する役割。

has_many :active_relationshipsだと、railsが外部キーにactive_relationships_idを探してしまい、ダメなので外部キーを明示的に示してあげる必要がある。BさんCさんDさんが今回はfollower_idを登録する役割。

#こうすればこれだけのメソッドが使えるようになる。


active_relationship.follower	フォロワーを返す
active_relationship.following	フォローしているユーザーを返す
user.active_relationships.create(following_id: user.id)	userを紐付けて能動的関係を作成/登録する
user.active_relationships.create!(following_id: user.id)	userを紐付けて能動的関係を作成/登録する (失敗時にエラーを出力)
user.active_relationships.build(following_id: user.id)	userと紐付けた新しいRelationshipオブジェクトを返す




#次に、フォローされることのみを考える。(１対多)

この際、relationshipモデルをpassive_relationshipモデルとして置き換える。

ユーザーAをフォローしている人はたくさんいるからhas_many。
ユーザーAをフォローしている人にとってfollowingの対象はユーザーA1人だからbelongs_to。


```lang:relationship.rb

 belongs_to :following, class_name: "User"

```

```lang:user.rb

has_many :active_relationships,class_name:  "Relationship", foreign_key: "following_id", dependent: :destroy

```


フォローされるユーザーAをfollower_idを外部キーを指定して特定する。




#フォローしているユーザーの集団を取り出す

1人のユーザーにはいくつもの「フォローする/される (多対多)」のリレーションシップがある。
なのでhas_many throughを使う。同じモデルを別の見方をするので、同じuserモデルに、対応した２つのhas_many throughを書かなければいけないからややこしいから注意。



```lang:user.rb

  has_many :following, through: :active_relationships, source: :following
```

railsはfollwingを見てrelationshipsテーブルのfollowing_idを使って対象のユーザーの集合を取得してくる。

source:はなくても良いがわかりやすくするために書居ておいた。
実際はマジでいらない。


これでuser.followingが使えるようになる。

この関連付けにより、フォローしているユーザーを配列の様に扱えるようになった。

例えば、include?メソッドを使ってフォローしているユーザーの集合を調べてみたり、関連付けを通してオブジェクトを探しだせるようになった。


user.following.find(other_user)でフォローしているユーザーを取り出せるようになった。


#あるユーザーをフォローしている集団（フォロワー）を取り出す

次に、user.followingと対になるuser.followersメソッドを作成する。


```lang:user.rb

  has_many :followers, through: :passive_relationships, source: :follower

```


これで、user.followersメソッドが使えるようになってfollowersの集団が取り出せるようになった。








 #followやunfollowなどのメソッドの追加

followingで取得した集合をより簡単に取り扱うためにメソッドを作成する。


```lang:user.rb

  # ユーザーをフォローする
  def follow(other_user)
    active_relationships.create(following_id: other_user.id)
  end

  # ユーザーをアンフォローする
  def unfollow(other_user)
    active_relationships.find_by(following_id: other_user.id).destroy
  end

  # 現在のユーザーがフォローしてたらtrueを返す
  def following?(other_user)
    following.include?(other_user)
  end

```



# user.followersメソッドを作成する

これは上のuser.followingメソッドと対になるやつ。

follower_idとfollowing_idを入れ替えるだけで、フォロワーについてもユーザーのフォローのときとまったく同じ方法が使用できる。

followerなので、フォローされていると考え、受動的関係と考え、passive_relationshipと考える。


```lang:user.rb

    has_many :passive_relationships, class_name: "Relationship", foreign_key: "following_id", dependent: :destroy

    has_many :followers, through: :passive_relationships, source: :follower


```




これで、フォローする、される機能ができた。

#フォロー、あんフォローボタンの作成

まず、メソッド定義から。


```lang:user.rb

  
  # ユーザーをフォローする
  def follow(other_user)
    active_relationships.create(following_id: other_user.id)
  end

  # ユーザーをアンフォローする
  def unfollow(other_user)
    active_relationships.find_by(following_id: other_user.id).destroy
  end

  # 現在のユーザーがフォローしてたらtrueを返す
  def following?(other_user)
    following.include?(other_user)
  end

```


次に、フォローするボタンを表示させるボタンを作成する



```lang:show.html.erb

<%= render 'follow_form' if logged_in? %>

```
もしログインしていたら、
follow_formページを表示するようにしている。



```lang:_follow_form.html.erb

<% unless current_user?(@user) %>
  <div id="follow_form">
  <% if current_user.following?(@user) %>
    <%= render 'unfollow' %>
  <% else %>
    <%= render 'follow' %>
  <% end %>
  </div>
<% end %>

```

もしログインしているユーザーが別のユーザーをフォローしているならunfolowへ、
もしフォローしていないならフォローへ。

```lang:_follow.html.erb

<%= form_for(current_user.active_relationships.build) do |f| %>
  <div><%= hidden_field_tag :following_id, @user.id %></div>
  <%= f.submit "Follow", class: "btn btn-primary" %>
<% end %>


```



```lang:_unfollow.html.erb

<%= form_for(current_user.active_relationships.find_by(following_id: @user.id),
             html: { method: :delete }) do |f| %>
  <%= f.submit "Unfollow", class: "btn" %>
<% end %>

```


これで、followを送信するボタンができた。
見てわかる通り、ボタンは1つで、
そのユーザーが別のユーザーをフォローしていないならばフォローボタンを、

フォローしているならアンフォローボタンを表示させるようにしている。






#ルーティングの設定
```lang:routes.rb

  resources :users do
    member do
     get :following, :followers
    end
  end

```

following_user GET    /users/:id/following(.:format) users#following
followers_user GET    /users/:id/followers(.:format) users#followers

このルーティングができる。







#followerとfollowingの表示
まず、ユーザーの詳細ページで自分のfollowingとfollowersの表示させるものを作る。




```lang:show.html.erb

<section class="stats">
  <%= render 'users/stats' %>
</section>
  
```


```lang:stats.html.erb

<% @user ||= current_user %>
<div class="stats">
  <a href="<%= following_user_path(@user) %>">
    <strong id="following" class="stat">
      <%= @user.following.count %>
    </strong>
    following
  </a>
  <a href="<%= followers_user_path(@user) %>">
    <strong id="followers" class="stat">
      <%= @user.followers.count %>
    </strong>
    followers
  </a>
</div>

```




これで、ユーザー詳細画面で自分のフォローフォロワー数が表示されるようになった。






#次に、followingページとfollowerページを表すための定義をする。

```lang:users_controller.rb

  def following
      @user  = User.find(params[:id])
      @users = @user.following
      render 'show_follow'
  end

  def followers
    @user  = User.find(params[:id])
    @users = @user.followers
    render 'show_follower'
  end
  
```

ユーザー詳細ページのfollowingとfollowersを押した時に、

`render 'show_follow'`はのちに作る`show_follow.html.erb`に飛ぶようにしている。

`render 'show_follower'`はのちに作る`show_follower.html.erb`に飛ぶようにしている。




#次に、フォローページとフォロワーページの詳細を作る。

```lang:show_follow.html.erb

<% @user.following.each do |user| %>
<table>
<tr>
  <td>  <%= user.name %> </td>
  <td><%= link_to '詳細', user_path(user) %></td>
</tr>

</table>
<% end %>
  
```


```lang:show_follower.html.erb

<% @user.followers.each do |user| %>
<table>
<tr>
  <td>  <%= user.name %> </td>
  <td><%= link_to '詳細', user_path(user) %></td>
</tr>

</table>
<% end %>

```

これで無事、フォローの集団とフォロワーの集団を取り出したものを表示するページを作成することができた。

これでひととおり、フォローの作成はできた。