Railsの基礎を使って、メッセージ投稿アプリを作ってみた　【第１章】（1.アプリ作成〜2.CRUD処理まで）

## はじめに
Railsの基礎を一通り学び終わったのですが、自分自身理解が追いついてないところもあると思うので、記事を作成する中でアウトプットし、理解を深めていくことが目的です。

## 概要
今回はタイトルにもあるとおり、簡単なメッセージ投稿アプリを解説も交えながら作成していきます。
アプリの作成からCRUD処理でメッセージ投稿機能の実装、それからリファクタリング、フラッシュやバリデーションの設定、エラーの処理、表示、最後にBootstrapでスタイルを付けるという感じで進めていきます。
なので、今回は4章に分けて記事を作成します。

## 開発手順
1. アプリの作成 <font color="DeepPink">(第１章)</font>
2. CRUD処理で簡単なメッセージ投稿アプリを実装 <font color="DeepPink">(第１章)</font>
3. リファクタリング （第２章）
4. フラッシュの実装 （第２章）
5. バリデーションの設定 （第２章）
6. エラーの処理 （第３章）
7. Bootstrapでスタイルをつける （第４章）


## 開発環境
- Ruby 2.7.3
- Rails 6.1.6.1
- Postgresql


## 1. アプリの作成
```ターミナル:ターミナル
rails new post_message_app -d postgresql -T
cd post_message_app
rails db:create
```
- データベースは`-d postgresql`で<font color="DeepPink">PostgreSQL</font>を指定
    - データーベースを指定した場合は <font color="DeepPink">rails db:create</font> が必要になります。
- <font color="DeepPink">-T</font> で MiniTest のディレクトリを作成しないように設定

### モデルとテーブルの作成
メッセージ投稿アプリなので、メッセージのタイトル（title）と内容（content）を投稿できるよう2つのカラムを用意し、モデルを作成します。
※ モデル名は必ず <font color="Red">単数形！</font>
```:ターミナル
rails g model Post title:string content:text
```
上記コマンドによって2つのファイルが作成されます。
- app/models/post.rb (モデル)
- db/migrate/作成日_create_posts.rb (マイグレーションファイル)

この時点で <font color="DeepPink">Post</font> モデルは作成されたが、テーブルは作成されていません。
ターミナルで`rails db:migrate`コマンドを実行することにより、先ほど作成されたマイグレーションファイルが実行され、データベースに posts テーブルが作成される。

マイグレーション実行後は、db/schema.rb で作成したテーブルの中身を確認できます。
```ruby:schema.rb
 create_table "posts", force: :cascade do |t|
    t.string "title"
    t.text "content"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end
```

### コントローラの作成
下記コマンドでコントローラとビューファイルまでまとめて作成。
※ コントローラ名は必ず<font color="Red">複数形！</font>
```:ターミナル
rails g controller posts new create index show edit update destroy
```
今回のアプリでは下記3つのファイルは使用しないので、この時点で削除しておきます。
- app/views/posts/create.html.erb
- app/views/posts/destroy.html.erb
- app/views/posts/update.html.erb

ここまでで、モデル、テーブル、コントローラ、ビューファイルなど初期設定が完了です。

## 2. CRUD処理
今回は簡単にRESTフルなアプリを作成するため、<font color="DeepPink">resources</font> を使用しルーティングを設定。

※ RESフルとは、（Representational State Transfer）「ネットワーク上にあるリソース（データ）に対して、作成、表示、更新、削除などの操作を行う」というアプリケーションを作成する上で必要な概念となります。

RESTの機能を担当するのがルーティングであり、resources を使用することで、簡単にRESTフルなアプリを作成することが可能となります。

```ruby:routes.rb
Rails.application.routes.draw do
  root "posts#index"
  resources :posts
end
```
`root "posts#index"`を追記することにより、トップページを投稿一覧ページに設定します。

