Help us understand the problem. What is going on with this article?

Rails Tutorial 拡張機能 part.2(コメント機能)

More than 1 year has passed since last update.

目標

積み立てた知識を使ってコメント機能を実装していきたい。
テスト駆動開発ですすめたかったがどこから手を付ければいいのか分からなかったので
機能実装から行っていく

モデル生成

この時点で迷った点がある。
1、コメントモデルを生成して、マイクロポストとは別の物として開発する
→マイクロポスト詳細ページに行けばコメント一覧、コメントフォーム出力(どちらかというとブログ寄り)

2、コメントモデルは作らず「マイクロポスト投稿フォームから”返信”」といった形で特定のマイクロポストへの返信カラムをもったマイクロポストにする(考え方があってるのか、それは分かりません)
→フィードに一つの投稿として出力できる(twitter寄り)

どういった流れかは想像できるのだが今の知識では2番でやる事が難しく思えたので1番を目指す。

ということでモデル生成

$ rails g model comment user:references micropost:references body:text

関連付けてます。

生成されたcommentモデルのマイグレーションファイル
 add_index :comments , [:user_id, :micropost_id, :created_at] #<-インデックスを複合キーにて追加

db:migrateも忘れず。

ルーティング設定

自分でルーティングを考えるのは初めてなので悩んだ。
使いたいurlとして
”/micropost/:micropost_id/comments” -> commentのcreateアクション

であると考えていった

フォローイング機能の時のネスト機能を参考にくんでみた
コードの左の ×印が消えなかったりエラーを起こしたり何度もありましたが、下記にまとまりました。

resources :microposts , only: [:create , :destroy ] do
        resources :comments
  end

ルーティング確認

$ rails routes


 micropost_comments GET    /microposts/:micropost_id/comments(.:format)          comments#index
                        POST   /microposts/:micropost_id/comments(.:format)          comments#create
  new_micropost_comment GET    /microposts/:micropost_id/comments/new(.:format)      comments#new
 edit_micropost_comment GET    /microposts/:micropost_id/comments/:id/edit(.:format) comments#edit
      micropost_comment GET    /microposts/:micropost_id/comments/:id(.:format)      comments#show
                        PATCH  /microposts/:micropost_id/comments/:id(.:format)      comments#update
                        PUT    /microposts/:micropost_id/comments/:id(.:format)      comments#update
                        DELETE /microposts/:micropost_id/comments/:id(.:format)      comments#destroy


必要の無いルーティングまでできてしまったけどもう考えるのに疲れたので先に進みます。
リファクタリングは機能全体が形になってから。

ビュー作成

何処にコメント置くかぁと考えた結果上記のようにブログ式を取敢えず目指そうと考えました。
多少SNS式アプリケーションと考えるなら野暮ったいのかなと思ったのですが、ここはもうとにかくいったん形にしたい。

tutorial内ではマイクロポストの詳細ページは作らなかったのでこちらを先に生成して、
特定のマイクロポストに対するコメントの一覧、その特定のマイクロポストに対するコメントフォームを作ろうと考えた

micropostのshowページ作成

/micropostsフォルダ内にshowアクション用のページを作成

<li id="micropost-<%= #{micropost.id} %>">
  <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
  <span class="user"><%= link_to micropost.user.name, micropost.user %></span>
  <span class="content">
      <%= link_to micropost.content , micropost %>
      <%= image_tag micropost.picture.url if micropost.picture?%>
  </span>

*これは同じ/micropostsフォルダ内にあったフィード用のmairopostsパーシャルを丸々引っ張って来ました。
showアクションを定義します

マイクロポストコントローラ
  def show 
    @micropost = Micropost.find(params[:id])
    @comment = Comment.new
    @user = User.find_by(id: @micropost.user_id)

  end

*この後生成されるコメント用のインスタンス変数も作成してます

インスタンス変数を作成したのでshowページの編集が必要になります

