[Ruby on rails]新規投稿機能の実装手順と仕組みについて


# 自己紹介
初めまして。現在24歳で未経験からエンジニアになるためにプログラミングを勉強しています。2021年5月からはDMMWEBCAMPを受講します。よろしくお願いします。  

#1. はじめに
自分が学習していく中で、すごくつまずいたRailsの「投稿の仕組み」について調べてみました。
この記事では、実際に投稿機能の実装をするためのコードの書き方、また、Railsのコントローラーのアクションが行われた時の、ターミナルのログの意味について説明していきます。


### 目次
1. テーブルの作成
　・dbに保存するためのモデルを作成
　・テーブルを作成するためのマイグレーションファイルを作成

2. データベースに反映させる

3. controller内にnewアクションを定義

4. viewファイルに入力フォームを作成

5. 投稿ボタンが押されたら反応するcreateアクションを実装

6. '/posts'にPOSTリクエストを送信

7. createアクションを実行し、投稿フォーム内のデータをデータベースに保存

8. 新規投稿がindex.html.erbで表示され、ユーザーの画面に表示される

#1. テーブルの作成
では、データベースに投稿したいデータを保存するためのモデルを作成します。
その後、テーブルを作成するためのマイグレーションファイルを作成します。
最後に、マイグレーションを実行してデータベースにテーブルを作成します。
まずは、データの格納先である、データベースに値を保存するために必要なモデルを作ります。
モデルは「データベース」そのものではなく、データベースに対応した箱を作り、その取扱い方法を記述するというものです。

**記述の仕方**

Model名は`大文字から始まる単数形`にします。
今回は投稿のタイトルをtitileカラム、本文のbodyカラムの2種類を作成します。
※1つ1つのデータの格納場所を`カラム`と言います。
データ型も指定する必要があり、
`titleは255文字以内としてString型`、`bodyはそれ以上入力する可能性があるのでtext型`にします。

```:console
$ rails g model Post title:string body:text
```

上記のコマンドを実行すると、モデル、マイグレーション、テストファイルが作成されます。
マイグレーションファイルはデータベースを変更するための指示書のようなものです。
上記のコマンドでは下記の内容のファイルになります。

```Ruby:db/_create_posts.rb
class CreatePosts < ActiveRecord::Migration[6.1]
def change
    create_table :posts do |t|
      t.string :title
      t.text :body
      t.timestamps
    end
  end
end
```
#2. データベースに反映させる
1のままではデータベースに反映されていません。
記入したマイグレーションファイルを反映させるには、下記のコードをターミナルで実行します。
すると、データベースにテーブルが作成され、カラムが追加されます。

**確認方法**

db/schema.rbというファイルをみると、ここに追加されていることでデータベースに反映されたことが確認できます。

```:console
 $ rails db:migrate
```



```ruby:db/migrate/20210419121504_create_posts

ActiveRecord::Schema.define(version: 2021_04_19_121504) do
  create_table "posts", force: :cascade do |t|
    t.string "title" #titleがマイグレーションされていることがわかります。
    t.text "body"    #bodyがマイグレーションされていることがわかります。
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end
end
```




#3. controller内にnewアクションを定義
```Ruby:app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def new
    @post = Post.new #空のインスタンスを生成
  end
end
```

では、app/controllers/posts_controller.rbのnewアクションで、新規投稿画面の表示に必要な準備をします。
「Post.new」と記述するとインスタンスが生成され、インスタンス変数@postに代入されてviewで利用できるようになります。
次で記述するapp/views/posts/new.html.erbのform_withに空のモデルが代入されたインスタンス変数を渡すと、フォームとPostモデルが関連づけられ投稿できるようになります。

**※インスタンス変数とは**

クラスのオブジェクト（インスタンス）がもつ変数の1つです。
コントローラのインスタンス変数をViewと共有することで、コントローラからViewへのデータの受け渡しを行うことができます。
インスタンス変数に代入した値は、そのオブジェクトがある限り、オブジェクトとともに存在し続けます。
また、インスタンス変数の名前には、先頭に必ず@（アットマーク）を付けます。    

#4. viewファイルに入力フォームを作成  
では、form_withを利用して、投稿を表示させるフォームを作成していきます。
編集するのは、views/posts/new.html.erbのファイルです。
まず、投稿を新規作成するには、form_withの引数に渡す必要があるため、入力フォームを作成していきます。
入力フォームはform_withを使用することにより簡単にデータベースに保存ができます。



