#まずはじめに
ポートフォリオに非同期通信を実装したので
アウトプットも兼ねて非同期通信の解説をしていきます。
なるべく分かりやすく解説をしていくので、
参考にしていただけたら嬉しいです!
##非同期通信とは。
非同期通信とはなんぞやといいますと、
Ajax(Asynchronous JavaScript + XML)は、ページ遷移せずにページを読み込める仕組みです。これを非同期通信と言います。
これなんですね。
つまり、ページの更新を行わなくてもページが読み込めるので
いちいちページが止まったりせずにストレスなく使えるんですね!
##非同期通信の流れ
非同期通信の流れを説明する前に通常の処理を簡単に解説します。
- HTTPリクエストがルーティングに飛ぶ
- ルーティングがどのコントローラーのアクションが判別
- コントローラーがどのモデルに対して指示を出す
- モデルが必要なデータをコントローラーに戻す
- コントローラーがviewファイルにモデルから持ってきたデータを送る
これが大まかな通常のやりとりの流れになります。
一方で非同期通信は最後の部分が変わってきます。
- HTTPリクエストがルーティングに飛ぶ
- ルーティングがどのコントローラーのアクションが判別
- コントローラーがどのモデルに対して指示を出す
- モデルが必要なデータをコントローラーに戻す
- アクション名.js.erbのファイルを探して非同期を行う
通常であればviewファイルを読み込むのですが
非同期通信となるとjs.erbファイルを読み込むため
ページ全体を更新する必要はなく一部がjs.erbに書き換わります。
これがイイネの非同期通信の流れになります!
##実装
それでは実際にどんな風に実装をするか見ていきましょう。
一応イイネ機能の部分から説明をしますが、説明不要の方は
viewファイルへ飛んでくださいね!
結構簡単に実装できますよ。
必要になるのはこちら
- マイグレーションファイル
- ルーティングファイル
- モデルファイル
- viewファイル
- js.erbファイル
- コントローラー
これらの中身を書いていきます。
##ステップ1:マイグレーションファイル
まずは、マイグレーションファイルから見ていきましょう。
class CreateLikes < ActiveRecord::Migration[5.2]
def change
create_table :likes do |t|
t.integer :user_id, null: false
t.integer :post_id, null: false
t.timestamps
end
end
end
いいねする際はイイネした投稿のidとuser_idが必要になるので、
2つカラムを用意してあげましょう!
書いたら、rails db migrateでテーブルを作成します。
##ステップ2:ルーティングファイル
いいね機能については、投稿に紐づいたイイネとなるので
投稿機能にネストをさせる必要がでてきます。
Rails.application.routes.draw do
# 投稿機能にネストさせる形でいいね機能を設定
resources :posts do
resource :likes, only: [:create, :destroy]
end
end
このように書くことでurlのpostの中にlikeを含ませることができます。
実際のURLはこんな感じ。
ちなみにresourceとlikesが複数形にならないのはidが不要だからです。
いいね自体のidがなくてもuserとpostのidの二つがあればどの投稿に誰かイイネしたか
特定ができるためこのような書き方をしています。
resourceと書くことでurlにはイイネのidは含まれなくなります。
##ステップ3:モデルファイル
# いいね判定メソッド
def liked_by?(user)
likes.where(user_id: user.id).exists?
end
この記述をモデルにしましょう。
これは後ほどviewファイルで必要になるのですが
投稿にいいねがついているかついていないかを識別(判定)するために必要です。
##ステップ4:viewファイル
次にviewファイルを見ていきます。
今回は投稿のeach文で表示している記事一つ一つに
いいね機能を実装しています。
それではコードを見ていきましょう!
<h2><i>Timeline</i></h2>
<% @posts.each do |post| %>
<div class="far fa-thumbs-up">
<span id="likes_<%= post.id %>"><%= render partial: 'likes/like', locals: {post: post}%></span>
likes
</div>
<% end %>
今回はeachで回しているため、どの投稿のidか?というのを識別するためにpost.idという書き方をして
どの投稿のidにイイネをつけるのかイイネを外すのかをjsファイルが判別するために必要になります。
後ろのrenderは部分テンプレートとなりますので下に記述します。
<% if post.liked_by?(current_user) %>
<%= link_to post_likes_path(post), method: :delete, remote:true do %>
<%= post.likes.count %>
<% end %>
<% else %>
<%= link_to post_likes_path(post), method: :post, remote:true do %>
<%= post.likes.count %>
<% end %>
<% end %>
ここで<% if post.liked_by?(current_user) %>に書かれているのは
postモデルに記述をしたメソッドになります。
部分テンプレートのlink_toに注目してほしいのですがこのリンクがcreateとdestroyへの
リンクとなっているため、このリンクを押すとlikeコントローラーへと飛んでいきます。
postを書いているのはコントローラーの部分と繋がるのですが
postの情報をlikeコントローラーに保存をしないといけないのでlink_toで
postをブロック変数として格納して、likeコントローラーへ飛ばします。
非同期通信を行う際は、remote:trueという記載が必要になります。
これをすることで非同期通信ができるようになるわけです。
##ステップ5:js.ファイル
<!--@postが実際に動いている内容としてはlikes_controllerが発火しているので@postが必要となる-->
$('#likes_<%= @post.id %>').html("<%= j(render partial: 'likes/like', locals: {post: @post}) %>");
@postで使用しているインスタンス変数は、イイネ機能のコントローラーからの
インスタンス変数になるため@postとインスタンス変数にする必要があります。
前の$の部分がviewファイルを特定しているcssでいうセレクタのような役割で
その後ろが非同期通信の役割を果たす記述となります。
##ステップ6:コントローラー
それでは最後にいいね機能のコントローラーをみていきましょう。
class LikesController < ApplicationController
before_action :authenticate_user!
def create
@post = Post.find(params[:post_id])
like = current_user.likes.new(post_id: @post.id)
like.save
# 通知の作成
# create_nitifiaction_likeはpostモデルを参照
@post.create_notification_like(current_user)
end
def destroy
@post = Post.find(params[:post_id])
like = current_user.likes.find_by(post_id: @post.id)
like.destroy
end
end
createとdestroyはほぼ同じ要領になるため、createのみを説明します。
最初にlikeテーブルにはpost_idとuser_idが必要になるため
それらに必要なデータを格納するための記述を書いていきます。
@postでやっていることとしては、viewファイルから飛んできた
postの情報があるのでPost.findとすることでpostの情報一式を持ってこれます。
そして次でやっていることとしてはuser_idとpost_idを格納する準備をします。
current_user.likesという記述をすることで現在のユーザーがイイネした
すべての情報を持ってくることができます。
そのあとにnewでpost_id: @post.idとすることで、post_idに格納する準備が整います。
ちなみにですがnewの後にuser_idは書かなくていいの?ということになりますが
Railsは非常に頭がいいため、likeモデルに必要なuser_idが必要であると判断してくれ
current_user.Likesとすることでその中にあるuser_idを格納してくれるんです。
頭いいですよね。
ということでこれでLikeテーブルに保存が完了しました!
destroyもほぼ同じような内容になりますがfind_byをしているところに注目。
find_byメソッドは、テーブルにある特定のカラムのみを引っ張りたい時に使用します!
なのでfind_by(post_user: @post.id)
という書き方をすることで投稿のidをもってこれるわけですね!
##まとめ
いかがでしたでしょうか?
非同期についてはjsファイルとviewにremote trueを記述することで
簡単に実装することができます。
いいね機能についても少し内容が難しいと思うので
何回も読んでみて理解を深めてくださいね!
わかりづらい箇所や不明点があれば気軽にコメントください!
それでは、チャオ!