<div id="micropost-<%= #{@micropost.id} %>">
  <%= link_to gravatar_for(@micropost.user, size: 120), @micropost.user ,:class=>"user-name" %>
  <br/>
  <span class="user"><%= link_to "#{@micropost.user.name}", @micropost.user  %></span>
  <br/>
  <span class="micropost_content">
      <%= @micropost.content %>
      <%= image_tag @micropost.picture.url if @micropost.picture?%>
  </span>

*見易くするためブランクタグの差し込み、ユーザー画像の調整、後ほどCSSを変えたいのでクラス名もいじってます。

最後にルーティングのonlyオプションにshowを追加すればマイクロポスト詳細ページとしてブラウザで確認出来ます。...はずです(後日復習を含めて記事にしてる為うろ覚え)

Commentリソースの生成

この課題の鬼門です。
この先は
1、railsコンソールで生成出来る事を確認
2、micropost/showページにコメント一覧としてコメントを表示することから目指しました。コンソールで理想のコメントがモデルから作れるのか、またブラウザに出力できるのか。一つ一つ手さぐりです。(例えばコメント生成コマンド .create  を打込んだ後Comment.countで数が増えているか...や、Comment.allを打込んでカラムが適切か...などなど。コンソールって意味あんのかよっておもってtutorialを進めてましたが、必要や)

railsコンソールにてコメント生成

コンソール画面にて、関連付けたカラムを考えながら初コメントを作りました。

Comment.create(user_id:1 , miicropst_id:300 , body:"gyaaあああ")

と、ここで考えるべきなのがバリデーションです。
空のコメントは投稿成功しちゃだめだし、ログインしてない人がコメント出来てしまったらコメントし放題の結果炎上してしまう等可能性があるので、制限を掛けます。

コメントモデル
class Comment < ApplicationRecord
  belongs_to :user
  belongs_to :micropost
  validates :body , presence: true
  validates :user_id , presence: true
end

ここでバリデーション設けなくちゃ!と考えた起点の存在性の制限をコードに起こすのと同時に、マイグレーションファイルを思い出して関連付けをします。(userモデルとmicropostモデルにも)

完了したらコンソールでComment.create、Commentインスタンスが作成されるはずです。(はずです。エラーは知りません)

