投稿に対する空コメントでInvalid dateが出たときの対処法

##目的
自作アプリで投稿にコメントができる機能を作成。
その際出てくるエラーの解決に時間がかかったので、解決までの考え方を備忘録として記事にしました。


##環境
windows10 home
Ubuntu 20.04.1 on WSL2
rails 6.0.3
ruby 2.6.6


##起きた現象
空のコメントを送信するとInvalid dateエラーが発生。
![eae5c07bf8195a5b11daa98e2106ada6](https://user-images.githubusercontent.com/69103382/138587293-696191b6-0d70-4411-96e9-b9912330c119.png)


**コメントモデルのカラムは以下に記載**

* id
* content(コメントの中身)
* user_id(外部キー)
* micropost_id(外部キー)
* created_at
* updated_at


##①ビューを確認

```microposts/show.html.erb
<% @comments.each do |c| %>
  <div class="d-flex"> 
    <div class="d-flex"> 
      <%= link_to user_path(c.user) do %> 
          <%= image_tag c.user.portrait.to_s, size: "40x40" %> 
      <% end %> 
    </div> 
    <div class="d-flex flex-column"> 
      <small class="text-muted"><%= c.user.name %></small> 
      <div> 
        <small><%= simple_format(h(c.content)) %></small> 
      </div> 
    </div> 
  </div> 
  <div class="d-flex flex-row mb-3 border-bottom border-dark"> 
    <div class="d-flex align-items-center"> 
      <small><%= Date.parse(c.created_at.to_s).strftime("%m月%d日") %></small>  <%# ここでエラー発生 %> 
    </div> 
    <% if c.user.id == current_user.id %> 
      <%= link_to "削除", micropost_comment_path(@micropost, c), method: :delete, data: {confirm: "コメントを削除しますか？"}, class: "btn btn-sm btn-light ml-2" %> 
    <% end %> 
  </div> 
<% end %>
```

日付を「〇〇月〇〇日」の形で表示させるところでエラーが発生している。
このメソッドは他のページでも使用しており、その際エラーがでていないことから
他の部分に不具合があり、それがここで引っかかっているのではないかと考えました。


##②バリデーションを確認
まずはコメントモデルのバリデーションがちゃんと記述できているか確かめる。

```comment.rb
class Comment < ApplicationRecord
  belongs_to :user
  belongs_to :micropost

  validates :content, presence: true, length: {maximum: 200}
  validates :user_id, {presence: true}
  validates :micropost_id, {presence: true}
end
```

モデルのバリデーションに問題はなさそう。
とすると、データベースのNotNull制約に何か問題があるのかも。

参考：[【Rails】「テーブルのカラムに定義するNot Null制約」と「モデルに定義するバリデーション（presence: true）」の挙動の違い。](https://qiita.com/wonder_meet/items/fa804f0d436a29c97460)

データベースの中身を調べてみる。

```
mysql> desc comments;
+--------------+-------------+------+-----+---------+----------------+
| Field        | Type        | Null | Key | Default | Extra          |
+--------------+-------------+------+-----+---------+----------------+
| id           | bigint      | NO   | PRI | NULL    | auto_increment |
| content      | text        | NO   |     | NULL    |                |
| user_id      | bigint      | NO   | MUL | NULL    |                |
| micropost_id | bigint      | NO   | MUL | NULL    |                |
| created_at   | datetime(6) | NO   |     | NULL    |                |
| updated_at   | datetime(6) | NO   |     | NULL    |                |
+--------------+-------------+------+-----+---------+----------------+
6 rows in set (0.03 sec)
```

ちゃんと:contentカラムにNotNull制約がついているのを確認。
どうやらバリデーションに問題はないみたい。


##③コンソールで確認
次に空投稿が送信されたときの動きをコンソールで調べてみる。

```
pry(main)> c = Comment.new(user_id:33, micropost_id:76, content:"")
=> #<Comment:0x0000563201d95c18
 id: nil,
 content: "",
 user_id: 33,
 micropost_id: 76,
 created_at: nil,
 updated_at: nil>

pry(main)> c.save!
ROLLBACK
ActiveRecord::RecordInvalid: バリデーションに失敗しました: Contentを入力してください
from /home/XXXXX/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.5/lib/active_record/validations.rb:80:in `raise_validation_error'
```

contentが空のコメントはちゃんとバリデーションで引っかかっている。
...ん？じゃあなんでInvalid dateエラーになるんだ？
空コメントのインスタンスが何らかの原因でcreateされてるってこと？


##④コントローラーを確認
投稿詳細ページにコメントを表示させるので、
コメントとマイクロポスト2つのコントローラーを確認する。

```comments_controller.rb
class CommentsController < ApplicationController
  before_action :authenticate_user!
  before_action :set_micropost

  def create
    @comments = @micropost.comments
    @comment = @micropost.comments.new(comment_params)
    @comment.user = current_user
    if @comment.save
      flash[:primary] = "コメントしました"
      redirect_to @micropost
    else
      flash.now[:danger] = "コメントを入力してください"
      render("microposts/show")
    end
  end
・・・
（略）
・・・
  private
    def set_micropost
      @micropost = Micropost.find_by(id: params[:micropost_id])
    end

    def comment_params
      params.required(:comment).permit(:content).merge(user_id: current_user.id, micropost_id: params[:micropost_id])
    end
end
```
```microposts_controller.rb
def show
  @micropost = Micropost.find_by(id: params[:id])
  @comments = @micropost.comments
  @comment = Comment.new # コメントフォームのインスタンスはmicropost/showで作成
end
```

マイクロポストコントローラーに問題はなさそう。
そうなるとコメントコントローラーのcreateアクションに原因がありそう。


##⑤原因判明
createアクションを見ていて最初に定義した@commentsと次に定義した@commentが
@micropost.commentsに紐付いているということに気づいた。
createされた@commentはすぐに@micropost.commentsとして組み込まれる。

組み込まれた@commentをビューが表示しようとするが、
created_atがnilなのでInvalid dateエラーになる。
つまり@commentを定義するとき@micropostと紐付けるのをやめればいいのでは？

**【変更前】**

```comment_controller.rb
・・・
def create
    @comments = @micropost.comments
    @comment = @micropost.comments.new(comment_params)
    @comment.user = current_user
    if @comment.save
      flash[:primary] = "コメントしました"
      redirect_to @micropost
    else
      flash.now[:danger] = "コメントを入力してください"
      render("microposts/show")
    end
  end
・・・
```

**【変更後】**

```comment_controller.rb
・・・
def create
    @comments = @micropost.comments
    @comment = Comment.new(comment_params)  # 変更
    @comment.user = current_user
    if @comment.save
      flash[:primary] = "コメントしました"
      redirect_to @micropost
    else
      flash.now[:danger] = "コメントを入力してください"
      render("microposts/show")
    end
  end
・・・
```

無事成功。
書いてみると単純なことですがものすごい時間取られました。
![image](https://user-images.githubusercontent.com/69103382/138589478-432596a7-8300-4895-b617-bc8ce5715ca0.png)