Ruby on Rails ~フォロー（友達申請）機能の実装（コードメモ）

TwitterやInstagramなどの友達をフォローする機能を実装してみました。


#1.relationshipモデルの作成

```
$ rails g model Relationship follower_id:integer following_id:integer
```
```
      invoke  active_record
      create    db/migrate/20170102050943_create_relationships.rb
      create    app/models/relationship.rb
      invoke    test_unit
      create      test/models/relationship_test.rb
      create      test/fixtures/relationships.yml
```
#2.マイグレーションファイルの編集
```db/migrate/...
class CreateRelationships < ActiveRecord::Migration
  def change
    create_table :relationships do |t|
      t.integer :follower_id
      t.integer :following_id

      t.timestamps
    end
    add_index :relationships, :follower_id
    add_index :relationships, :following_id
    add_index :relationships, [:follower_id, :following_id], unique: true
  end
end
```
```
$ bundle exec rake db:migrate
```
#3.relationshipモデルとuserモデルの編集
```app/models/relationship.rb
class Relationship < ActiveRecord::Base
  belongs_to :follower, class_name: "User"
  belongs_to :following, class_name: "User"
  validates :follower_id, presence: true
  validates :following_id, presence: true
end
```
```app/models/user.rb
class User < ActiveRecord::Base

  # 省略

  has_many :following_relationships, foreign_key: "follower_id", class_name: "Relationship", dependent: :destroy
  has_many :followings, through: :following_relationships

  has_many :follower_relationships, foreign_key: "following_id", class_name: "Relationship", dependent: :destroy
  has_many :followers, through: :follower_relationships

  def following?(other_user)
    following_relationships.find_by(following_id: other_user.id)
  end

  def follow!(other_user)
    following_relationships.create!(following_id: other_user.id)
  end

  def unfollow!(other_user)
    following_relationships.find_by(following_id: other_user.id).destroy
  end

end
```
#4.ビューファイルの編集
```app/views/users/show.html.erb
  <span id="user_<%= @user.id %>" class="follow-wrapper">
      <%= render 'follow_form', user: @user if signed_in? %>
    </span>
```
```app/views/users/_follow_form.html.erb
<% unless current_user.id == user.id %>
  <% if current_user.following?(user) %>
    <%= render 'users/unfollow', user: user %>
  <% else %>
    <%= render 'users/follow', user: user %>
  <% end %>
<% end %>
```
```app/views/users/_follow.html.erb
<%= form_for(current_user.following_relationships.build(following_id: user.id),
             remote: true) do |f| %>
  <div><%= f.hidden_field :following_id %></div>
  <%= f.submit "Follow", class: "btn btn-large btn-primary" %>
<% end %>
```
```app/views/users/_unfollow.html.erb
<%= form_for(current_user.following_relationships.find_by(following_id: user.id),
             html: { method: :delete },
             remote: true) do |f| %>
  <%= f.submit "Unfollow", class: "btn btn-large" %>
<% end %>
```
#5.routeの設定
```config/routes.rb
  resources :users do
    member do
      get :following, :followers, :users_tweets
    end
  end
  resources :relationships, only: [:create, :destroy]
```

#6.コントローラーの設定

```
$ rails g controller relationships
```

```app/controllers/relationships_controller.rb

  def create
    @user = User.find(params[:relationship][:following_id])
    current_user.follow!(@user)
    respond_to do |format|
      format.html { redirect_to @user }
      format.js
    end
  end

  def destroy
    @user = Relationship.find(params[:id]).following
    current_user.unfollow!(@user)
    respond_to do |format|
      format.html { redirect_to @user }
      format.js
    end
  end
```

```app/controllers/users_controller.rb
  def following
    @title = "フォロー"
    @user = User.find(params[:id])
    @users = @user.followings
    render 'show_follow'
  end

  def followers
    @title = "フォロワー"
    @user = User.find(params[:id])
    @users = @user.followers
    render 'show_follow'
  end
```

#7.ビューファイルの編集
```app/views/users/show.html.erb
  <h4>
    <%= render 'users/stats' %>
  </h4>
```
```app/views/users/_stats.html.erb
<% @user ||= current_user %>
<div class="stats">
  <a href="<%= following_user_path(@user) %>">
    <strong id="following" class="stat">
      <%= @user.followings.count %>
    </strong>
    フォロー
  </a>
  &nbsp;
  <a href="<%= followers_user_path(@user) %>">
    <strong id="followers" class="stat">
      <%= @user.followers.count %>
    </strong>
    フォロワー
  </a>
</div>
```
```app/views/users/show_follow.html.erb
<div class="row">
  <aside class="span4">
    <section>
      <%= render 'users/stats' %>
    <h2><%= @title %></h2>
      <% if @users.any? %>
          <% @users.each do |user| %>
          <div class="user_avatars">
            <span><img src="<%= user.avatar.url(:thumb) %>"></span><br />
            <%= link_to user.username ,user_path(user) %>
          </div>
          <% end %>
      <% end %>
    </section>
  </aside>
</div>
```

#8.jsファイルの編集

```app/views/relationships/create.js.erb
$("#user_<%= @user.id %>").html("<%= escape_javascript(render('users/follow_form', user: @user)) %>")
$("#followers").html('<%= @user.followers.count %>')
```

```app/views/relationships/destroy.js.erb
$("#user_<%= @user.id %>").html("<%= escape_javascript(render('users/follow_form', user: @user)) %>")
$("#followers").html('<%= @user.followers.count %>')
```