【Rails】コメント機能のエラーメッセージ表示方法（初学者向け）

# はじめに
本記事は、駆け出しエンジニアの第一歩！[AdventCalendar2020 19日目](https://qiita.com/advent-calendar/2020/programming-beginner)の記事です。
# アプリケーション概要
ユーザー投稿に対して、コメントができる一般的なアプリケーションです。（ユーザー登録はdeviseを使用）
現状では、commentに対するバリデーションは設定していないため、空投稿が可能な状態になっています。
## ER図
![_2020-12-15_12.07.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869111/9dcfa3f0-d24a-25cd-6cc2-30fedf23a704.png)
## コメント機能
実装前のコメント機能のviewとcontrollerは以下の通りです。

```ruby:post_images/show.html.erb　※コメント投稿部分のみ抜粋
<%= form_with model:[@post_image, @comment], local:true do |f| %>
  <div class="row">
    <div class="col-sm-12">
       <%= f.text_area :comment, rows:'5', class: "form-control",placeholder: "コメントをここに" %>
    </div>
 　</div>
   <%= f.submit "送信する", class: "btn btn-lg btn-base-1 mt-20 pull-right" %>
<% end %>
```

```ruby:post_images/controller.rb
def show
    @post_image = PostImage.find(params[:id])
    @comment = Comment.new
end

```

```ruby:comments/controller.rb
def create
   post_image = PostImage.find(params[:post_image_id])
   comment = current_user.comments.new(comment_params)
   comment.post_image_id = post_image.id
   comment.save
   redirect_to post_image_path(post_image)
end

private
def comment_params
  params.require(:comment).permit(:comment)
end
```

# コメント機能にエラーメッセージをつける
### 1. コメントモデルのバリデーション設定

```ruby:models/comments.rb
validates :comment, presence: true
```
### 2. コントローラを条件分岐させる
ローカル変数commentのsaveに失敗した場合、commentはエラー内容を含んだものであるとわかる。その状態をviewに反映させる（エラーメッセージ を表示する）ために、インスタンス変数＠error_commentにエラー内容を含んだcommentを再定義する必要がある。

```ruby:comments/controller.rb
def create
    post_image = PostImage.find(params[:post_image_id])
    comment = current_user.post_comments.new(comment_params)
    comment.post_image_id = post_image.id

   #6行追加（if〜end）--------------------------------
    if comment.save
	    redirect_to post_image_path(post_image)
	else
		@error_comment = comment
		render 'post_image/show'
	end
   #-----------------------------------------------
end

private
def comment_params
  params.require(:comment).permit(:comment)
end
```

### 3. エラーメッセージの表示を記載する
さっき再定義した＠error_commentをviewに渡す。

```html:post_images/show.html.erb　※コメント投稿部分のみ抜粋
<%= form_with model:[@post_image, @post_comment], local:true do |f| %>

----エラーメッセージの表示を追加------------------------------------------------
  <% if @error_comment.present? %>
    <div id="error_explanation">
      <h2><%= @error_comment.errors.count %>件のエラーが発生しました。</h2>
      <ul>
        <% @error_comment.errors.full_messages.each do |message| %>
        <li><%= message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>
--------------------------------------------------------------------------

  <div class="row">
    <div class="col-sm-12">
      <%= f.text_area :comment, rows:'5', class: "form-control",placeholder: "コメントをここに" %>
    </div>
  </div>
  <%= f.submit "送信する", class: "btn btn-lg btn-base-1 mt-20 pull-right" %>
  <% end %>
```

2行目の<% if @error_comment.present? %>でエラーがあるときのみ、表示されるようにする。＠error_commentは、commentが空の投稿の時のみ再定義される変数のため、showページを表示するときには定義されていない。
<% if @error_comment.present? %>がないと以下のようなエラーが出る。
![_2020-12-15_15.06.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869111/d1be9073-8f81-0d80-143c-335407f2368b.png)

### 4. post_imageのshowアクションの内容をcreateアクションに追記する
コメント投稿が失敗した場合、post_imageのshowページへrenderするようにしてあるが、showのviewを表示するのに必要なインスタンス変数がcommentコントローラのcreateアクションにはないので、追記してやる必要がある。

```ruby:post_images/controller.rb
def show
    @post_image = PostImage.find(params[:id])
    @post_comment = PostComment.new
end
```
追記する。

```ruby:comments/controller.rb
def create
    post_image = PostImage.find(params[:post_image_id])
    comment = current_user.post_comments.new(post_comment_params)
    comment.post_image_id = post_image.id
    if comment.save
	    redirect_to post_image_path(post_image)
	else
		@error_comment = comment

#------post_imageのshowアクションを追加---------------
		@post_image = PostImage.find(params[:id])
	    @post_comment = PostComment.new
#-------------------------------------------------
		render 'post_image/show'
	end
end

private
def comment_params
  params.require(:comment).permit(:comment)
end
```
これでコメント投稿を実行しても、idがないので見つけることができないという以下のようなメッセージが出る。正確にはidはあるけど名称が変わって違うものになっているのでidを見つけることができないという感じ。![_2020-12-15_15.19.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869111/39df7a3d-e1a2-3f75-7795-d63fc402e8cc.png)
rails routesで確認すると、

|Prefix|Verb|URI Pattern|Controller#Action|                                                                         
|:-----:|:-------:|:------:|:------:|
|post_image|GET|/post_images/:id(.:format)|post_image#show|
|post_image_comments|POST|/post_images/:post_image_id/comments(.:format)|comment#create|
showアクションではpost_imagesのパラメーターidは:idであったが、createアクションの場合、パラメーターidはpost_image_idとなっている。そのため、パラメーターidをpost_image_idに変更する。

```ruby:comments/controller.rb
def create
    post_image = PostImage.find(params[:post_image_id])
    comment = current_user.post_comments.new(post_comment_params)
    comment.post_image_id = post_image.id
    if comment.save
	    redirect_to post_image_path(post_image)
	else
		@error_comment = comment
 #----------変更（:id　→　:post_image_id）------------------------
		@post_image = PostImage.find(params[:post_image_id])
 #------------------------------------------------------------
	    @post_comment = PostComment.new
		render 'post_image/show'
	end
end

private
def comment_params
  params.require(:comment).permit(:comment)
end
```

![_2020-12-15_15.37.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869111/0983c222-c00f-adc6-8040-448cab5520cf.png)

# まとめ
非常に回りくどい説明になりましたが、初学者の方でエラー解決に苦労している方などの参考になれば幸いです。