その2です。
##編集画面作成
アクション、ビューの作成あたりは問題なし。
問題は
<h2>編集する</h2>
<%= form_for @post, url: post_path(@post.id) do |f| %>
<p>
<%= f.text_field :title,placeholder:"タイトルをいれてください" %>
<% if @post.errors.messages[:title].any? %>
<span class="error"><%= @post.errors.messages[:title] %></span>
<% end %>
</p>
<p>
<%= f.text_area :body,placeholder:"本文をいれてください" %>
<% if @post.errors.messages[:body].any? %>
<span class="error"><%= @post.errors.messages[:body] %></span>
<% end %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
完成した上記の最初の行、
<%= form_for @post, url: post_path(@post.id) do |f| %>
ここである。
動画では、newの画面とほぼ同じなので~の流れからコピペして、
form_forのあとをすこし変更している。
ここでnewの同じ部分のコードを下記に
<%= form_for :post, url: posts_path do |f| %>
違いはform_for直後とリンク先である。リンク先は問題ない。
問題は、form_for @post(←edit)とform_for :post(←new)である。
投稿その1でform_for :post(←new)が:postなのはpostモデルを参照しているからとか書いた気がする。
確かに動画ではそう言っている。
ではform_for @post(←edit)ではどんなコメントなのかというと、、、
form_forはインスタンス変数の@postにしてあげます。
とのこと。
これはform_forをしっかり理解せねばならないのではと感じる。
似たことを書いてる記事を発見。
この[Ⅰ]が近いかなと。
ただ、やっぱり:postが成立する理由がわからないので
newのほうを@postにかえてみる。
あれ、いけた。
なんで。行けそうな気がしたけど、そんなもんなのだろうか?
わかる方、コメントください。
で、肝心のupdateアクションですが、
def update
@post = Post.find(params[:id])
if @post.update(post_params)
redirect_to root_path
else
render "edit"
end
end
ここで気づいたがrenderは""が必要なんですね。
redirect_toはいらんけども。
なんかプロゲートではeditの時はname属性でもともとのやつをもらってきてとか何とかで
めんどくさかった記憶があるんだけど、、、ここではなんか一発。
この違いもどこかで検証しないといけませんね。。。
ここでも、ストロングパラメータを使っていることには注意!
##パーシャルの作成
newとeditで重複多いのでそこをパーシャルで。これは問題なし。
ポイントはパーシャルのform_for部分。
<%= form_for @post do |f| %>
newとeditではリンク先をきちきちつけてましたがパーシャルにすると区別できなくなります。
でもここは必殺Railsさんがかってにリンク先をわけてくれるそうです。
わけてくれる理由としては
resources :posts
上記のおかげらしいです。
ec2-user:~/environment/myblog (master) $ rails routes
Prefix Verb URI Pattern Controller#Action
posts GET /posts(.:format) posts#index
POST /posts(.:format) posts#create
new_post GET /posts/new(.:format) posts#new
edit_post GET /posts/:id/edit(.:format) posts#edit
post GET /posts/:id(.:format) posts#show
PATCH /posts/:id(.:format) posts#update
PUT /posts/:id(.:format) posts#update
DELETE /posts/:id(.:format) posts#destroy
root GET / posts#index
厳密にいくと上記ですかね。
つまり、createとupdateへのつなぎは明示的に書かなくてもOKということです。
それは、元のデータ(editのときだけあるもの)の有無でRailsがリンク先をかえてくれるということみたい。
なんとも素晴らしいですね。
##記事の削除
まずは削除ボタンをトップに追加。
<%=link_to "[削除する]", post_path(post.id), method: :delete, class: "command", data: {confirm: "削除しますか?"} %>
いくつか謎なところが。
まず、deleteに関してはpost、getと異なり明示的にかいてやらないといけない。
なので、method: :deleteを記述。
これはなぜ??
ec2-user:~/environment/myblog (master) $ rails routes
Prefix Verb URI Pattern Controller#Action
posts GET /posts(.:format) posts#index
POST /posts(.:format) posts#create
new_post GET /posts/new(.:format) posts#new
edit_post GET /posts/:id/edit(.:format) posts#edit
post GET /posts/:id(.:format) posts#show
PATCH /posts/:id(.:format) posts#update
PUT /posts/:id(.:format) posts#update
DELETE /posts/:id(.:format) posts#destroy
root GET / posts#index
これは理解が及びません。調べてもわからない。
また、method: :deleteが「delete」なことも注目。destroyはアクション側でだけ。
ここもわかる方いたらおしえてください。
data: {confirm: "削除しますか?"}
これはもう覚えます。
##コメント作成
ec2-user:~/environment/myblog (master) $ rails g model Comment body:string post:references
Running via Spring preloader in process 5550
invoke active_record
create db/migrate/20200511015328_create_comments.rb
create app/models/comment.rb
invoke test_unit
create test/models/comment_test.rb
create test/fixtures/comments.yml
ec2-user:~/environment/myblog (master) $ rails db:migrate
== 20200511015328 CreateComments: migrating ===================================
-- create_table(:comments)
-> 0.0041s
== 20200511015328 CreateComments: migrated (0.0046s) ==========================
まずはコメントモデルの作成。
大事なのは、post:referencesの部分。
コメントは各投稿に対して行うのでコメントモデルとポストモデルは紐づけねばならない。
これはチュートリアルでも結構あったし、理解が足りない点だった。
プロゲートではさくっといってたけどよくわからんとこだった。
で、上記でモデルを作成すると、、、
class Comment < ApplicationRecord
belongs_to :post
end
コメントモデルの中にはbelong_to:postとなり、ポストモデルとの関連が書かれる。
これだと一方的なので
class Post < ApplicationRecord
has_many :comments
validates :title,presence: true, length: {minimum: 3,message: "文字数が不足しています。3文字以上記入してください。" }
validates :body,presence: true, length: {minimum: 1,message: "文字数が不足しています。1文字以上記入してください。" }
end
こちらにはhas_manyを追記。1:多数なので複数形にすること注意。
これをすることで、両モデルが連携(?)し、今後は
@post.comments
を使えるようになる。
さらに、コメントのルーティングも作成しなければならない。
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
resources :posts do
resources :comments
end
root "posts#index"
end
これで、再度、ルーティングの確認。
ec2-user:~/environment/myblog (master) $ rails routes
Prefix Verb URI Pattern Controller#Action
post_comments GET /posts/:post_id/comments(.:format) comments#index
POST /posts/:post_id/comments(.:format) comments#create
new_post_comment GET /posts/:post_id/comments/new(.:format) comments#new
edit_post_comment GET /posts/:post_id/comments/:id/edit(.:format) comments#edit
post_comment GET /posts/:post_id/comments/:id(.:format) comments#show
PATCH /posts/:post_id/comments/:id(.:format) comments#update
PUT /posts/:post_id/comments/:id(.:format) comments#update
DELETE /posts/:post_id/comments/:id(.:format) comments#destroy
posts GET /posts(.:format) posts#index
POST /posts(.:format) posts#create
new_post GET /posts/new(.:format) posts#new
edit_post GET /posts/:id/edit(.:format) posts#edit
post GET /posts/:id(.:format) posts#show
PATCH /posts/:id(.:format) posts#update
PUT /posts/:id(.:format) posts#update
DELETE /posts/:id(.:format) posts#destroy
root GET / posts#index
コメントを混ぜてのルーティングがでた。
※チュートリアルでも逐一これを確認しながらやればよかった。。。
そして、showへの投稿機能の追加
パーシャルをコピペしてform_for追加。
重要なform_forの後ろですが、、、
<%= form_for ([@post,@post.comments.build]) do |f| %>
@post,@post.comment.build
理由としては、まずは今回はコメントのnewなのでルーティングを確認
POST /posts/:post_id/comments(.:format) comments#create
※動画ではnewを使うといっているがコメントでcreateという訂正がある
みると、必要なのは:post_idとコメントの新しいオブジェクト。
なので、@postでidというかどの投稿かを取得、そして@post.comment.buildでコメントのオブジェクト作成。
よって、この二つを渡してあげる。
であってます??
ここはかなりポイントのはず。。。
そしてコメントコントローラー
class CommentsController < ApplicationController
def create
@post = Post.find(params[:post_id])
@post.comment.create(comment_params)
redirect_to post_path(post.id)
end
private
def comment_params
params.require(:comment).permit(:body)
end
end
createアクション内部が結構やっかいで、、、
@post = Post.find(params[:post_id])
ここの[:post_id]部分ですが、、、
POST /posts/:post_id/comments(.:format) comments#create
上記のようになってるから、的な感じなのですが、、、
なぜ:post_idなのかわからない、、、:idじゃだめなの。。。?
深ぼってみた。
まず、各ルーティングを再度↓に
ec2-user:~/environment/myblog (master) $ rails routes
Prefix Verb URI Pattern Controller#Action
post_comments GET /posts/:post_id/comments(.:format) comments#index
POST /posts/:post_id/comments(.:format) comments#create
new_post_comment GET /posts/:post_id/comments/new(.:format) comments#new
edit_post_comment GET /posts/:post_id/comments/:id/edit(.:format) comments#edit
post_comment GET /posts/:post_id/comments/:id(.:format) comments#show
PATCH /posts/:post_id/comments/:id(.:format) comments#update
PUT /posts/:post_id/comments/:id(.:format) comments#update
DELETE /posts/:post_id/comments/:id(.:format) comments#destroy
posts GET /posts(.:format) posts#index
POST /posts(.:format) posts#create
new_post GET /posts/new(.:format) posts#new
edit_post GET /posts/:id/edit(.:format) posts#edit
post GET /posts/:id(.:format) posts#show
PATCH /posts/:id(.:format) posts#update
PUT /posts/:id(.:format) posts#update
DELETE /posts/:id(.:format) posts#destroy
root GET / posts#index
それぞれのcreate部分をぬいてくると
POST /posts/:post_id/comments(.:format) comments#create
POST /posts(.:format) posts#create
別になにも感じない。それぞれのアクションも
#postsコントローラー
def create
#render plain: params[:post].inspect
@post = Post.new(post_params)
if @post.save
redirect_to root_path
else
# render plain:@post.errors.inspect
render "new"
end
end
#commentsコントローラー
def create
@post = Post.find(params[:post_id])
@post.comments.create(comment_params)
redirect_to post_path(post.id)
end
特になにも思い当たることなし。
→すみません、なぜ:post_idなのかわからない、、、:idじゃだめなの。。。?を検証してます
しょうがないので、もう一度ルーティングをみてみると、
post_comment GET /posts/:post_id/comments/:id(.:format) comments#show
post GET /posts/:id(.:format) posts#show
showを比較してみるとURI Patternが、:post_idと:idでことなる。
ここがポイントそう。
#postsコントローラー
def show
@post = Post.find(params[:id])
end
↑のようにpostsコントローラーではparams[:id]を使っている。
完全に妄想だけども、、、
今回のコメント投稿に関する部分に関しては、
コメント投稿ボタンを押すタイミングでコメントコントローラーのcreateアクションに送られる(comments#create)
そして、まずそこで現状のページを取得し(@post = Post.find(params[:post_id]))、
※ここで、コメントコントローラーにいってるので
POST /posts/:post_id/comments(.:format) comments#create
これが発動し、現状のページを取得するには:post_idにしないといけない、となる
コメントを保存(@post.comments.create(comment_params))、
最後にリダイレクト(redirect_to post_path(post.id))。
ですかね、妄想的には。
まちがってたらご指摘ください。もうわかりません。
さらに進んでコメント投稿をしてみようとすると、エラー
undefined local variable or method `post' for #<CommentsController:0x00007f4a3def98e8> Did you mean? @post
def create
@post = Post.find(params[:post_id])
@post.comment.create(comment_params)
redirect_to post_path(post.id)
end
ここのredirect_toの引数が@postじゃねえのかと。
ここで、引数に関してRubyの基本に立ち返って考えてみると、、、
リダイレクト先に関しては、コメント投稿と同じページ、つまり、showのページ、いまいるページ。
引数は、そこに当てはまるものを上からとってくるみたいなものだったはず。
と、考えると、アクションの最初に今いるページを取得して、@postに代入しているじゃないか。と。
よって、redirect_to post_path(@post)となる。
※すべて妄想です
ここでいったん、コメント追記はOK。
##コメント削除
showページを修正
<h2><%= @post.title %></h2>
<P><%= simple_format @post.body %></P>
<h3>コメント</h3>
<% if @post.comments.any? %>
<ul>
<% @post.comments.each do |comment| %>
<li>
<%= comment.body %>
<%=link_to "[削除する]", post_comment_path(@post,comment), method: :delete, class: "command", data: {confirm: "削除しますか?"} %>
</li>
<% end %>
</ul>
<% end %>
<%= form_for ([@post,@post.comments.build]) do |f| %>
<p>
<%= f.text_field :body %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
気になるのは
<%=link_to "[削除する]", post_comment_path(@post,comment), method: :delete, class: "command", data: {confirm: "削除しますか?"} %>
削除リンク箇所のpost_comment_path(@post,comment)部分。
post_comment_pathに関してはルーティングから理解。
引数(@post,comment)に関しては説明からいくと
<% @post.comments.each do |comment| %>部分の
@postとcommentを渡してあげればよいので、とのこと。
なるほど。
これで削除機能も実装完了。
以上になります。
ドットインストール入門のほう、チュートリアルで実装した開発環境で問題なくできました。
プロゲートからチュートリアルいくより、ドットインストールはさむほうが断然おススメ。