```Ruby:/views/posts/new.html.erb
<%= form_with model: @post, local: true do |form| %>

<p><%= form.label :title %><br>
<%= form.text_field :title %> #titleの入力フォーム生成
</p>

<p><%= form.label :body %><br>
<%= form.text_area :body %> #bodyの入力フォーム生成
</p>

<p><%= form.submit %> #送信ボタンを生成
</p>

<% end %>
```


#5. 投稿ボタンが押されたら反応するcreateアクションを実装
では、app/controllers/posts_controller.rbにフォームから送られてきたデータを保存する処理createアクション、送られてきたデータを受け取るためのpost_paramsを定義していきます。

```Ruby:app/controllers/posts_controller.rb
def create
  @post = Post.new(post_params) #データを新規登録するためのインスタンス生成
  @post.save #データをデータベースに保存するためのsaveメソッド実行
  redirect_to action: 'index' #トップ画面へリダイレクト
end

private
  def post_params #ストロングパラメータ
    params.require(:post).permit(:title, :body) #パラメーターのキー
  end
 end
````


## 6.URL '/posts'にPOSTリクエストがサーバーに送信  
では、実際にcreateアクションが押され、裏では何が起きているかを説明していきます。
titleフォーム、bodyフォームに内容を記述し投稿ボタンを押すと、`まずPOSTメソッドの'/posts'といわれるところに送信`されます。 

**HTTPメソッドの役割について**    

| GET              |ページの取得         | 
|:-----------------|:------------------|
| POST             |データの作成・登録    |
| PUT(PATCH)       |データの更新         |
| DELETE           |データの削除         |  

**投稿ボタンが押された時のターミナルの状況**

```Ruby:console
1.Started POST "/posts" for ::1 at 2021-04-19 12:05:00 +0900
2.Parameters: {"authenticity_token"=>"[FILTERED]", "post"=>{"title"=>"タイトル", "body"=>"ボディー"}, "commit"=>"Create Post"
3.TRANSACTION (0.1ms)  begin transaction
   ↳ app/controllers/posts_controller.rb:13:in `create'
```
**1行目について**

`POSTと言うリクエストが'/posts'`と言うURLに対して行われたと言う意味です。
投稿すると'/posts'と言うURLに対して指令が出されます。ここのルーティングは`routes.rb`で設定されています。
ターミナルで**rails routes**と入力、実行してもらうと`posts_path`には`GET`と`POST`の2種類の役割があることがわかります。
`posts_path`に対してgetをすると`posts#index`アクションが呼び出され、
また、`posts_path`に対してpostをすると`posts#create`アクションが呼び出されます。
そして、今回はform_withで自動的にHTMLのフォームタグが生成され、methodの部分がpostになっているのでPOST通信が行われています。 

**2行目について**

次に、createアクションが反応すると、先ほどposts_controllerのnewアクションで定義した、
@postと言う変数にPost.newが渡されます。さらに@postは（post_params）と言う引数を持っています。
post_paramsと言うのはRailsで送られてきた値を受け取るためのメソッドで、privateで定義されているリクエストとともに送られてくる情報のことをいいます。

**3行目について**

データベースに変更をくわえる手続きを「TRANSACTION」、'create'はposts_controllerでのcreateアクションで行われているという意味で、正常に投稿が行われたと言うことを表しています。  

**※privateについて**

このprivateでは何が定義されているかというと上記の(:post)と言うパラメーターのキーの中にさらに（:titleと:body）のキーを許可するということが書いてあります。

**ストロングパラメータについて**

`requireメソッド`を使用する事で、params内の特定のキーに紐付く値だけを抽出する事ができます。今回で言う(:post)
そのため、引数には取り出したい値のキーを指定する必要があります。`permitメソッド`を使用する事で、許可された値のみを取得することができます。(:title, :body）
#7. createアクションを実行投稿フォーム内のデータをデータベースに保存
そして、createアクションで上から順にユーザーからのデータを@postに代入し、**@post.save**でデータベースへ保存をします。**redirect_to action: 'index'** でトップ画面へリダイレクトをします。  
## 8.新規投稿がindex.html.erbで表示され、ユーザーの画面に表示される  
new.html.erbで投稿した内容をindex.html.erbで確認し、終了となります。

## 9.感想
この記事は、新規投稿の実装に困っている方の役に立ちたいという気持ちで書いてみました。
また、人にわかるように意識して書くことで、曖昧だった部分が整理できました。新規投稿についてもわかっているつもりでしたが、いざ、説明してみるとわかっていない部分、曖昧な部分が多くあることに気づき、正しい知識を付ける良いきっかけになりました。記事を書くことで人に説明するという意味も、理解することができました。