ドットインストールRails入門編をcloud9でやってみる：その2

その２です。

##編集画面作成

アクション、ビューの作成あたりは問題なし。

問題は

```ruby

<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 %>
```

完成した上記の最初の行、

```ruby

<%= form_for @post, url: post_path(@post.id) do |f| %>

```

ここである。

動画では、newの画面とほぼ同じなので~の流れからコピペして、
form_forのあとをすこし変更している。
ここでnewの同じ部分のコードを下記に

```ruby

<%= form_for :post, url: posts_path do |f| %>

```

違いはform_for直後とリンク先である。リンク先は問題ない。
問題は、form_for @post(←edit)とform_for :post(←new)である。

投稿その１でform_for :post(←new)が:postなのはpostモデルを参照しているからとか書いた気がする。
確かに動画ではそう言っている。

ではform_for @post(←edit)ではどんなコメントなのかというと、、、
form_forはインスタンス変数の@postにしてあげます。

とのこと。

これはform_forをしっかり理解せねばならないのではと感じる。

似たことを書いてる記事を発見。

[こちら](https://qiita.com/duka/items/0e9a4034056e37fd02b9)

この[Ⅰ]が近いかなと。

ただ、やっぱり:postが成立する理由がわからないので
newのほうを@postにかえてみる。

あれ、いけた。
なんで。行けそうな気がしたけど、そんなもんなのだろうか？
わかる方、コメントください。


で、肝心のupdateアクションですが、

```ruby
  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部分。

```ruby
<%= form_for @post do |f| %>
```

newとeditではリンク先をきちきちつけてましたがパーシャルにすると区別できなくなります。
でもここは必殺Railsさんがかってにリンク先をわけてくれるそうです。
わけてくれる理由としては

```ruby
resources :posts
```

上記のおかげらしいです。

```ruby
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がリンク先をかえてくれるということみたい。
なんとも素晴らしいですね。


##記事の削除

まずは削除ボタンをトップに追加。

```ruby
<%=link_to "[削除する]", post_path(post.id), method: :delete, class: "command", data: {confirm: "削除しますか?"} %>
```

いくつか謎なところが。

まず、deleteに関してはpost、getと異なり明示的にかいてやらないといけない。
なので、method: :deleteを記述。

これはなぜ？？

```ruby
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: "削除しますか?"}

これはもう覚えます。


##コメント作成

```ruby
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の部分。

コメントは各投稿に対して行うのでコメントモデルとポストモデルは紐づけねばならない。
これはチュートリアルでも結構あったし、理解が足りない点だった。
プロゲートではさくっといってたけどよくわからんとこだった。

で、上記でモデルを作成すると、、、

```ruby
class Comment < ApplicationRecord
  belongs_to :post
end
```

コメントモデルの中にはbelong_to:postとなり、ポストモデルとの関連が書かれる。

これだと一方的なので

```ruby
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：多数なので複数形にすること注意。

これをすることで、両モデルが連携(？)し、今後は

```ruby
@post.comments
```

を使えるようになる。

さらに、コメントのルーティングも作成しなければならない。

```ruby
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
```

これで、再度、ルーティングの確認。

```ruby
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の後ろですが、、、

```ruby
<%= form_for ([@post,@post.comments.build]) do |f| %>
```

@post,@post.comment.build

理由としては、まずは今回はコメントのnewなのでルーティングを確認

```ruby
 POST   /posts/:post_id/comments(.:format)          comments#create
```
※動画ではnewを使うといっているがコメントでcreateという訂正がある

みると、必要なのは:post_idとコメントの新しいオブジェクト。
なので、@postでidというかどの投稿かを取得、そして@post.comment.buildでコメントのオブジェクト作成。
よって、この二つを渡してあげる。

であってます？？

ここはかなりポイントのはず。。。

そしてコメントコントローラー

```ruby
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]部分ですが、、、

```ruby
 POST   /posts/:post_id/comments(.:format)          comments#create
```

上記のようになってるから、的な感じなのですが、、、
なぜ:post_idなのかわからない、、、:idじゃだめなの。。。？

深ぼってみた。

まず、各ルーティングを再度↓に

```ruby
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部分をぬいてくると

```ruby
                  POST   /posts/:post_id/comments(.:format)          comments#create
                  POST   /posts(.:format)                            posts#create
```

別になにも感じない。それぞれのアクションも

```ruby
#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じゃだめなの。。。？を検証してます

しょうがないので、もう一度ルーティングをみてみると、

```ruby
     post_comment GET    /posts/:post_id/comments/:id(.:format)      comments#show
             post GET    /posts/:id(.:format)                        posts#show             
```

showを比較してみるとURI Patternが、:post_idと:idでことなる。
ここがポイントそう。

```ruby
#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
```

```ruby
  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ページを修正

```ruby
<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 %>
```

気になるのは

```ruby
<%=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を渡してあげればよいので、とのこと。

なるほど。

これで削除機能も実装完了。

以上になります。

ドットインストール入門のほう、チュートリアルで実装した開発環境で問題なくできました。

プロゲートからチュートリアルいくより、ドットインストールはさむほうが断然おススメ。