Railsのform_withについて掘り下げる


## 軽く自己紹介
はじめましての方は初めまして。伊東と申します。
大卒後未経験でプログラマーになりまして、気づけば7,8年くらい働いています。
2020年3月からDMM WebCampのメンターをしています。

## 対象読者
- Ruby on Railsで初めてMVCフレームワークを触った
- 検証ツールでHTML要素をあまり見ない
- 初学者の方

## 前提
```sh
$ rails -v
Rails 5.2.4.3
$ ruby -v
ruby 2.6.2p47 (2019-03-13 revision 67232) [x86_64-linux]
$
```

Rubyのバージョン、Rails5系だよってだけです。バージョン互換などは考慮しておりませんが、~~書く内容はそんなに影響するような話じゃないのでは？？って思っています。~~ authenticity_tokenの箇所でデフォルトの挙動が違うぽいです...本題というわけではないので各バージョンで調べてくださいmm


## 伝えたいこと
**検証ツール見ていきましょう**って話です。
普段からinput nameとかの属性をちゃんと見てる人は読む必要がない記事になってます。
本当に基本的なことしか書かないので悪しからず

## そもそもActionView::Helpersってなに？？
よく使うのはform系ではないかな？と思います。
結局htmlで出来ることをRailsがメソッドを用意してくれていて変数を使用しやすくしてくれています。

- form_with
    - `<form></form>`
- text_field_tag
    - `<input type="text" ...>`
- text_area_tag
    - `<textarea ...></textarea>`
- submit_tagなど
    - `<input type="submit" ...>`

詳しくは以下リンクから見てみてください。
https://api.rubyonrails.org/classes/ActionView/Helpers.html

## 事前準備
細かい説明は割愛して作っていきます。

## プロジェクト作成

```
$: 基本的に使用するログインユーザー
#: rootユーザー(OSの管理者権限で実行している)
```
↑仕事し始めた頃は `$` やら `#` やら区別ついていなくって闇雲にsudoつけてた時代もありました...本当に止めたほうが良いです :cry: 


```shell:
$ rails new without_action-view-helpers # プロジェクト作成
# 省略
$ cd without_action-view-helpers/
$ rails g controller books index new # コントローラー(アクション含)作成
# 省略
$ rails g model Book title:string price:integer description:text # モデル作成
# 省略
$ rails db:migrate # DB反映
# 省略
$
```

## ルーティングの設定

```rb:config/routes.rb
Rails.application.routes.draw do
  resources :books, only: [:index, :new, :create, :show, :edit, :update]
end
```

```sh:ルーティングの確認
$ rails routes | grep book # grepは文字列検索しています
                    books GET   /books(.:format)                                                                         books#index
                          POST  /books(.:format)                                                                         books#create
                 new_book GET   /books/new(.:format)                                                                     books#new
                edit_book GET   /books/:id/edit(.:format)                                                                books#edit
                     book GET   /books/:id(.:format)                                                                     books#show
                          PATCH /books/:id(.:format)                                                                     books#update
                          PUT   /books/:id(.:format)                                                                     books#update
$
```

### controller
めーっちゃシンプルな形。

```rb:app/controllers/books_controller.rb
class BooksController < ApplicationController
  def index
    @books = Book.all
  end

  def new
    @book = Book.new
  end

  def create
    book = Book.new(book_params)
    book.save
    redirect_to books_path
  end

  def show
    @book = Book.find(params[:id])
  end

  def edit
    @book = Book.find(params[:id])
  end

  def update
    book = Book.find(params[:id])
    book.update(book_params)
    redirect_to books_path
  end

  private
  def book_params
    params.require(:book).permit(:title, :price, :description)
  end
end
```

### view

```erb:app/views/books/new.html.erb
<h1>Books#new</h1>

<div>
  <%= render 'books/form', book: @book %>
</div>
```

部分テンプレート化しています。

```erb:app/views/books/_form.html.erb
<%= form_with model: book, local: true do |f| %>

  <div>
    <%= f.label :title %>
    <%= f.text_field :title %>
  </div>

  <div>
    <%= f.label :price %>
    <%= f.number_field :price %>
  </div>

  <div>
    <%= f.label :description %>
    <%= f.text_area :description %>
  </div>

  <div>
    <%= f.submit :submit %>
  </div>

<% end %>
```

## ブラウザの検証ツールを使用して掘り下げて見ていきます
### new
スクショだとこんな感じ
![スクリーンショット 2020-12-10 20.26.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91432/27264ebc-170e-20b4-891b-eb1e966d92fc.png)