### リンクの作成
全てのページで <font color="DeepPink">投稿一覧ページ、新規投稿ページ</font>のリンクが表示されるように <font color="DeepPink">application.html.erb</font> に追記。
```erb:app/views/layouts/application.html.erb
<!-- 略　-->
  <body>
    <%= link_to "投稿一覧", posts_path %> <%= link_to "新規投稿", new_post_path %>
    <hr>
    <%= yield %>
  </body>
</html>
```
※ <font color="DeepPink">Prefix</font> を使用したパス名は下記コマンドで確認可能です。
```:ターミナル
rails routes | grep posts
```

### 新規投稿機能（ new, create ）
ルーティングは <font color="DeepPink">resources</font> で設定しているので、コントローラから作成します。
```ruby:app/controllers/posts_controller.rb
class PostsController < ApplicationController

 def new
   @post = Post.new
 end

 def create
   post = Post.create!(post_params)
   redirect_to post_path(post)
 end 

# 略

 private

 def post_params
  params.require(:post).permit(:title, :content)
 end

end
```
createアクションでは変数に<font color="DeepPink"> @ </font>はつけず、また新規投稿後は、投稿詳細ページにリダイレクトするように実装します。

※ <font color="DeepPink">@ </font>を付けると付けないの違いについては、付けたらその変数はアクションと紐づくビューファイルで使用できるが、付けなかった場合は、ビューファイルで使用できません。

今回 create.html.erb 使用しないので、<font color="DeepPink">@ </font>は付けないようにしています。
<font color="DeepPink">private</font> には <font color="DeepPink">Strong Parameter</font> （post_params）を記述し、データベースに予期せぬ値が登録されないよう <font color="DeepPink">title</font> と <font color="DeepPink">content</font> のみに制限。

次に <font color="DeepPink">form_with</font> ヘルパーメソッドを使用しビューファイルで新規投稿ページを作成します。
```erb:app/views/posts/new.html.erb
<h1>新規投稿</h1>
<%= form_with model: @post, local: true do |form| %>
  <div>
    <%= form.label :title, "タイトル" %>
    <%= form.text_field :title %>
  </div>
  <div>
    <%= form.label :content, "内容" %>
    <%= form.text_field :content %>
  </div>
  <div>
    <%= form.submit "送信" %>
  </div>
<% end %>
```
<font color="DeepPink">form_with</font> でモデルオブジェクト（＠post）が posts テーブルに存在するかを判定し、存在しなければ createアクション、存在すれば updateアクションに処理を自動的に振り分ける役割を持ちます。

### 詳細（show）
まずはコントローラの処理から記述します。
```ruby:app/controllers/posts_controller.rb
class PostsController < ApplicationController
  # 略

  def show
    @post = Post.find(params[:id])
  end

  # 略

end
```
findメソッドで,<font color="DeepPink"> [:id] </font>に対応する投稿をデータベースから取り出し、@post に代入。
※ <font color="DeepPink">params[:id]</font> はshowアクションを実行した際の URL<font color="DeepPink"> posts/:id </font>と対応しています。

投稿詳細ページのビューファイルは下記の通り記述し、投稿したタイトルと内容を表示するようにします。
```erb:app/views/posts/show.html.erb
<h1>投稿詳細</h1>
<p>タイトル <%= @post.title %></p>
<p>内容　　<%= @post.content %></p>
```
これで新規投稿ページで投稿し、詳細ページにリダイレクトされるところまでの流れが出来上がりです。

この記事を見ながら同じように作成している方がいるのであれば、ここで一旦サーバーを起動し、新規投稿ページにアクセスし、投稿ができ、詳細ページにリダイレクトされるか確認することをお勧めします。
URL：http://localhost:3000/posts/new　

