Railsを使ったToDoリストの作成(5.CRUDのCreate機能)

#概要
本記事は、初学者がRailsを使ってToDoリストを作成する過程を記したものです。
私と同じく初学者の方で、Railsのアウトプット段階でつまづいている方に向けて基礎の基礎を押さえた解説をしております。
抜け漏れや説明不足など多々あるとは思いますが、読んでくださった方にとって少しでも役に立つ記事であれば幸いです。

#環境
* `Homebrew: 2.5.10` -> MacOSのパッケージ管理ツール
* `ruby: 2.6.5p114` -> Ruby
* `Rails: 6.0.3.4` -> Rails
* `node: 14.3.0` -> Node.js
* `yarn: 1.22.10` -> JSのパッケージ管理ツール
* `Bundler: 2.1.4` -> gemのバージョン管理ツール

```:iTerm
$ brew -v => Homebrew 2.5.10
$ ruby -v => ruby 2.6.5p114
$ rails -v => Rails 6.0.3.4
$ npm version => node: '14.3.0'
$ yarn -v => 1.22.10
$ Bundler -v => Bundler version 2.1.4
```

#第５章　CRUDのCreate（new／create）
第５章では、以下の機能を実装していきます。

* 新規登録画面（newアクション）
* 新規登録画面で入力された情報をデータベースに保存する機能（createアクション）

では、詳しく見ていきましょう。

##1 newアクション
まずは、newアクションを使って新規登録画面を実装していきます。

###1 コントローラ

まず、コントローラで新規登録された情報を保存するための空のインスタンスを作ります。ActiveRecordのnewメソッドを使っていきます。

```app/controllers/boards_controller.rb
def new
    @board = Board.new
end
```

`(name: '  ', description: '  ')`のような空の箱がインスタンス変数に代入されているイメージです。

###2 ビュー

次に、投稿フォームを作成していきます。

```new.html.haml
.container
  %h2.form_title New Board
  = form_with(model: @board, local: 'true') do |f|
    %div
      = f.label :title, 'Name'
    %div
      = f.text_field :name, class: 'text'
    %div
      = f.label :description, 'Description'
    %div
      = f.text_area :description, class: 'text'
    %div
      = f.submit :Submit, class: 'btn-primary'
```
フォーム作成のポイントは２つあります。

* form_withメソッドを使ってフォームを作成しています。form_withメソッドの引数は`model: [モデルクラスのインスタンス], local: 'true'`を指定します。`local: 'true'`を指定しない場合はAjaxによる送信になります。
* フォームヘルパーを使って、ラベル・１行入力欄、複数行入力欄を作成しています。フォームヘルパーは以下のように指定します。

