Devise+フォロー、アンフォロー機能の実装をしていこう!

遅かれ早かれ、やっとフォロー、アンフォローまでの実装が終わりました。

今回Deviseを使って実装するのが初めてだったのでまとめておきます!

これを見た人は参考までにどうぞ。

remonote.jp

こちらの記事を参考にしほとんど一緒の内容になってしまいますが、若干違う場所もあります。

それではレッツラゴー

 

モデルの作成

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

$ rails generate model Relationship follower_id:integer followed_id:integer

 

class CreateRelationships < ActiveRecord::Migration[5.1]
  def change
    create_table :relationships do |t|
      t.integer :follower_id
      t.integer :followed_id
 
      t.timestamps null: false
    end
 
    add_index :relationships, :follower_id
    add_index :relationships, :followed_id
    add_index :relationships, [:follower_id, :followed_id], unique: true
  end
end

 マイグレーションファイルが作成されるので黄色の箇所の複合キーインデックスを付け足す。ここでは、同じユーザーが2回以上同じ人をフォロー出来ないように防いでいる。

そしていつも通り

$ rails  db:migrate 

 

UserとRelationshiipの関連付け

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

 

app/models/rerationship.rb

class Relationship < ApplicationRecord
  belongs_to :follower, class_name: "User"
  belongs_to :followed, class_name: "User"
  
  validates :follower_id, presence: true
  validates :followed_id, presence: true
end

 下の2行でついでにバリデーションも設定

 

app/models/user.rb

 

class User < ActiveRecord::Base
 
  has_many :active_relationships, class_name:  "Relationship",
                                  foreign_key: "follower_id",
                                  dependent:   :destroy
  has_many :passive_relationships, class_name:  "Relationship",
                                   foreign_key: "followed_id",
                                   dependent:   :destroy
  has_many :following, through: :active_relationships, source: :followed
  has_many :followers, through: :passive_relationships, source: :follower
  
  # ユーザーをフォローする
  def follow(other_user)
    active_relationships.create(followed_id: other_user.id)
  end
 
  # ユーザーをアンフォローする
  def unfollow(other_user)
    active_relationships.find_by(followed_id: other_user.id).destroy
  end
 
  # 現在のユーザーがフォローしてたらtrueを返す
  def following?(other_user)
    following.include?(other_user)
  end
end

 仮装的にUserモデルを2つに分けるためにactive_rerationshipsとpassive_rerationshipsという別名をつけます。

followiing,followersはそれぞれsourceから名前を上書きしています。followingによる関連付けを使ってfollow,unfollow,following?メソッドを作成していきます。ここではUserは2つとなるのでselfではなくother_userを使います。

 

ルーティング

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

 Railsチュートリアルでは

resources :users do

  member  do

    get :following, :followers

  end

end

 とすることで

f:id:yamasey712:20190912234719p:plain

このようなURLが使えるようになりましたが、今回はDeviseを使ってるので

Rails.application.routes.draw do
 
  devise_scope :user do
    get 'users/:id/' => 'users/registrations#show', as: 'show'
    get 'users/:id/following', to: 'users/registrations#following', as: 'following'
    get 'users/:id/followers', to: 'users/registrations#followers', as: 'followers'
  end
 
  resources :relationships, only: [:create, :destroy]
 
end

 このように自分で2つのgetリクエストを作ってあげます。こうすることでチュートリアルと同じものがDevise上でも出来上がります。

あとはrelationshipのresourcesもお忘れなく!

 

必要なテンプレートの作成

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

 今回はajaxでの実装をしていく形になります。

まずは、フォロワーの統計情報を表示するパーシャルを作成します。

app/views/users/shared/_stats.html.erb

<% @user ||= current_user %>
<div class="stats">
  <a href="<%= following_path(@user) %>">フォロー
    <strong id="following" class="stat">
      <%= @user.following.count %>
    </strong>
  </a>
  <a href="<%= followers_path(@user) %>">フォロワー
    <strong id="followers" class="stat">
      <%= @user.followers.count %>
    </strong>
  </a>
</div>

 

次は先ほどの統計情報とフォローボタンを表示させるためのViewを追加します。

また、ログインしている人だけに表示させます。

 

app/views/registrations/show.html.erb

<section class="stats">
  <%= render 'users/shared/stats' %>
</section>
<section>
  <%= render 'users/shared/follow_form' if user_signed_in? %>
</section>

 

次にfollow,unfollowのパーシャルを作成

app/views/users/shared/_follow_form.html.erb

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

 

次にfollow_form.html.erbで用意したパーシャルを作成するのですが、ajaxにするためremote:trueにします。

 

app/views/users/shared/_follow.html.erb

<%= form_for(current_user.active_relationships.build, remote: true) do |f| %>
  <div><%= hidden_field_tag :followed_id, @user.id %></div>
  <%= f.submit "フォローする", class: "follow-btn" %>
<% end %>

 app/views/users/shared/_unfollow.html.erb

<%= form_for(current_user.active_relationships.find_by(followed_id: @user.id),
             html: { method: :delete }, remote:true) do |f| %>
  <%= f.submit "フォロー中", class: "unfollow-btn" %>
<% end %>

 