[3] pry(main)> Comment.create(user_id:1 , micropost_id:300 , body:"あああ")                                                                                                                                  
   (0.2ms)  begin transaction
  User Load (0.6ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  Micropost Load (0.2ms)  SELECT  "microposts".* FROM "microposts" WHERE "microposts"."id" = ? ORDER BY "microposts"."created_at" DESC LIMIT ?  [["id", 300], ["LIMIT", 1]]
  SQL (0.7ms)  INSERT INTO "comments" ("user_id", "micropost_id", "body", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?)  [["user_id", 1], ["micropost_id", 300], ["body", "あああ"], ["created_at", "2017-12-10 17:27:04.767558"], ["updated_at", "2017-12-10 17:27:04.767558"]]
   (14.1ms)  commit transaction
=> #<Comment:0x0000000507e0e0 id: 1, user_id: 1, micropost_id: 300, body: "あああ", created_at: Sun, 10 Dec 2017 17:27:04 UTC +00:00, updated_at: Sun, 10 Dec 2017 17:27:04 UTC +00:00, picture: nil>

ブラウザ出力を目指します。

マイクロポスト/showページ
.
.
.

  <div class="comment_index">
  <h3>Comments</h3>
  <% @micropost.comments.each do |comment| %>
         <%= link_to gravatar_for(comment.user, size: 50), comment.user %>
         <p><%= link_to "#{comment.user.name}" , @user %>: <%= comment.body %>
         <span> <%= image_tag comment.picture.url if comment.picture?%></span>

    <span class= "batu"><%= link_to "x" , [@micropost,comment] , method: :delete if current_user?(comment.user) %></span></p>

    <hr/>
  <% end %>
  </div>

言葉で流れを表すとと
showアクションで定義した投稿に対するコメント(複数:has_many)をeachメソッドで取りだす。
プロフ画像の出力。tutorialから引用、プロフ画像50px。
ユーザーネームをブラウザ出力(link_to第一引数(eachで取りだされているコメントのユーザーネーム))リンク先はそのユーザーの詳細ページ : コメント内容  。
画像があるなら画像出力(tutorialから)。
if文で、コメントしたユーザが今のユーザーなら(current_userはtutorialで定義しました)”x”出力、リンク先はcomment.desroy。

この時点でコンソールで作ったコメントは、指定したmicropost_idの詳細ページに出るはずです。

コメント入力フォーム

 <div class="comment-form">
      <%= form_for([@micropost , @comment]) do |f| %>
    <%= render 'shared/error_messages', object: f.object %>
    <div class="field">
      <%= f.text_area :body, placeholder: "Send comment..." %>
    </div>
    <%= f.submit "Post", class: "btn btn-primary" %>
    <span class="picture">
      <%= f.file_field :picture, accept: 'image/jpeg,image/gif,image/png' %>
    </span>
    <% end %>

  <script type="text/javascript">
    $('#micropost_picture').bind('change', function() {
      var size_in_megabytes = this.files[0].size/500/500;
      if (size_in_megabytes > 5) {
        alert('Maximum file size is 5MB. Please choose a smaller file.');
      }
    });
  </script>
  </div> 

micropost投稿フォームを丸々引っ張ってます。引数、class名、を整えただけだったと思います。
後はコメントにしては画像がデカ過ぎたりplaceholderをコメント仕様に。

Commentコントローラ

(正直めんどくさがり+実装後数日たっている為、詳しく書ききれないと言うのもあって作業が前後しています。)
やっとコントローラを実装します。

これまでにブラウザにてコメントフォーム、”x”のdeleteリンクもできてるのでコントローラを設定出来れば終わるはず。

$ rails g controller comments

-------------------------------------------------------

class CommentsController < ApplicationController

  def create
    @micropost=Micropost.find(params[:micropost_id])
    @comment = @micropost.comments.build(comment_params)
    @comment.user = current_user
    if @comment.save
      redirect_to micropost_path(@micropost)
    else
      render "microposts/show"
    end

  end

  def destroy
    @comment = Comment.find_by(id: params[:id])
    @comment.destroy
     redirect_to  request.referrer || root_url
  end

  private 

  def comment_params
    params.require(:comment).permit(:body , :picture)
  end


end

・ストロングパラメータが登場、注意
(pictureカラムを許可するのを忘れていて、画像を選択してフォームから送信しても、サーバーログでは画像をうけ取ってる反応はするのにも関わらずブラウザに出力出来ず小一時間はまりました。)

・ @comment.user = current_user
このコードはあるwebページを参考にしたのですが見つかりませんでした。
このコードが無いとなぜかエラーが出ます。

・Redirect_toの引数はリクエストしたページが無ければホームに。です。

これで一通りは完了です。

おまけ

フィードの出力にて投稿に対するコメント数を出力したい。

  <span><%= "コメント(#{micropost.comments.count})"%></span>

こいつをあるべきとこへ埋め込めば出ます。

今後

今出来る事をこなしただけなので理想の形は別にあります。

最初に書いたようにコメントモデルとしてのコメントはSNSでやるならやっぱりスタイリッシュでは
ないと思いますし、今の実装ですとユーザー名がリンクなので字が青く、またリンクとしてのクリック受付範囲が小さいなあとおもいます。

レイアウトもパッとしないなぁと、編集と同時に後にCSSをいじろうと思ってクラス名を与えましたが、どうもフロントエンドは興味がわきません。。。。
テストはまた別の機会で行おうと思います。

オリジナルアプリのアイデアが固まるまでの事前練習として始めた拡張機能実装でしたが、
こちらを土台に何か作ってもいいかなぁと安易ですが思いました。引き続き拡張していきます。

krppppp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした