Railsの基礎を使って、メッセージ投稿アプリを作ってみた　【第２章】（3.リファクタリング〜5.バリデーションの設定まで）

## 概要
前回の記事（第１章）で作成したアプリの続きです。
[Railsの基礎を使って、メッセージ投稿アプリを作ってみた　【第１章】（1.アプリ作成〜2.CRUD処理まで）](https://qiita.com/web_fumiya/items/11c45c5d49fa9ab832c3)

今回の第２章では、コントローラの処理のリファクタリング、ビューファイルを部分テンプレートで共通化、フラッシュメッセージの設定&表示、モデルにバリデーションの追加という流れで進めていきます。

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

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

## リファクタリング
まずコントローラの処理を見てみると、同じ処理を書いてる箇所が複数見当たります。
```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 

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

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

 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

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

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

end
```
`@post = Post.find(params[:id])`が２箇所、`post = Post.find(params[:id])`が２箇所あります。
複数箇所に同じ処理をかくと、修正が必要になったときに、全て変更することになり、手間もかかります。
なので今回は<font color="DeepPink"> before_action </font>フィルタを使って、共通化します。
```ruby:app/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :set_post, only: %i[show edit update destroy]

  def new
   @post = Post.new
  end

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

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

  def show
  end

  def edit
  end

  def update
   @post.update!(post_params)
   redirect_to post_path(post)
  end

  def destroy
   @post.destroy!
   redirect_to root_path
  end

 private

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

# 略
end
```
`before_acrion :メソッド名`と記述することにより、指定したアクションの前にメソッドを指定することが可能となります。
メソッド名は<font color="DeepPink"> private </font>に`set_post` を定義し、同じ処理を書いていたところを修正。
また、<font color="DeepPink"> update </font>、<font color="DeepPink"> destroy </font>に関しては、ビューファイルに変数を渡す必要がなかったので、<font color="DeepPink"> @ </font>のない変数を利用してましたが、共通化するために、<font color="DeepPink"> @ </font>をつけた変数に変更。

これで、コントローラのリファクタリングは完了です。
次にビューファイルを見てみると、新規投稿ページ（`new.html.erb`）と編集ページ(`edit.html.erb`)が１行目以外同じ内容となってます。

【新規投稿ページ】
```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 %>
```
【編集ページ】
```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 %>
```
同じ記述をしている部分のみ、別でファイルを作成し、作成したビューファイルをレンダリングする形とします。

【新たに作成したビューファイル】
```erb:app/views/posts/_form.html.erb
<%= 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 %>
```
【新規投稿ページ】
```erb:app/views/posts/new.html.erb
<h1>新規投稿</h1>
<%= render "form" %>
```
【編集ページ】
```erb:app/views/posts/edit.html.erb
<h1>編集</h1>
<%= render "form" %>
```

ここで注意したいのが、
- 新たに作成したファイルはファイル名の最初を<font color="DeepPink"> _ </font>から始まるようにすること。今回は`_form.html.erb`
- レンダリングする際は、<font color="DeepPink"><%= render "form" %></font>のように<font color="DeepPink">　_ </font>は付けないこと。

これで部分テンプレートは完成しましたが、新規投稿ページでも編集ページでも送信ボタンとなっています。
新規投稿ページでは送信ボタン、編集ページでは更新ボタンと表示されるよう変数を使用します。

まず、`_form.html.erb` の`<%= form.submit "送信" %>`を<font color="DeepPink"> button_name </font>とし、変数に変更します。
```erb:app/views/posts/_form.html.erb
<%= 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 button_name %>
  </div>
<% end %>
```
次に、`new.html.erb`と`edit.html.erb`で変数を定義して完成です。
```erb:app/views/posts/new.html.erb
<h1>新規投稿</h1>
<%= render "form", button_name: "投稿" %>
```
```erb:app/views/posts/edit.html.erb
<h1>編集</h1>
<%= render "form", button_name: "更新" %>
```

最後に、前章の最初で作成した全ページに表示されるリンクも、新たに別ファイルを作成し、レンダリングする形とします。

【新たに作成したファイル】
```erb:app/views/layouts/_header.html.erb
<%= link_to "投稿一覧", posts_path %> <%= link_to "新規投稿" new_post_path %>
```
`<%= render "layouts/header" %>`とし、上記で作成したファイルをレンダリング。
```erb:app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  
<!-- 略 -->

  <body>
    <%= render "layouts/header" %>
    <%= yield %>
  </body>
</html>
```
`app/views/layouts/application.html.erb`でレンダリングさせる場合は、必ず`<%= render "header" %>`ではなく、`<%= render "layouts/header" %>`にしないとエラーが出るので注意して下さい。

これでビューファイルの部分テンプレートは完了です。
ここで一度サーバーを起動し、問題なく機能するか確認することをお勧めします。

## フラッシュ
投稿、更新、削除した際は以下のようにリダイレクトするようになっています。
新規投稿後　→ 投稿詳細ページ
編集ページで更新後　→ 投稿一覧ページ
投稿一覧で削除後　→ 投稿一覧ページ

このままだと、しっかり投稿できているのか、更新、削除できているのかと不安に思う方もいると思うので、<font color="DeepPink">投稿しました</font>や<font color="DeepPink">削除しました</font>などメッセージが表示されるようにフラッシュメッセージを追加し、表示する作業に入ります。

フラッシュメッセージを追加するには、以下の２点の作業が必要です。
1. コントローラでフラッシュメッセージの設定
2. ビューでフラッシュメッセージの表示

まずはコントローラの方から進めていきます。
```ruby:app/controllers/posts_controller.rb
class PostsController < ApplicationController

# 略

 def create
   post = Post.create!(post_params)
   redirect_to post_path(post),notice: "投稿しました"
 end 

# 略

 def update
   post = Post.find(params[:id])
   post.update!(post_params)
   redirect_to post_path(post), notice: "更新しました"
 end

 def destroy
   post = Post.find(params[:id])
   post.destroy!
   redirect_to root_path, notice: "削除しました"
 end 

# 略

```
今回はリダイレクトと併用するので<font color="DeepPink"> notice: "メッセージ" </font>とします。
リダイレクトと併用しない場合は<font color="DeepPink">flash[:notice] = "メッセージ"</font>と書きます。
これで、コントローラのフラッシュメッセージの設定は完了です。

次にビューファイルでのフラッシュメッセージの表示に進みます。
全ページで使用できるように、`app/views/layouts/application.html.erb`に記述し、レンダリングする形とします。

【新たに作成したファイル】
```erb:app/views/layouts/_flash.html.erb
<% flash.each do |flash_type, msg| %>
  <p><%= msg %></p>
  <hr>
<% end %>
```

```erb:app/views/layouts/application.html.erb
<!-- 略 -->

 <body>
    <%= render "layouts/header" %>
    <%= render "layouts/flash" %>
    <%= yield %>
  </body>
</html>
```
フラッシュを表示させるだけであれば、`<%= flash[:notice] %>`、`<%= flash[:alert] %>`で表示されます。
今回は、両方を共通化させるために、<font color="DeepPink">each </font>メソッドを使用します。

これでフラッシュメッセージの設定&表示は完了です。
実際に、サーバーを起動し、新規投稿をした際や更新、削除した際、フラッシュメッセージが表示されているか確認することをお勧めします。

## バリデーションの追加
バリデーションとは、データベースに何かを保存する際に、事前に内容を検証する機能です。
例で挙げると、メールアドレスを登録する際、すでに登録されていた場合は、「こちらのアドレスはすでに登録済みです」と表示されることがあると思います。
これらは事前にバリデーションを定義しておくことで働く機能となります。

現在作成しているアプリでも、空文字で投稿できてしまったり、文字数制限がない状態です。
なので、データベースとやり取りをするモデルに以下のバリデーションを追加します。
- タイトル、内容共に空文字投稿禁止
- タイトルは50文字以下、内容は500文字以下
```ruby:app/models/post.rb
class Post < ApplicationRecord
  validates :title, presence: true, length: { maximum: 50 }
  validates :content, presence: true, length: { maximum: 500 }
end
```
コンソールを起動して、以下を入力し、実際に機能しているか確認してみましょう。
```console:コンソール
Post.create!(title: "", content: "")
Post.create!(title: "テスト", content: "")
Post.create!(title: "", cotent: "テスト")
Post.create!(title: "1" * 51, content: "テスト")
Post.create!(title: "テスト", content: "2" * 501)
```
確認後、`Validation failed`とエラーメッセージがターミナルに表示されることが確認できればOKです。

これでバリデーションの設定は完了となります。
次回の第３章では、バリデーションを設定したことによって発生するエラーの処理を変更し、エラーメッセージを表示させる作業に入ります。

お疲れ様でした！