```ruby
=f.[メソッド名] :[column名], class: 'class名'
```
フォームヘルパーの種類について知りたい場合は以下の記事が参考になります。
* [フォームヘルパーの種類と例](https://qiita.com/Hal_mai/items/1e5afd0c99dd9059839f)
* [Action View フォームヘルパー](https://railsguides.jp/form_helpers.html)

###3 一覧表示画面にリンクを貼る

最後に、一覧表示ページから新規登録ページに遷移できるようにリンクを貼っていきましょう。pathは[rails info](http://localhost:3000/rails/info/routes)の'boards#new'を参照してください。

```app/views/boards/index.html.haml
= link_to new_board_path do
  .add_card
    %p + Add new board
```

以上で、新規登録画面の実装は完了です。

##2 createアクション
では、次にcreateアクションを使って新規登録画面で入力された情報をデータベースに保存し、保存が成功したら詳細表示ページに遷移する機能を実装していきます。

###1 コントローラ

コントローラで以下のように記述します。詳しい説明は後述します。

```app/controllers/boards_controller.rb
def create
    @board = Board.new(board_params)
    if @board.save
        redirect_to board_path(@board), notice: 'Save successful'
    else
        flash.now[:error] = 'Could not save'
        render :new
    end
end

private
def board_params
    params.require(:board).permit(:name, :description)
end
```

Board.newでインスタンスを作成し、インスタンス変数に代入します。引数にはStrong Parameter(後述)を渡します。
ActiveRecordのsaveメソッドを実行し、作成したインスタンスをデータベースに保存します。

Q. newアクションで新しいインスタンスを作成していたのに、なぜcretaeアクションで再度作成しなければいけないのか？

A. Webアプリケーションでは、ブラウザからのリクエストのたびに新しくプロジェクトが実行されるため、newアクション(GETリクエスト)で作成した変数は利用することができません。そのため、今回はパラメータを渡して再度インスタンスを作成することが必要となってきます。

####'render'と'redirect to'

`render`はアクションに続けてビューを表示させる場合に使用します。
`redirect_to`はアクションを実行した直後にビューを表示せず、別のURLに遷移したい場合に使用します。
今回のケースではif文を使用して、インスタンスが保存された場合は詳細表示ページ(boards#show)に遷移するようにして、保存が失敗した場合は、現在の画面のビュー(boards#new)を表示するようにしています。
`redirect_to`の引数に@boardを指定しているのは、idを渡すためです。`id: @board.id`だと思ってください。

####Strong Parameter

Strong ParameterはRailsのセキュリティに関する概念の一つです。board_paramsメソッドに記載している内容が該当します。
`[更新する対象のモデル名_params]`と命名する必要があります。
また、Strong ParameterはBoardsControllerクラスの外で使用することはないのでprivateメソッドを使用します。

| 表記 | 意味|
|:--|:--|
| params  |  リクエストパラメータとして送られてきた情報が格納されているオブジェクト |
|  .require(:board) |  オブジェクトの中にboardというキーが入っている必要があります |
|  .permit(:name, :description) |  boardの中で、nameとdescriptionを保存する対象として許可します |

ちなみに今回のparamsの状況は以下のようになっています。
![スクリーンショット 2020-11-20 16.30.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864210/94785ecf-f067-3339-88bf-d693ff94a563.png)
これを見ると、paramsはハッシュのようになっていてboardというキーとnameやdescriptionなどのデータが入ったバリューを持っていることがわかります。

####Flashメッセージ
Flashは、リダイレクトやレンダーする際に、次のリクエストに対してちょっとしたデータを伝えるために用意された仕組みです。
Flashにはハッシュでデータを格納することができます。

`redirect_to`の場合は、第２引数に`notice: '[表示したいメッセージ]'`を指定し、renderの場合は`flash.now[:error] = '[表示したいメッセージ]'`を指定することで設定することができます。
今回の場合は以下のようなハッシュが格納されています。

```ruby
flash = { notice: 'Save successful', error: 'Could not save' }
```

ビューで以下のように設定することで画面に表示することができます。

```ruby
- if flash.present?
  .flash
    - flash.each do |key, value|
      %div.key= value
```
これは何をやっているかというと、まず、present?メソッドを使ってflashにデータが入っているかどうかを確認します。if文を使ってtrueの場合は３行目と４行目を実行します。

###2 モデル

モデルではvalidationの設定を行います。
validationとは、データの内容が正しいかどうかをチェックする仕組みのことを指します。

モデルのvalidationは、モデルに対応するオブジェクトをデータベースに登録・更新する前に検証を行い、エラーがあれば登録・更新処理をせずに差し戻しを行うという仕組みになっています。
今回のケースではsaveメソッドを実行しデータベースの登録を行う前に自動的に検証を行い、エラーがあればfalseが返ってきます。検証エラーの詳細はerrorsメソッドを使うことで確認することができます。

```app/models/board.rb
class Board < ApplicationRecord
    validates :name, presence: true
    validates :description, presence: true
end
```

今回は、Railsがデフォルトで用意してくれている検証用のヘルパーを利用し必須のデータが入っていなければエラーが出るようにしています。
記述方法は以下の通りです。

```ruby
validates :[column名], presence: true
```

###3 ビュー

ビューではvalidationの検証エラーのエラー文を取得して表示できるように設定していきたいと思います。
具体的には、まずerrorsメソッドを使って検証エラーの詳細が入ったオブジェクトを取得します。このオブジェクトにfull_messagesメソッドを使ってエラー文の全てを配列で取得します。さらにこれをeach文で表現することにより、各エラーがリストで表示されるようにします。

```haml:app/views/new.html/haml
%ul
  - @board.errors.full_messages.each do |message|
    %li= message
```

コンソールで確認すると以下のようになっています。
![スクリーンショット 2020-11-20 19.58.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864210/d2e05ad3-6cab-e2c6-9c79-12fffa2f2246.png)
errorsメソッドで検証エラーの詳細がわかります。
また、full_messagesメソッドでエラー文の全てが配列で取得できていることがわかります。
これをeach文を使ってリスト化しているため、検証に引っかかった文章を表示することができます。

以上で、新規登録機能の実装は完了です。