『rails』いいね機能の実装方法① 同期通信まで

#はじめに　
現在オリジナルアプリの「散歩習慣化アプリ」製作中です。
その中でいいね機能を初めて実装したのでまとめます。

記事にすると長くなるので、①同期通信までと、②非同期通信で記事を分けてまとめます。
今回は①同期通信までをまとめます。

非同期通信については以下の記事でまとめてます。
『rails』いいね機能の実装方法② 非同期通信の実装
https://qiita.com/yuhi_taka/items/396305dae07ea2971313

#実装の準備
　まずは前提として以下のようなアプリを作成しています。
・散歩時間や感想、画像を投稿できるアプリ
・ホーム画面には投稿一覧が表示される
[![Image from Gyazo](https://i.gyazo.com/245158cbedfd761fea19aab5518c8bc0.png)](https://gyazo.com/245158cbedfd761fea19aab5518c8bc0)

上記の散歩時間の下に
いいねボタンを表示させたい。


データベース設計は以下のとおりです。
![morning-walk.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378833/3ff33848-1115-b8a7-b4a2-6081753ed312.png)

※参考文献　ここからは以下の記事を参考にしました
https://techtechmedia.com/favorite-function-rails/


#モデルの設定
　まずはモデルを作っていきます。
likesテーブルは、usersテーブルとwalksテーブルの外部キーをそれぞれ保持します。
従って以下のように記述して、rails db:migrateします。

```
  t.integer :user_id
  t.integer :walk_id
  t.timestamps
```
これでlikesテーブルができました。

#アソシエーションの設定
　次にアソシエーションの設定をします。
![morning-walk.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378833/e0ff03d9-e908-f5ec-95ad-b95305e1b0e5.png)

まずはuser.rbと、walk.rbにアソシエーションを設定します。

ユーザーは複数の「いいね」をする事ができ、
散歩記録も複数の「いいね」を保有できるので、has_manyを指定します。

```user.rb,walk.rb
has_many :likes
```

次にlike.rbに、usersテーブルと、walksテーブルへの関係を定義します。
それぞれ必ず紐づく先があるので、belongs_toを指定します。

```like.rb
  belongs_to :user
  belongs_to :walk
```

以上でアソシエーションの設定は終わりです。

#viewにいいねを表示
　次にviewに「いいね」を表示してみます。
条件としては
いいねが既に押されている場合には、「いいねを外す」ボタンを表示
いいねがまだ押されていない場合には、「いいね」ボタンを表示します。


```index.html
省略
   <h5 class="card-title">今日の一言：<%= walk.content%></h5>
   <p class="card-text">散歩時間：<%= walk.time%>分</p>

<#以下いいね部分追加>
   <% if user_signed_in? && current_user.liked_by?(walk.id) %>
     いいねを外す
   <% else %>
     いいね
   <% end %>  
省略
```
この時にliked_by?(post.id)という自作メソッドで、いいねの有無を判断します。

#liked_byメソッドの定義
　次に先ほどviewで指定したliked_byメソッドを定義します。
テーブル操作に関するメソッドは、モデルに作成します。

```user.rb
 def liked_by?(post_id)
    likes.where(post_id: post_id).exists?
 end
```
whereメソッドとexistsメソッドを使い、引数で渡したpost_idに紐づくlikeテーブルが存在するかを判定します。
もしも存在すればtrueを返し、存在しなかったらfalseを返します。

ここまでで以下のように散歩時間の下に「いいね」を配置しました。
[![Image from Gyazo](https://i.gyazo.com/c974ec08d221aafd51bc9363b96bb19e.png)](https://gyazo.com/c974ec08d221aafd51bc9363b96bb19e)
※現状はまだリンクをつけてないので、ボタンは押せません。

#ルーティングを設定する
　さてここからは先程表示させたいいねボタンにリンクを付けたいと思います。
まずはルーティングの設定です。
いいねを付ける、削除するため、
likesに対してcreateとdestroyを定義します。

```routes.rb
 resources :walks do
    resources :likes, only: [:create,:destroy]
  end
```

注意点としては、walksテーブルにネストさせている事です。
ネストする理由は、どの散歩記録（walks）に対しての「いいね」かを特定する必要があるからです。

これでルーティングの設定ができました。

#いいねボタンを押す
　次にいいねにリンクを付けてlikesテーブルに保存できるようにします。
まずはコントローラーの設定からです。
likesコントローラーを生成します。

```
rails g controller likes
```

次にcreateアクションにいいねボタンを押下したときの処理を記述します。

```likes_controller.rb
def create
    like = Like.create(user_id: current_user.id,walk_id: params[:walk_id] )
    redirect_to root_path
  end
```

これでlikesテーブルに保存ができるようになりました。

次にいいねボタンにリンクを付けます。

```index.html
<h5 class="card-title">今日の一言：<%= walk.content%></h5>
 <p class="card-text">散歩時間：<%= walk.time%>分</p>
 <% if current_user.liked_by?(walk.id) %>
    いいねを外す
 <% else %>
<以下修正>
    <%= link_to 'いいね', walk_likes_path(walk,walk.likes), method: :POST %>
 <% end %>
```

ここでの注意点は、**「walk_likes_path(walk,walk.likes)」で引数を2つ指定している点**です。
ネストしているため、引数を2つ指定しないとno route machエラーが出ます。
試しにrails routesしてみます。


```
 walk_likes POST   /walks/:walk_id/likes(.:format)      likes#create
```

「 /walks/:walk_id/likes(.:format)  」とあるように、walk_idと、walkに紐づくlikesもパスに含まれています。

ネストしている時は、引数を2つ指定するということに注意しましょう。

ここまでで、いいねボタンにリンクをつける事ができました。

#いいねボタンの解除
　次にいいねボタンの解除機能を実装します。
まずはコントローラーのdestroyアクションを追記します。

```likes_controller.rb
def destroy
    Like.find_by(user_id: current_user.id,walk_id: params[:walk_id] ).destroy
    redirect_to root_path
  end
```
find_byメソッドは複数の条件をもとに検索できるメソッドです。

次にviewを修正します。

```index.html
<h5 class="card-title">今日の一言：<%= walk.content%></h5>
 <p class="card-text">散歩時間：<%= walk.time%>分</p>
 <% if current_user.liked_by?(walk.id) %>
<以下修正>   
  <%= link_to 'いいねを外す', walk_likes_path(walk,walk.likes), method: :DELETE %>
 <% else %>
    <%= link_to 'いいね', walk_likes_path(walk,walk.likes), method: :POST %>
 <% end %>
```

これで以下のように同期通信での、いいねが実装できました。

[![Image from Gyazo](https://i.gyazo.com/b811626fb5ee90181cd13fcff174ca61.gif)](https://gyazo.com/b811626fb5ee90181cd13fcff174ca61)



#まとめ
　今回はいいね機能について、同期通信までをまとめました。
次回は非同期通信処理についてまとめていきます。

非同期通信については以下の記事でまとめてます。
https://qiita.com/yuhi_taka/items/396305dae07ea2971313

参考文献
https://techtechmedia.com/favorite-function-rails/