成果物の作成10 投稿記事に対するコメント機能の追加
今回はコメント機能を追加していこうと思います。 独自にComentモデルを作り、PostデータとコメントするUserモデルとの関係性も記述しなければならないので、少し難易度が高いです。
どんなモデルを作るか
コメントはさほど長い文章で無くても構わないと思うのでstring型で行こうと思います。
Commentモデルはcontentをstring型で作成し、外部主キーをPost_idとし、コメントする自分自身Userモデルとの関連付けにUser_idのカラムも作ります。
こんな感じでしょうか
Commentモデル
外部主キー | コメント文 | 投稿者のid |
---|---|---|
post_id | content | user_id |
モデルの作成
rails g model comment content:string user:references post:references
post_idはコメントをたくさん持てる= has_many
コメントはpost_idを一つだけ持てる=belong_to
コメントは投稿者(つまりcurrent_user)のuser_id一つだけ持っている
投稿者(current_user)はコメントををたくさん持てる=has_many
Railsチュートリアルの復習ですが、関係性としてはこんな感じになると思います。
そして、先程のCommentモデルの作成でcomment.rbに
class Comment < ApplicationRecord belongs_to :user belongs_to :post end
が含まれているので2. 3はもう大丈夫です。
残りの1. 4をuser.rbとpost.rbに追加します。
has_many :comments
そしてrails db:migrate.
リソースの追加
以下の入れ子構造の書き方でpost_idと絡めたresouceとします。
resources :posts, only: [:show,:create, :destroy] do resources :comments, only: [:create] #/posts/posts_id/comentにpostアクセスでcreateアクションを実行 end
コメントの表示とフォーム画面の追加
以下で記事詳細表示ページにコメントの表示部を追加します
<% @comments.each do |c| %> <div> <a href="/users/<%= @post.user.id %>"><%= c.user.name %></a> <%= c.content %> <hr> </div> <% end %>
form_forで、ネストされたURLにどうやってpostすればいいのか悩んだんですが、 インスタンス変数を配列として渡せば、よしなにしてくれるようです。
例えば/posts/posts_id/commentリソースにpostしたければ
<%= form_for [@post, @comment] do |f| %> <% end %>
等と書くといいようです。
よって、以下を記事詳細画面に追加します
<%= form_for [@post, @comment] do |f| %> <%= f.text_field :content %> <br> <%= f.submit 'コメントする' %> <% end %>
後はコントローラを下のように書けば動くはず…
class CommentsController < ApplicationController def create #/posts/posts_id/comentにpostアクセスでcreateアクションを実行。コメントを投稿する @comment = Comment.new(comment_params) #paramsから@commentを作成 @comment.user_id = current_user.id #ログインしている自分自身のidを代入 if @comment.save #もし無事に保存できたら flash[:success] = "comment created!" redirect_back(fallback_location: root_path) else flash[:danger] = "comment abort" redirect_back(fallback_location: root_path) end end private def comment_params params.require(:comment).permit(:content,:post) end end
ここで問題発生。DBにcommentが保存できない。ここでまたも大ハマリしてしまいました。。(泣)
色々試してみるけど、一向に保存してくれない
原因を探っていくために、どもうsaveのままだとただfalseを返すだけなので、save!ににして例外を返してもらうようにする(エラー内容を吐いてくれる)
Validation failed: Post must exist, Post can’t be blank
とのこと。Postがない?@commentにはpost_idはあってもPostデータそのものは含まれていないはずだが…と考えながら調べてみると
以下の記事を発見
どうも紐付いているデータを検査しているので、紐付いているPostが空だとエラーが出るらしい。
つまり、紐付いているPostが空ということになります
Postデータと紐付いた形で@comment を作りたいが。。。小一時間悩んでいろんな資料を見たり、ググったり考えました。
ふと、外部主キーpost_id - content - user_id という形で紐付いているはずなので、
post_idの部分にUrlのパラメーターからPostデータを引っ張ってきて、Postを作り.commentsでつなげればPostモデルと紐付いたcommentデータが作れるのではないかと気づき、実際やってみました
def create #/posts/posts_id/comentにpostアクセスでcreateアクションを実行。コメントを投稿する @comment = Post.find(params[:post_id]).comments.new(comment_params) #paramsから@commentを作成。その際Postも紐付ける @comment.user_id = current_user.id #ログインしている自分自身のidを代入 if @comment.save! #もし無事に保存できたら flash[:success] = "comment created!" redirect_back(fallback_location: root_path) else flash[:danger] = "comment Error" redirect_back(fallback_location: root_path) end end
結果無事成功!commentと紐付いているPostデータが取得できていなかったんですね!
後はテストを書いて成功したらgit push してデプロイして完成です。