followingアクションとfollowersアクション

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

次の2つのアクションをshow_followページ1つで読み込むようにさせる。

 

app/views/users/registrations_controller.rb

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

 このアクションに対応するビューを作成

 

app/views/users/registrations/show_follow.html.erb

<div>
  <h1><%= @user.name %></h1>
  <section class="stats">
      <%= render 'users/shared/stats' %>
  </section>
  <div>
    <h3><%= @title %></h3>
    <% if @users.any? %>
      <ul class="users follow">
        <% @users.each do |user| %>
          <li><%= link_to user.name, "#" %></li>
        <% end %>
      </ul>
    <% end %>
  </div>
</div>

 

RelationshipコントローラでAjaxリクエストに対応

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

まずは、Relationshipコントローラ作成

$ rails g controller Relationships

app/controller/relationships_controller.rb

 

class RelationshipsController < ApplicationController
  before_action :authenticate_user!
 
  def create
    @user = User.find(params[:followed_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]).followed
    current_user.unfollow(@user)
    respond_to do |format|
      format.html { redirect_to @user }
      format.js
    end
  end
 
end

 これらのアクションが呼び出すjs.erbファイルを作成

app/views/relastionships/create.js.erb

$("#follow_form").html("<%= escape_javascript(render('devise/shared/unfollow')) %>");
$("#followers").html('<%= @user.followers.count %>');

 app/views/relationships/destroy.js.erb

$("#follow_form").html("<%= escape_javascript(render('devise/shared/follow')) %>");
$("#followers").html('<%= @user.followers.count %>');

 以上!!!

これでできるはず!

人によってはDeviseの部分が違ったりしてくるのでそこは対応させてください。

また、レイアウトもしていないのでとても見栄えはよくないので自分なりにアレンジしてみてください。

ばいちゃ

めちゃ大変?1つ目のRailsポートフォリオ製作日記①

最近パソコンばかりで頭パンクしてしまいそうになったので、"Township"という平和な街づくりアプリをはじめました。

久々の更新です。

今日から1つ目のポートフォリオについて自分なりの感想を踏まえながら日記にしていきます。

さあ書いていこう!

 

 結論

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

 めちゃめちゃ難しいそれに尽きる。

 

 どうして?

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

 理由は3つあります。 

1つ目は、単に自分自身の圧倒的な実力不足。

2つ目は、やるべきことが多い。

3つ目は、襲いかかるこれでいいのか?感

僕はこの3つが大枠の理由です。

しかし!!

ここからはその3つのような難しい要素を持ちながらやることがとても重要

。ということを書いていきます。

 

なんで重要なのよ

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

 まず1つ目から見ていこう。

取り組んでいく中で圧倒的な技術不足を痛感すると思います。

あれはなんだったっけ。このメソッドなんだよ。ウワァアとなりながら必死にググる。幾度となくググる。試す。1つ前に進む。

はっきり言ってこの繰り返しです。しかし、それを繰り返していく中で、次にエラーになった時に、このエラーはここが原因だ。とか、このエラーにならないためにこう実装していこう。などその都度その都度理解していき、勝手に知識が身についていく。

これが僕達みたいな未経験へっぽこには必要なやり方だと思います。

ここで1番ダメなのは、取り組む前からまだ技術不足だからもっと勉強してから取り組もう。ということです。いわゆる『わかってから始めたい病』。

そんなこと考えていたら、どんどん遅れていく。そして自分と同じ職に就きたい人たちにもどんどん抜かされていく。

僕は見えない競争だとも思っています。絶対に負けたくないって思ってやってます。

とりあえずやってしまおう!!ということ。

2つ目は

Railsチュートリアル以上にやらなくてはいけないことが多いということ。Railsチュートリアルに少し機能を追加した程度じゃ大した評価に繋がりません。

そこを軸にしていくのはいいですが、そこから機能どんどん追加してより自分らしいアプリケーションにしていくことが大事だと思うからです。

また、アプリの見た目も大事になってくるので、デザインもそれなりにやります。テストだってRspecで書いてデプロイはAWSでする。などやるべきことがたくさんあります。その都度やりながら学んでいかなくてはいけないのでとても難しく大変な作業です。

ですが、自分のアプリケーションを作っている時が何よりも楽しいと思えるのが僕の中での強みです。

3つ目は

常に襲いかかってくる本当にこれでいいのか?という恐怖感です。

コードを書いていきその先に起こることがまだ分からない中で本当にこれをしていいのだろうか。本当にこれでこの先で全てが動いていくのか。など恐怖感が常に背後にいます。

こいつが厄介ですが、ここでの解決策は1つ!

とにかくやってみること!これに尽きます。やはり恐怖は感じるけど止まってるより失敗して前に進んでいく方がいいし、何より成功するためにはやらなきゃわかんないからですね。

当たって砕けろ精神が今の僕にはあってるんだなと思います。

 

最後に

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

 今は30%ぐらいの完成度ですがまずは一気に完成まで持っていきそれからエラーに直接関わってこないデザインや、フォームなどを修正していき自分の中で納得できる1つを作り上げていきたいと思います。

明日からもどんどんいくぞ。ってことでバイバイ。