【Ruby】 Ruby On Railsでポートフォリオを作ってみた

#■はじめに
　Ruby on Rails で簡単なアプリを作成したので、苦労したポイントや
　工夫した点を今後の備忘録としてメモしたいと思い、記事を書きました。
#■環境構築
　・Mac
　・Gitインストール済み
　・Ruby 2.6.5
　・Rails 6.0.3.4
　・Visual Studio Code
#■アプリの概要
ユーザー同士で服をシェアできるアプリです。普段私が服を買うとき、他人が着ている服を参考に買うことが多いため、自分たちが持っている服の写真や来てみての感想、値段などをシェアできるアプリがあると便利だと思い、作成致しました。
#■データベース設計
今回使用するのは以下の3つのテーブルです。ユーザー(user)は複数の服(fuku)を投稿できます。
さらに、中間テーブルを用いることで、ユーザーは投稿された服にいいね(likes)を押すことができます。

###usersテーブル                                
|      column       |       type        | 
|:-----------------:|:----------------- |  
| id                |       string      |
| name              |       string      |
| email             |       string      |
| profile_image_id  |       string      |


###fukusテーブル

|      column       |       type         | 
|:-----------------:|:------------------:|
| id                |       string       |
| title             |       string       |
| body              |       text         |
| user_id           |       integer      |
| image_id          |       string       |


###likesテーブル(中間テーブル)

|      column       |       type         | 
|:-----------------:|:------------------:|
| id                |       string       |
| user_id           |       integer      |
| fuku_id           |      integer       |

#deviseの実装
ログイン認証機能を作る際、1から自分で全て作ると時間がかかるため、簡単に実装できるdeviseを使用しました。

```
$rails generate devise :install
```
ログイン機能に必要な設定の方法が出てくるのでコードを書き足します。
さらに、deviseに基づいたUserモデルを作成します。

```
$rails g devise User
```
この時、deviseに基づいたUserモデルはデフォルトでemailとpasswordカラムしかもっていないので、migrationファイルにnameとprofile_image_idを書き足します。

```
$rails g migration add_name_to_users name:string profile_image_id:string
$rails db:migrate
```

新規登録用のviewにもprofile_imageとnameのフィールドを追加します。

```ruby:new.html.erb
  <%= f.label :プロフィール画像 %>
  <%= f.attachment_field :profile_image %>  
  <%= f.text_field :name, autofocus: true, placeholder:"名前", autocomplete: "name" %>
```

これでユーザーの新規登録機能が実装できたと思ったのですが、新規登録画面で名前やプロフィール画像を設定しても
データベースに反映されませんでした。調べたところ、deviseではデフォルトで新規登録する際はメールアドレスとパスワードしか受け取らないことが判明しました。
以下のようにapplication.controllerにストロングパラメーターを記載することで、追加したカラムも受け取れるようになりました。

```ruby:application.controller.rb
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :profile_image])
end
```
#Refileの実装
userモデルとFukuモデルそれぞれに画像を投稿する機能を実装したいので、refileを使うことにしました。
他にも似たようなgemにcarrierwaveがありますが、refileはcarrierwaveの作者がその後継として作ったもので比較的新しいものだそうなので、今回はrefileを使用しました。
Qiitaのこちらの記事を参考にしました。
https://qiita.com/salvage0707/items/2614c97a1f256027ef71

#中間テーブル
投稿された服に対していいねを押せる機能を実装します。
###必要な処理
①「いいねしたユーザーのid：user_id」と「いいねされた服のid：fuku_id」が「中間テーブル：likesテーブル」に保存される。
②「いいね」「いいね済み」によっていいねボタンが切り替わる

###Likeモデル
まずは、「いいねしたユーザー」と「いいねされた服」を保存するテーブル(likesテーブル)を作ります。

```
$rails g model like user_id:integer fuku_id:integer
$rails db:migrate
```

###アソーシエイション
モデルに対してアソーシエイションを記述していきます。
UserとFukuは1対多、
UserとLikeは1対多、FukuとLikeも1対多なので、


```ruby:user.rb
has_many :fukus
has_many :likes
```
```ruby:fuku.rb
belongs_to :user
has_many :fukus
```
```ruby:like.rb
belongs_to :user
belongs_to :fuku
```
となります。

###ルーティング
投稿された服に対していいねをつけるため、likesはfukus内にネストさせました。
resourceは一行書くだけでルーティングが自動育成されるので非常に便利だと感じました。


```
 resources :fukus do
      resources :likes, only:[:create, :destroy]
 end
```

###コントローラー
likesコントローラーを作ります。

```
$rails g controller likes
``` 

次に服の詳細ページで、ログインしているユーザーがいいねを押せるよう、viewを編集します。
ログインしているユーザーが既にいいねを押した場合と押していない場合とで異なる動作を行うようにします。

```ruby:app/views/fukus/show.html.erb
<% if Like.find_by(user_id:current_user.id, fuku_id:@fuku.id) %>
 <%= link_to 'いいね済み', fuku_likes_path(@fuku), method: :delete %>
<%else%>
 <%= link_to 'いいね', fuku_likes_path(@fuku), method: :post %>
<% end %>
```

likesコントローラーを以下のように記載し、createアクションでログイン中のユーザーのlikesテーブルに、いいねを押したユーザーと投稿のidを格納します。
destroyアクションでいいねの取り消しを行っています。



```ruby:likes_controller.rb
def create
 @like=Like.new(user_id:current_user.id, fuku_id:params[:fuku_id])
 @lika 
 redirect_back(fallback_location: root_path)
end

def destroy
 @like =Like.find_by(user_id:current_user.id, fuku_id:params[:fuku_id])
 @like.destroy
 redirect_back(fallback_location: root_path)
end
```

次に、ユーザー詳細ページでいいねした投稿を見れるようにコントローラーとviewを編集します。

`@fukus`→各ユーザーが投稿した服のuser_idを取得
`@likes`→各ユーザー押したいいねのuser_idを取得


```ruby:users_controller.rb
def show
    @user=User.find_by(id:params[:id])
    @fukus=Fuku.where(user_id:@user.id)
    @likes=Like.where(user_id:@user.id)
end
```

```ruby:app/users/show.html.erb
  <% @likes.each do |like| %>
         <% fuku=Fuku.find_by(id:like.fuku_id)%>
         <%= link_to(fuku_path(fuku)) do %>
          <%=attachment_image_tag fuku,:image,class:"thumbnail150" %>
         <%end%>
  <%end%>
```

以上でいいね機能の実装は完了です。

#補足
###before_action :authenticate_user!
コントローラーの先頭に記載することで、そこで行われる処理はログインユーザーによってのみ実行可能となる。
また、例えばindexアクションのみはログインしていないユーザーをでも行えるようにしたい場合は下記の通り記述する。

```
class FukusController < ApplicationController
  before_action :authenticate_user!, except: [:index]
  
　def index
    ＠fukus=Fuku.all
  end

  def show
    @fuku=Fuku.find_by(id:params[:id])
  end
end
```

###今後学習したいこと
自分でゼロからアプリを作ることによって最低限のCRUD処理などの知識はついたと思います。まだまだエラーが度々発生しますが、似たような記事を参考にすることで、自分で調べて解決する力が付いたと思います。今後もっと学習を継続し、作成したアプリに例えばユーザー同士でメッセージができる機能、服のブランドや種類、価格帯によって検索をかけられる機能などを追加したいです。また、私はフロントエンドの実装が苦手なので、その辺の勉強も併せてやりたいです。