#目標
積み立てた知識を使ってコメント機能を実装していきたい。
テスト駆動開発ですすめたかったがどこから手を付ければいいのか分からなかったので
機能実装から行っていく
#モデル生成
この時点で迷った点がある。
1、コメントモデルを生成して、マイクロポストとは別の物として開発する
→マイクロポスト詳細ページに行けばコメント一覧、コメントフォーム出力(どちらかというとブログ寄り)
2、コメントモデルは作らず「マイクロポスト投稿フォームから”返信”」といった形で特定のマイクロポストへの返信カラムをもったマイクロポストにする(考え方があってるのか、それは分かりません)
→フィードに一つの投稿として出力できる(twitter寄り)
どういった流れかは想像できるのだが今の知識では2番でやる事が難しく思えたので1番を目指す。
ということでモデル生成
$ rails g model comment user:references micropost:references body:text
関連付けてます。
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>
ブラウザ出力を目指します。
.
.
.
<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をいじろうと思ってクラス名を与えましたが、どうもフロントエンドは興味がわきません。。。。
テストはまた別の機会で行おうと思います。
オリジナルアプリのアイデアが固まるまでの事前練習として始めた拡張機能実装でしたが、
こちらを土台に何か作ってもいいかなぁと安易ですが思いました。引き続き拡張していきます。