### 一覧表示（index）
投稿詳細ページでは、タイトルと内容が表示されるので、一覧ページではタイトルのみ表示する形とします。
まずはコントローラから
```ruby:app/controllers/posts_controller.rb
class PostsController < ApplicationController

# 略

 def index
   @posts = Post.order(:id :asc)
 end

# 略

end
```
<font color="DeepPink">Post.order </font>を使用した目的としては、<font color="DeepPink">Post.all </font>だと編集した場合、順番が変わってしまうので、<font color="DeepPink">Post.order </font>を使用し、id順で取得するようにします。

```erb:app/views/posts/index.html.erb
<h1>投稿一覧</h1>
<table>
  <thead>
    <tr>
      <th scope="col">No.</th>
      <th scope="col">タイトル</th>
      <th scope="col"></th>
    </tr>
  </thead>
  <tbody>
    <% @posts.each.with_index(1) do |post, i| %>
      <tr>
        <th scope="row"><%= i %></th>
        <td><%= post.title %></td>
        <td>
          <%= link_to "詳細", post_path(post) %>
          <%= link_to "編集", edit_post_path(post) %>
          <%= link_to "削除", post, method: :delete, data: { confirm: "削除しますか?" } %>
        </td>
      </tr>
    <% end %>
  </tbody>
</table>
```
今回は詳細・編集・削除のリンクが縦に並ぶように、<font color="DeepPink">table</font> ダグを使用。
<font color="DeepPink">each </font>メソッドを使い、タイトルのみ繰り返し処理を行います。
ここで注意したいのが削除のリンクで、
<font color="DeepPink">link_to </font>を使用した場合、指定がない限りHTTPメソッドは<font color="DeepPink"> GET </font>が使用されます。
しかし削除の場合は、<font color="DeepPink">DELETE </font>なので指定してあげる必要があります。
また<font color="DeepPink"> data: { confirm: "削除しますか?" } </font>を記述することで、削除ボタンをクリックした際にダイアログを表示することができます。

### 削除機能（destroy）
先ほどの投稿一覧ページで削除のリンクは作成済みのため、コントローラの設定のみ行います。
```ruby:app/controllers/posts_controller.rb
class PostsController < ApplicationController

# 略

 def destroy
   post = Post.find(params[:id])
   post.destroy!
   redirect_to root_path
 end

end
```
ビューファイルは削除済みなので、ここでも変数は <font color="DeepPink">@</font> は付けないようにします。

### 編集機能（edit, update）
この編集機能ができれば、CRUD処理は完成です。
長くなり、疲れも溜まってきたところですが、最後がんばりましょう！

まずはコントローラから
```ruby:app/controllers/posts_controller.rb
class PostsController < ApplicationController

# 略

 def edit
   @post = Post.find(params[:id])
 end

 def update
  post = Post.find(params[:id])
  post.update!(post_params)
  redirect_to post_path(post)
 end

# 略

end
```
編集機能も削除機能と似た感じで、投稿データの<font color="DeepPink"> id </font>を飛ばして、コントローラで受け取り、<font color="DeepPink">update </font>メソッドを実行し、投稿詳細ページにリダイレクトする形とします。

```erb:app/views/posts/edit.html.erb
<h1>編集</h1>
<%= form_with model: @post, local: true do |form| %>
  <div>
    <%= form.label :title, "タイトル" %>
    <%= form.text_field :title %>
  </div>
  <div>
    <%= form.label :content, "内容" %>
    <%= form.text_field :content %>
  </div>
  <div>
    <%= form.submit "送信" %>
  </div>
<% end %>
```
ここは新規投稿機能（new.html.erb）とほぼ同じです。
<font color="DeepPink">form_with </font>でデータベースに存在するかを判定し、今回は存在するので、updateアクションに自動的に振り分けてくれます。

これで完成となるので、サーバーを起動し、一通りの操作が可能か確認してみましょう。

次回は今回作成したCRUD処理で実装した部分を、リファクタリングし、フラッシュメッセージの設定、バリデーションの追加と進めていきます！

お疲れ様でした！