Rails tutorial 14章 要約 〜ユーザーをフォローする〜

おはようございます!!

春は出会いと別れの季節ですね!!!!どんな出会いがあるか今からワクワクです!

さて、今日はrails tutorial最終章をまとめたいと思います。

ぜひ、最後まで見ていってください

今回の章では本家Twitterでも実装されているフォロー機能を実装していきます。

今回のように多対多の場合は中間テーブルというのを作成します。

(前回やっていた投稿機能は1対多でした。)

なぜかといいますと、わざわざfollowerモデルを作り、そこに再びユーザーの情報を保存するのは無駄が多いからです。

その為、フォローしたユーザーのid、フォローされたユーザーのidのみを保存するテーブルを作成します。

これをもとにモデルを作成します。

rails g model Relationship follower_id:integer followed_id:integer
rails db:migrate

 続いてuser/relationshipsの関連付けを行います。

投稿機能の時は以下のように記述しました。

user.rb

has_many :microposts

micropost.rb

belongs_to :user

しかし、今回は作成したモデル名がrelationshipsである為、active_relationshipと記述してもrailsは見つけてくれません。

その為、クラス名を明示する必要があります。

また、belongs_toも投稿機能の時はmicropostにuser_idが含まれていたのであのような記述で大丈夫でしたが、今回はフォローするユーザーをfollowed_idから特定しなければならないのでこれも明示する必要があります。

以上より、下記のようになります。

user.rb

has_many :active_relationships, class_name: "Relationship",
                 :forien_key "followed_id",
                 dependent: :destroy 

relationship.rb

belongs_to :follower :class_name: "User"
belongs_to :followed :class_name: "User"

次にfollowingとfollowerの実装をします。

これには、has_many throughを使います。

user.rb

has_many :following through: :active_relationship, source: :followed

followedでなく、followingを使ったのは複数形にした時違和感が出ないようにする為です。

フォロワーについても同じように実装します。

user.rb

has_many :passive_relationships, class_name: "Relationship",
                  foreign_key: "folloed_id",
                  dependent: :destroy
has_many :follower through: :passive_relationship, source: :follower

次にインターフェイスを作成していきます。

followingとfollowerのそれぞれの一覧を表すページを作ります。

ルーティングは以下のようになります。

routes.rb

resources :users do
  members do 
    get :followers、 :following
  end
end

usersの内側に入れることによってusers/1/followerのようなurlを作成することができます。

次にfollowing,followerの数を表示するパーシャルを作成します。

_stat.html.erb

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

@user.following(followers).countでそれぞれの数をカウントしています。

このパーシャルをホームページに差し込みます。

次にfollow,unfollowボタンの実装をします。

_folow_form.html.erb

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

また、followとunfollowの壁画するには新たなルーティングを実装する必要が出てきます。

routes.rb

resources :relationships、    only:[:create、destory]

次にパーシャルを作成します。

_follow.html.erb

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

_unfollow.html.erb

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

_follow.html.erbは新たなリレーションシップを作成し、_unfollow.html.erbはfollowed_idからフォローしているユーザーを探し出し、削除しています。

次にfollowing,followersアクション,ビューを実装していきます。

users_controller.rb

before_action :logged_in_user, only:[...:following,:followers]
.
.
.
def following
  @title = "Following"
  @user = User.find(params[:di])
  @users = @user.following.page(params[:page])
  render 'show_follow'
end

def followers
  @title = "Followers"
  @user = User.find(params[:id])
  @users = @user.followers.page(params[:page])
  render 'show_follow'
end

show_follow.html.erb

<% provide(:title, @title) %>
<div class="row">
  <aside class="col-md-4">
    <section class="user_info">
      <%= gravatar_for @user %>
      <h1><%= @user.name %></h1>
      <span><%= link_to "view my profile", @user %></span>
      <span><b>Microposts:</b> <%= @user.microposts.count %></span>
    </section>
    <section class="stats">
      <%= render 'shared/stats' %>
      <% if @users.any? %>
        <div class="user_avatars">
          <% @users.each do |user| %>
            <%= link_to gravatar_for(user, size: 30), user %>
          <% end %>
        </div>
      <% end %>
    </section>
  </aside>
  <div class="col-md-8">
    <h3><%= @title %></h3>
    <% if @users.any? %>
      <ul class="users follow">
        <%= render @users %>
      </ul>
      <%= paginate %>
    <% end %>
  </div>
</div>

followers、followingの大まかな枠組みは同じであるため作成するviewは1つで変数で表示を変えています。

最後にホームページに自身とフォロワーのマイクロポストを表示するようにします。

上記を満たすsql文は以下のようになります。

SELECT * FROM microposts
WHERE user_id IN (<list of ids>) OR user_id = <user id>

whereの後に続く文はフォロワーに含まれているか、自分自身のidという意味です。

よって次のようになります。

Micropost.where("user_id IN (?) OR user_id = ?", following_ids,id)

しかし、これだとデータが膨大になった時に動作が遅くなってしまうので、リファクタリングする必要があります。

疑問符に挿入する値を予め明記することを意識したリファクタリングすると以下のようになります。

following_ids = "SELECT followed_id from relationships
                 WHERE followed_id = :user_id"
Micropost.where("user_id IN (#{following_ids}) 
OR user_id = :user_id, user_id:id")

これで完成です。

また、フェードを実装したのでrails tutorial自体終わりました!!!!

これからはtutorialをやってみて、自分に足りないところがわかったのでそこを埋めていきたいと思います!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です