【Ruby on Rails】form_withメソッドとDBの使い方

#【Ruby on Rails】form_withメソッドとDBの使い方

form_withヘルパーメソッドを使うことで、動的テンプレートの中でform関連タグを簡単に作成することができる。DBとの連携も可能。

form_withはv5.1以降から使用可能。それ以前は、form_forとform_tagを状況に応じて使い分ける仕様。

以下は[Railsの公式ガイド](https://railsguides.jp/getting_started.html)に沿って作成。


##目次

1. [ヘルパーメソッドとは？](#ヘルパーメソッドとは)
2. [form_with](#form_with)
3. [form.label](#formlabel)
4. [form.text_field](#formtext_field)
5. [form.text_area](#formtext_area)
6. [一般的なsubmit後の処理](#一般的なsubmit後の処理)
7. [form.submit](#formsubmit)
8. [paramsの中身](#paramsの中身)
9. [GETデータのparamsへの格納](#getデータのparamsへの格納)
10. [DBとの連携](#DBとの連携)
    1. [modelを作成](#1-modelの生成)
    2. [migrateでmodelとDBを紐付け](#2-migrateでmodelとdbを紐付け)
    3. [createアクションにDB操作を追加](#3-createアクションにdb操作を追加)
    4. [@変数の名称と役割](#変数の名称と役割)
5. [DBに格納したデータの呼び出し](#dbに格納したデータの呼び出し)
    6. [showアクションの作成](#showアクションの作成)
    7. [viewファイルの作成](#viewファイルの作成)
    8. [DBに格納された全てのデータの取得](#dbに格納された全てのデータの取得)
9. [validationの設定](#validationの設定)
    10. [validationメソッドとは？](#validationメソッドとは)
    11. [validatationメソッドの使い方](#validatationメソッドの使い方)
    12. [validation結果による条件分岐](#validation結果による条件分岐)
    13. [エラーメッセージの表示](#エラーメッセージの表示)
14. [DBデータの更新](#dbデータの更新)
    1. [更新したいデータを呼び出す（editアクション）](#1-更新したいデータを呼び出すeditアクション)
    2. [更新用viewファイルの作成（edit.html.erb）](#2-更新用viewファイルの作成edithtmlerb)
    3. [updateアクションの定義](#3-updateアクションの定義)
    4. [一覧ページの各要素に編集リンクを設置](#4-一覧ページの各要素に編集リンクを設置)
    5. [詳細ページに編集リンクを設置](#5-詳細ページに編集リンクを設置)
6. [パーシャルファイルの作成](#パーシャルファイルの作成)
    7. [パーシャルファイルの命名ルール](#パーシャルファイルの命名ルール)
    8. [パーシャルファイルの呼び出し](#パーシャルファイルの呼び出し)
    9. [パーシャル化実例](#パーシャル化実例)
10. [削除機能の追加](#削除機能の追加)
    1. [destroyアクションの作成](#1-destroyアクションの作成)
    2. [viewファイルに削除ボタン設置](#2-viewファイルに削除ボタン設置)





<br>
##ヘルパーメソッドとは？
動的テンプレートの中でHTMLタグを簡易的に作成するためのメソッド。


##form_with

```erb:
<%= form_with scope: :article, url: articles_path, local: true do |form| %>
　formの内容
<% end %>
```

・`scope: :コントロール名`
何のformかを判断するためのもの。

・`url: パス`
action属性の値。設定しないと自ページになる。


・`local: true`
htmlとしてフォームを送信する場合に記述。
ない場合(デフォルト)はajax通信となる。


・|変数|
FormBuilderオブジェクト。変数名は任意。
formやfが使われることが多い。

・`<% end %>`
必ずendで閉じる。




```html: 
<form action="/articles" accept-charset="UTF-8" method="post">
</form>
```




<br>
##form.label
`<%= form.label :title %>`

labelタグを生成する。「:テキスト」で画面上に内容が表示される。

```html:
<label for="formの名前_title">Title</label>
```
formの名前には、scope:で設定したテキストが入る。


[＞labelタグの詳細](https://railsdoc.com/page/label)


<br>
##form.text_field
`<%= form.text_field :title %>`

inputタグのtextボックスを作成する。


```html:
<input type="text" name="formの名前[title]" id="formの名前_title">
```

nameとid属性には、scope:で設定したテキストと、設定したテキストが入る。


**▼画面表示例**

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/6cf30e74-4a89-9e78-ff4c-47744fd875c1.png)|
|:--|




<br>
##form.text_area

`<%= form.text_area :text %>`

textareaタグを生成する。


```html:
<textarea name="article[text]" id="article_text"></textarea>
```

nameとid属性には、scope:で設定したテキストと、設定したテキストが入る。


<br>
**▼form.labelヘルパーとセットで使用する**

```erb
  <p>
    <%= form.label :text %><br>
    <%= form.text_area :text %>
  </p>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/f29c98a4-372d-13c7-f408-6169dfc600f3.png)




<br>
##form.submit

`<%= form.submit '送信する' %>`

送信ボタンを作成する。

```html
<input type="submit" name="commit" value="送信する" data-disable-with="送信する">
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/6e44982a-acdd-b037-ba39-f9058553fbf8.png)




※ヘルパーの中でテキストの指定がない場合は、「Save forｍの名前」となる。

`<%= form.submit %>`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/e331db97-546d-a97f-cda4-5a3bea7bcb42.png)




##一般的なsubmit後の処理

1. resourcesメソッドでルーティングを自動設定
2. パスをコントロール名としPOSTで渡したデータでcreateアクションを実行
3. オブジェクト.saveでDBへの登録を行う（後述のmodelの作成とDB連携を参照）


###1. resourcesメソッドでルーティングを自動設定
RailsでCRAD操作を行う場合、ルーティングでresourcesメソッドを使うのが一般的。

routes.rbファイルに`resources :コントローラー名`を記述すると、以下ルーティングが自動生成される。


```rb:routes.rb
resources :articles
```

```:terminal
$ rails routes
      Prefix Verb   URI Pattern                  Controller#Action
welcome_index GET    /welcome/index(.:format)     welcome#index
     articles GET    /articles(.:format)          articles#index
              POST   /articles(.:format)          articles#create
  new_article GET    /articles/new(.:format)      articles#new
 edit_article GET    /articles/:id/edit(.:format) articles#edit
      article GET    /articles/:id(.:format)      articles#show
              PATCH  /articles/:id(.:format)      articles#update
              PUT    /articles/:id(.:format)      articles#update
              DELETE /articles/:id(.:format)      articles#destroy
         root GET    /                            welcome#index
```


###2. パスをコントロール名としPOSTで渡したデータでcreateアクションを実行
データをPOSTで送信する場合、pathは`articles`を指定する。

上記表ではprefixのみの表示（_path）が省略されているので、実際記述は`articles_path`となる。


送信ボタンがクリックされると、パラメータ(params)に格納されたデータが渡され、articlesコントローラーのcreateアクションが実行される。



##paramsの中身
createアクションでparamsの中身を表示するコードは下記になる。

```rb
  def create
    render plain: params[:article].inspect
  end
```

・`render` ページに表示するメソッド
・`plain:` renderのオプション。後ろに続く内容をそのまま出力する。
・`params[:フォームの名前]`
・`.inspect`　型を含め表示する。デバッグ用に使用されることが多い。

つまり、paramsに格納されたデータのうち、指定したformのデータを表示する処理。


<br>
**▼実例**
以下内容で送信する。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/42009e84-0216-abab-bac5-62830a5e791f.png)|
|:--|


```:ブラウザの表示
<ActionController::Parameters {"title"=>"test_title", "text"=>"createアクションの処理をテストしています。"} permitted: false>
```

プロパティtitleには「test_title」が格納されている。



<br>
##GETデータのparamsへの格納
仮に、POSTでなくGETでデータを渡したとすると、URLは以下のようになる。

```
https://ドメイン?title=test_title&text=createアクションの処理をテストしています。
```

?の後に「プロパティ=値」が＆で繋がっていく。



<br>
##DBとの連携
これまでの流れでは、controllerとviewを使って変数paramsにデータを格納したのみに留まっている。

格納したデータをDBと連携させるためには

1. modelを作成
2. migrateでmodelとDBを紐付け
3. createアクションにDB操作を追加

する必要がある。


<br>
###1 modelの生成

`rails g model モデル名 カラム名:型 カラム名:型...`

```
$ rails generate model Article title:string text:text
```

・ モデル名は単数形（冒頭大文字）
 　　　┗ 対応するDBは複数形で自動生成される
・　カラム名はプロパティ名と合わせる


>例
・model名：Article
・DB名：articles


実行が完了すると、app > modelsフォルダ配下にmodelファイルが生成される。（model名.rb）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/08f7f411-d382-7088-8a82-918e40d9d3ec.png)

 


###modelの処理内容
生成したmodelファイルの中身は以下のようになっている。

```rb:model
class Article < ApplicationRecord
end
```

- クラス(`Article`)が作成されている。
- このクラスは`ApplicationRecord`を継承している。
- このApplicationRecordクラスは、`ActiveRecord::Base`クラスを継承している。

これにより、CRUD操作やvalidationなど、モデルとDB操作が関連付けられる。




<br>
###2 migrateでmodelとDBを紐付け
マイグレーションとはrubyのクラスで、DB操作を簡単に行うための仕組み。

マイグレーションは db:migrate　で実行する。


```
$ rails db:migrate
```

マイグレーションを行うと、

- db > migrateの下にDBマイグレーションファイルが作成される。
- 実行するごとにファイルが増えていく
- changeメソッドが使われている
- 後から取り消すことも可能。





<br>
##3 createアクションにDB操作を追加
該当するコントローラーのcreateアクションに、DBへの保存操作を記述する。

セキュリティ上、コントローラーが受け取ったパラメータを直接モデルに渡せないため、必須かつ許可する宣言をする。

・`@article = モデル名.new(params.require(:モデル名).permit(:プロパティ名, :プロパティ名,,,))`
　┗　コントローラーが受け取ったデータをモデル（DB）と紐付け
　┗ 変数名はmodelのフィールド名が一般的
　┗ モデル名は大文字（※クラスのため）
　┗ Modelは初期化時にフィールド（属性）が与えらる
　┗ params[:フィールド名]でフィールドを指定
　


・`変数.save`
　┗ 対象をモデル（DB）に保存
　┗ 成功したかは論理値で返される


・`redirect_to @変数`
　┗ showアクションにリダイレクト


```rb
def create
  @article = Article.new(params.require(:article).permit(:title, :text))

  @article.save
  redirect_to @article
end
```



<br>
###メソッドの共通化
コントローラーで受け取ったパラメータとモデルの紐付けは繰り返し使うため、メソッドで切り出して共通化する。

**private**宣言の後に記述する。

```rb
def create
  @article = Article.new(article_params)

  @article.save
  redirect_to @article
end

private
  def article_params
    params.require(:article).permit(:title, :text)
  end
```



<br>
###セキュリティーエラーの出るコード（もとの状態）
セキュリティ強化のため、コントローラーは受け取ったパラメータがノーチェック（requireとpermitがない）の場合、エラーとなる。

▼パラメータをModelに直接渡す記述

・`@変数 = モデル名.new(params[:フィールド名(単数形)])`
　┗ 変数名はmodelのフィールド名が一般的
　┗ モデル名は大文字（※クラスのため）
　┗ Modelは初期化時にフィールド（属性）が与えらる
　┗ params[:フィールド名]でフィールドを指定


```rb:
def create
  #セキュリティのためエラーになる
  #@article = Article.new(params[:article])

  @article.save
  redirect_to @article
end
```


<br>
###@変数の名称と役割
アットマークのついた変数をインスタンス変数と呼ぶ。

コントローラーでインスタンス変数を作成すると、自動でビューにコピーする。

すなわち、viewのテンプレートで呼び出すことが可能になる。






<br>
##DBに格納したデータの呼び出し
###showアクションの作成
redirect_toでshowアクションを自動で実行するため、showアクションを記述する。

DBに格納したデータを呼び出す場合はfindメソッドを使う。

`@変数 = モデル名.find(params[:プロパティ])`

```rb
def show
    @article = Article.find(params[:id])
  end
```

params[:id]は:idパラメータの取り出し。
idは.saveした時点でデータに自動付与される。


<br>
###viewファイルの作成
show.html.erbファイルを作成する。

▼データの取得
`<%= @変数名.プロパティ名  %>`

```erb:
<p>
  <strong>Title:</strong>
  <%= @article.title %>
</p>

<p>
  <strong>Text:</strong>
  <%= @article.text %>
</p>

<%= link_to '[トップ画面に戻る]', articles_path  %>
```

コントローラーに記述したインスタンス変数はviewにも自動コピーされているため、プロパティ名を指定することで動的にデータを取得できる。



<br>
##DBに格納された全てのデータの取得
###allメソッド
あるモデル(DB)に格納されている全てのデータはallメソッドで取得できる。

`モデル名.all`
　┗ モデル名は頭文字大文字


controllerのindexアクションに、modelの全てのデータを格納したインスタンス変数を定義する。

```rb:
  def index
    @articles = Article.all
  end
```


###eachメソッド
eachメソッドを使って取得したデータを一つづつ表示する。

```erb:eachメソッド
<% オブジェクト.each do |変数| %>`
   繰り返し処理
<% end %> 必須
```



```erb:
<h1>Articles一覧</h1>

<table>
    <tr>
        <th>Title</th>
        <th>Text</th>
        <th>詳細ページ</th>
    </tr>
    
  <% @articles.each do |article| %>
    <tr>
        <td><%= article.title %></td>
        <td><%= article.text %></td>
        <td><%= link_to "[ id:#{article.id} ]", article_path(article) %> </td>
    </tr>
    
  <% end %>  
</table>
```



<br>
##validationの設定
###validationメソッドとは？
validationメソッドを使うことで、条件を満たした場合のみDBの保存を許可することができる。

条件を満たさない場合は**DBに登録しない**。



###validatationメソッドの使い方
validationの設定は**model**で行う。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/0039ce42-9a84-f3eb-5888-f38f77293914.png)

**▼記述方法**
`validates ①対象カラム, ②バリデーションの種類`


①カラム（属性）の指定: `:カラム名`
　┗ 複数のカラムを指定する場合は、カンマでつなげる

②バリデーションの種類： `バリデーションの種類:内容`
　┗ 値の有無　presence:true
　┗ ５文字以上 length: {minimum: 5}
　┗ 10文字以下 length: {maximum: 10}
　┗ 整数かどうか numericality: {only_integer:true}
　┗ 10以上 numericality: {greater_than:10}
　┗ 100以下 numericality: {less_than:100}
　┗ 重複禁止 uniqueness: true


<br>
例えば、カラムtitleとtextに対して、記載あり、文字数3文字以上100文字以下、重複禁止とする場合

```rb:
class Article < ApplicationRecord
    validates :title, :text, presence: true, 
                                length: {minimum: 3, maximum: 100}, 
                                uniqueness: true
end
```



<br>
###validation結果による条件分岐
validation実行結果が満たされなかった場合、その処理には**false**が格納される。

DBに保存する、`オブジェクト.save`の結果でvalidationが満たされなかった場合、**`オブジェクト.save`自体がfalse**となる。


if文を使って、.saveの結果により処理を分岐させる。

```rb:
  def create
    @article = Article.new(article_params)
 
    # validation結果に合わせた処理
    if @article.save
      redirect_to @article
    else
      render 'new'
    end
  end
```

**▼redirect_to**
`redirect_to @article`はパラメータを指定しているため、showメソッドが実行され、詳細ページに飛ぶ。


<br>
**▼render**
`render 'ファイル名'`で指定したファイルを開く。
　┗ ファイル名は**html.erb**を省略可

`render 'new'`は、現在のコントローラーのnew.html.erbを開く処理となる。


<br>
###エラーメッセージの表示
validationでエラーが発生した場合に、エラーの個数と内容を表示する。

・errorsメソッド
オブジェクトでエラーが発生している場合はエラー情報を取得する。

・any?メソッド
データの有無を調べる。データがある場合はtrueとなる。

・pluralizeメソッド
`pluralize(数値, "文字列")`
第一引数の数値が２位上の場合は、指定した文字列を複数形にする。


・full_messagesメソッド
errorsメソッドと組み合わせることで、発生しているエラーを配列の形で取り出す。


```erb:.erb
　<!--エラー時の表示-->
　<% if @article.errors.any? %>

　  <div id="error_exp">
　    <h2><%= pluralize(@article.errors.count, "error") %> 
　       下記エラーが発生しています:
　    </h2>

　    <ul>
　      <% @article.errors.full_messages.each do |msg| %>
　        <li><%= msg %></li>
　     <% end %>
　    </ul>
　  </div>

　 <% end %>
```




<br>
##DBデータの更新

データの更新手順

1. 更新したいデータを呼び出す（editアクション）
2. 更新用viewファイルの作成（edit.html.erb）
3. updateアクションの設定
4. 一覧ページの各要素に編集リンクを設置
5. 詳細ページに編集リンクを設置


<br>
###1. 更新したいデータを呼び出す（editアクション）
コントローラーにeditアクションを作成し、findメソッドで指定したid番号に該当するデータを呼び出す。

取得したデータは変数インスタンスに格納する。

`@変数名 = モデル名.find(params[:id])`

```rb:コントローラー
  def edit
    @article = Article.find(params[:id])
  end
```




<br>
###2. 更新用viewファイルの作成（edit.html.erb）
基本的には新規作成ページと同じ。
異なるのは、formにデータを渡すため、オプションで**「model: データ」**を指定する。

「model:オプション」を指定した場合、**submit後の処理はupdateアクション**となる。


`<%= form_with model: @article, local: true do |form| %>`

```erb:
<h1>編集ページ(edit）</h1>


<%= form_with model: @article, local: true do |form| %>

　
  <p>
    <%= form.label :title %><br>
    <%= form.text_field :title %>
  </p>

  <p>
    <%= form.label :text %><br>
    <%= form.text_area :text %>
  </p>

  <p>
    <%= form.submit %>
  </p>
<% end %>

　<!--エラー時の表示-->
　<% if @article.errors.any? %>
　  <div id="error_exp">
　    <h2><%= pluralize(@article.errors.count, "error") %> 
　       下記エラーが発生しています:
　    </h2>
　    <ul>
　      <% @article.errors.full_messages.each do |msg| %>
　        <li><%= msg %></li>
　     <% end %>
　    </ul>
　  </div>
　 <% end %>


<%= link_to '一覧ページ', controller: 'articles' %><br>
<%= link_to '編集ページ', controller: 'articles', action: 'new' %><br>
<%= link_to 'id：1の詳細ページ', controller: 'articles', action: 'show', id: 1 %><br>
```



<br>
###3. updateアクションの定義
コントローラーにupdateアクションを定義する。

updateを実行し、エラーが発生しなければ詳細ページ(show)に遷移。エラー発生時は新規登録ページ(new)を表示する。

```rb:
  def update
    @article = Article.find(params[:id])
    
    #updateのvalidation結果で条件分岐
    if @article.update(article_params)
      redirect_to @article
    else
      render 'new'
    end
  end



  private
    def article_params
      params.require(:article).permit(:title, :text)
    end
```

ここではif文のupdateメソッドの引数はtitleとtextを渡しているが、全てのカラムを渡す必要はない。




<br>
###4. 一覧ページの各要素に編集リンクを設置

`<%= link_to "アンカーテキスト", edit_article_path(インスタンス) %>`
　┗ インスタンスを指定すればOK。id番号である必要はない。

```erb
  <% @articles.each do |article| %>
    <tr>
        <td><%= article.title %></td>
        <td><%= article.text %></td>
        <td><%= link_to "[ id:#{article.id} ]", article_path(article) %> </td>

        <td><%= link_to "[edit]", edit_article_path(article) %></td>
    </tr>
    
  <% end %>  
```



<br>
###5. 詳細ページに編集リンクを設置

```erb:
<%= link_to '[編集]', edit_article_path(@article) %>
```

一覧ページと異なり、詳細ページのインスタンスは@articleに格納されている。




<br>
##パーシャルファイルの作成
railsの設計思想はdryに基づくため、ほぼ類似しているnewとshowの共通部分を共通化する。

共通化したファイルをパーシャルファイルと呼ぶ。


###パーシャルファイルの命名ルール
パーシャルファイルのファイル名は冒頭にアンダースコア「 _ 」を設置する。


`_ファイル名.html.erb`



###パーシャルファイルの呼び出し
パーシャルファイルはファイル名で呼び出す。
※アンスコ「 _ 」不要

`render 'ファイル名'`


###パーシャル化実例

パーシャルファイル`_form.html.erb`を作成する。

```erb:_form.html.erb
<%= form_with model: @article, local: true do |form| %>

　
  <p>
    <%= form.label :title %><br>
    <%= form.text_field :title %>
  </p>

  <p>
    <%= form.label :text %><br>
    <%= form.text_area :text %>
  </p>

  <p>
    <%= form.submit %>
  </p>
<% end %>

　<!--エラー時の表示-->
　<% if @article.errors.any? %>
　  <div id="error_exp">
　    <h2><%= pluralize(@article.errors.count, "error") %> 
　       下記エラーが発生しています:
　    </h2>
　    <ul>
　      <% @article.errors.full_messages.each do |msg| %>
　        <li><%= msg %></li>
　     <% end %>
　    </ul>
　  </div>
　 <% end %>


<br>
<%= link_to '一覧ページ', controller: 'articles' %><br>
<%= link_to '編集ページ', controller: 'articles', action: 'new' %><br>
<%= link_to 'id：1の詳細ページ', controller: 'articles', action: 'show', id: 1 %><br>
```



<br>
newとeditファイルで作成したパーシャルファイルを呼び出す。

```erb:new.html.erb
<h1>投稿ページ（new）</h1>

<%= render 'form' %>
```

```erb:edit.html.erb
<h1>編集ページ(edit）</h1>

<%= render 'form' %>
```



<br>
##削除機能の追加

1. destroyアクションの作成
2. viewファイルに削除ボタン設置


###1. destroyアクションの作成

コントローラーにdestroyアクションを作成し、destroyメソッドを設定する。

`変数インスタンス.destroy`

削除後は一覧ページを表示する。

```rb
 def destroy
    @article = Article.find(params[:id])
    @article.destroy
    
    redirect_to articles_path
  end
```


###2. viewファイルに削除ボタン設置
destroyアクションを実行するパスは「article_path」となる。

```:routes
Prefix Verb   URI Pattern                  Controller#Action
article DELETE /articles/:id(.:format)      articles#destroy
```

オプションで`method: :delete」`を指定。

また、削除ボタンをクリックすると、ダイアログを表示する仕様にするため、下記オプションを追加。

`data: { confirm: 'ダイアログ用のテキスト' } %`



```erb:
<%= link_to '[delete]', article_path(article),
 method: :delete, data: { confirm: '削除してもいいですか？' } %>
```


<br>
以上で、CRUD操作ができるアプリが完成。