```html:まずformタグやinput>hiddenの部分から見ていきます
<form action="/books" accept-charset="UTF-8" method="post">
  <input name="utf8" type="hidden" value="✓">
  <input type="hidden" name="authenticity_token" value="cgX+LsxfzE7A7F5Tm8GAlF2KdaifSdqdrWd6cOgazlcbZx8OQ4c6LazJtdRYIdPjzmfmPUGHkuBoxSQhdSqiIA==">
</form>
```

action？accept-charset？method？hidden？指定していない項目がいくつもでてきました。

### method
methodはsubmitされた時のhttpメソッドです。
よく使われるのだと、get/post/put/patch/delete/options 辺りですね。細かい説明は割愛します。

### action
actionはformタグ内のsubmitボタンがポチッとなされた時に実行されるURI(パス)を指定します。
`/books` で methodが`post`になっているので、books_controllerのcreateアクションに飛ぶという仕組みです。

#### form_withでaction指定してなくない？
form_withを使用するときにurlの指定が無い場合はmodelからactionのpathを勝手に作ってくれています。モデル名とコントローラー名が一致している場合は 省略可能です。 `model: book` と書くだけでいいので簡単ですね。

ちなみに `admin` というnamespaceを使用している場合は `model: [:admin, book]` としてあげれば、`/admin/books` というURIを作ってくれます。便利便利

#### hidden
name="utf8" については調べてないです！割愛します
name="authenticity_token" についてはCSRFの対策として自動発行されているものです。他のサイトから不正なリクエストを受けないように発行してるものです。
今回は5.2系で試しているのでデフォルトで有効になっているようですがそれ以外のバージョンであれば必要に応じて設定すれば有効になります。

### inputタグ
```html:入力項目はどのようになっているか
<div>
  <label for="book_title">Title</label>
  <input type="text" name="book[title]" id="book_title">
</div>
```

![スクリーンショット 2020-12-13 16.21.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91432/2621a151-8298-39a5-675d-60f78f40eba6.png)

inputタグを見てみると
`<input type="text" name="book[title]" id="book_title">` となっています。
`type="text"`は単純にテキストを入力出来るという意味です。text以外にもあるのでこれ見ればわかると思います。 (詳しくはこれ: http://www.htmq.com/html5/input.shtml )
`name="book[title]"` これが多分一番大事になってくると思います。Rails的にはコントローラー側で `params[:book][:title]` で入力された値が取得出来るという意味になります。これを理解していれば、入力された値がDBに保存できない？みたいなことは少なくなると思います。

ちなむと、form_withではmodelが必須ではありません。なので `form_with model: nil` という記載も出来てしまいます。
その場合input>nameがどうなるかというと `name="title"` のようにbookが消えてしまいます。そうするとストロングパラメータと辻褄が合わなくなりバリデーションエラーなどに引っかかりrollbackというログが出るでしょう。。。

### edit

![スクリーンショット 2020-12-13 17.08.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91432/a1a065dc-d50c-0fbb-6c9f-6c4cf9646df1.png)


```html:適宜省略してます
<form action="/books/1" accept-charset="UTF-8" method="post">
  <input type="hidden" name="_method" value="patch">
  <div>
    <label for="book_title">Title</label>
    <input type="text" value="たーいとる" name="book[title]" id="book_title">
  </div>

  <div>
    <input type="submit" name="commit" value="submit" data-disable-with="submit">
  </div>

</form>
```

### formタグ, hiddenタグ
action部分は `action="/books/1"` になっています。`model: book`で指定している変数bookが保存済みの場合URIを自動的にbook.idを設定してくれています。

hiddenタグで新規の時には無かったものが存在しています。 `<input type="hidden" name="_method" value="patch">` これでhttpメソッドをpatchにしています。
なんでformタグで指定しないの？？ってなると思うんですけど、多分ですがform>methodではpost/getしか指定できないからだと思います(間違ってたら指摘してもらえると :bow: )。 http://www.htmq.com/html5/form.shtml


### inputタグ
`<input type="text" value="たーいとる" name="book[title]" id="book_title">`

value="たーいとる" という項目が増えています。これで入力部分の値を指定しています。それ以外は同じなので割愛します。
検証ツールを使うとhtmlの中身書き換えられるんですけど、valueの中身を書き換えたり遊ぶと知識の幅が増えるので常に遊び心は大事だと思います。（攻撃にならない程度に）


## まとめ
HTMLも単純に画面に表示されたものを見るのではなく、どのようにHTMLが生成されているかを確認したり意識することで、HTMLであったりRailsの仕組みを少しずつ理解出来るようになっていくと思います。
閉じタグがない等でもシンタックスエラーが発生しないのがなんとも初見殺しな気もしますが、エディタにリンター入れて対策すれば問題無いですね

あまり長い文章書くの得意じゃないんですが、役に立てると嬉しく思います。。。
またお声掛け頂いたメンターの方ありがとうございます。