『メッセージを投稿』できる最低限のRailsアプリを丁寧に作る(これで初心者完全卒業!)

# この記事の基本的な方針
メッセージを投稿して一覧表示するだけの簡単なアプリを作ります。
完成するのは以下のたった4画面のアプリです。

【TOP画面(ログイン前)】             　　　 【TOP画面(ログイン後)】
<img width="230" display="inline" alt="a0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/066baa43-6945-9174-0040-403550137cbe.png">    <img width="230" display="inline" alt="a4.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/a12d6aa3-f04f-c7e1-6632-406ce9599741.png">
【登録画面】
<img width="230" alt="a1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/a0c57e94-7fd4-45ee-34c7-bf08dcb9a4a5.png">
【ログイン画面】
<img width="230" alt="a2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/8c1ea7d6-3f1b-5624-71b8-50a5f16770b3.png">
【投稿画面】
<img width="230" alt="a3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/7d018380-525d-2759-08e7-2d402963d8a7.png">
これを丁寧に作って、初心者を卒業しましょう。
# 想定する読み手
既に一度Railsアプリをチュートリアルやスクール等で作ったことがある方を想定しております。
Mac使用で、パソコンの環境構築は完了していることが前提です。
# 具体的なコーディング手順
[完成品GitHub](https://github.com/annaPanda8170/minimum_rails_application/tree/%E3%80%8E%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%82%92%E6%8A%95%E7%A8%BF%E3%80%8F%E3%81%A7%E3%81%8D%E3%82%8B%E6%9C%80%E4%BD%8E%E9%99%90%E3%81%AERails%E3%82%A2%E3%83%97%E3%83%AA%E3%82%92%E4%B8%81%E5%AF%A7%E3%81%AB%E4%BD%9C%E3%82%8B(%E3%81%93%E3%82%8C%E3%81%A7%E5%88%9D%E5%BF%83%E8%80%85%E5%AE%8C%E5%85%A8%E5%8D%92%E6%A5%AD!))（masterではなくこの記事のタイトルと同名のブランチなので注意して下さい）
## ①アプリを立ち上げる
まずターミナルを開き`cd`コマンドを使い、アプリを立ち上げたい場所に移動します。
今回はデスクトップに作ります。

```bash:Terminal
$ cd    #ルートディレクトリに移動する
$ cd Desktop   #デスクトップに移動する
```
そして`rails new`します。

```bash:Terminal
$ rails _5.2.4.1_ new <名前> -d myspl
```

します。今回はのバージョンは`5.2.4.1`にしました。
MySQLをデータベースとして使用しますので、`-d mysql`を付けています。
これでアプリが立ち上げられたと思うので、そのアプリのルートディレクトリに移動します。

```bash:Terminal
$ cd <名前>
```

データベースを作ります。

```bash:Terminal
$ rails db:create
```

（中身が空であってもこの時点でデータベースは作っておかないと、ブラウザでアクセスした時に`ActiveRecord::NoDatabaseError`になってしまいます。）

そして

```bash:Terminal
rails  s
```

でサーバーを立ち上げて、ブラウザで`localhost:3000/`にアクセスすると
<img width="400" alt="rails new.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/a4a1f857-28b0-e4b4-d20a-2f140f357261.jpeg">
これが表示されます。

続いて、コントローラを作ります。

```bash:Terminal
$ rails g controller <名前>
```

名前は自由ですが一番関連のある（データベースの）テーブル名の複数形にするのが一般的です。
今回はTOP画面にmessage一覧を表示するので、

```bash:Terminal
$ rails g controller messages index
```

今回`index`を加えていますが、このように名前の後にアクションを並べれば、そのアクション用のhtml.erbファイルが作られ、ルーティングとコントローラファイルに必要なことが追記されます。アクションはもちろん複数並べてよく、コンマ無しで隙間だけ開けて並べていきます。

ここでエディタを起動します。

まずルーティングを設定をします。上でアクション名を並べていればすでに書かれています。

```ruby:config/routes.rb
get "messages/index"
```

取り急ぎ、適当にビューを書きます。

```erb:app/views/messages/index.html.erb
<div>こんにちは</div>
```

ここでブラウザで`localhost:3000/コントローラ名/index`にアクセスすれば、「こんにちは」が見れるはずです。

通常メインのindex画面をルート画面とするので

```ruby:config/routes.rb
root "messages#index"
```

を追記しておきます。`localhost:3000`でもアクセスできるようになります。
## ②ログイン機能を作る
ログイン機能は自作することもできますが、deviseというgemを使うことが極めて一般的であるのでこれを今回は使用します。

deviseをインストールします。

```ruby:Gemfile
gem 'devise'
```

こちらを追記し、ターミナルで

```bash:Terminal
$ bundle install
```

をし、そしてdeviseを使い始めるにはターミナルで

```bash:Terminal
rails g devise:install
```

をする必要があります。これでconfigディレクトリにファイルができたので、サーバの再起動の必要があります。
さらに、

```bash:Terminal
rails g devise <モデル名>
```

をすれば必要ないくつかのファイルが作られます。この時のモデル名はなんでもいいのですが、userとするのが一般的です。

続いて、上の`rails g devise user` でマイグレーションファイルも作られているので、

```bash:Terminal
$ rails db:migrate
```

します。これでデータベースにユーザー情報用のテーブルが作られます。

deviseはデフォルトではEmailとPasswordで登録する仕様になっています。EmailやPassword以外を含めて登録する場合やビューやコントローラを編集する場合は別の手続きが必要ですが、今回はこのままでいきます。

この時点でURIをブラウザに直接打ち込めば新規登録画面とログイン画面が表示される段階まで来ていますので確認します。URIは `rails routes`で確認します。
会員登録は`localhost:3000/users/sign_up`ですね。結果は以下です。
<img width="300" alt="in.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/d507a04f-8a1a-8524-584f-2cbb401ae5d6.png">
ログインの`localhost:3000/users/sign_in`も同様ですね。
ここで適当に新規登録をしてみて、成功すると自動的にログイン状態になり、『こんにちは』のTOP画面に遷移します。これもログインしたらルート画面に遷移するdeviseのデフォルトの機能です。
これでログイン機能完成です。
## ③TOP画面作成
さてあとは今『こんにちは』になっているTOP画面を整えて、取り急ぎ`会員登録画面`と`ログイン画面`と`TOP画面`でぐるぐるできるだけの状態を作ります。
TOP画面は、
ログイン状態であれば、そのユーザーのEmailアドレスとログアウトへのリンク、
非ログイン状態であれば、新規登録へのリンクとログインへのリンクが表示されるようにします。
全体を一度に載せてみます。

```app/views/コントローラ名/index.html.erb
<% if user_signed_in? %>
  <%= current_user.email %>
  <%= link_to 'ログアウト', destroy_user_session_path, method: :delete %>
<% else %>
  <%= link_to '新規登録', new_user_registration_path %>
  <%= link_to 'ログイン', new_user_session_path %>
<% end %>
```

まず、このファイルのように拡張子を`.erb`は、HTMLファイルにRubyのコードを埋め込めるようになります。
`<%= %>`と`<% %>`が重要ですね。`<%= %>`と`<% %>`の違いは「画面上に表示されるか否か」みたいな嘘教えられたことないですか？`<%= %>`や`<% %>`の違いは「HTMLとして出力するか否か」です。きちんと覚えましょう。
if文で条件分岐されてtrueとなった方のみがHTMLとして出力されるので、もちろんif文自体はHTMLとして出力はしませんので`<% %>`を使います。
後の説明は省きます。
これで以下のような状態ができていると思います。


【TOP画面(ログイン前)】             　　　 【TOP画面(ログイン後)】
<img width="230" display="inline" alt="a0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/066baa43-6945-9174-0040-403550137cbe.png">    <img width="230" alt="a9.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/23eb0bd8-130a-e4f5-2c6e-2e5071388877.png">
【登録画面】
<img width="230" alt="a1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/a0c57e94-7fd4-45ee-34c7-bf08dcb9a4a5.png">
【ログイン画面】
<img width="230" alt="a2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/8c1ea7d6-3f1b-5624-71b8-50a5f16770b3.png">

ここまでが難しい場合は[過去記事](https://qiita.com/annaPanda8170/items/8856425be9d563fbffa7)に戻ってみてください。
次に行きます。
## ④メッセージテーブルを作る
`マイグレーションファイル`と`モデル`を作るため、

```bash:Terminal
$ rails g model message
```
を打ちます。
`マイグレーションファイル`はデータベースのテーブルを作るためのファイルで、`モデル`はデータベースからデータを出し入れするときの設定やルールを書くファイルです。もう大丈夫ですね？

一般的にモデル名は単数形でコントローラ名は複数形です。

ちなみにこれらのファイルは`Active Record`というライブラリを継承していて、そのおかげでデータベースに関することをRubyで記述できます。本来はMySQLデータベースとやりとりするにはSQLという言語で書かなければならないので、`Active Record`が翻訳をしてくれているということになります。

続いてマイグレーションファイルを書いて、メッセージテーブルを完成させます。
今回はメッセージの内容とそれを誰が投稿したかの情報を保存できるようにしようと思うので、`t.string :message`と`t.references :user, foreign_key: true`を追記し

```ruby:db/migrate/2020xxxxxxxxxx_create_messages.rb
class CreateMessages < ActiveRecord::Migration[5.2]
  def change
    create_table :messages do |t|
      t.string :message
      t.references :user, foreign_key: true
      t.timestamps
    end
  end
end
```

となるようにします。
ちなみにこの時点でブラウザからアクセスしようとするとエラーになります。理由はもちろん

```bash:Terminal
$ rails db:migrate
```

していないからですね。これを済ませたら、メッセージテーブルは完成です。
一応ちゃんと出来ているかデータベースを見に行ってみましょう。
<img width="441" alt="mysql.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/2cb082f0-485a-b707-cb4c-0139cfb6098b.png">
私はSequelProを使っていてこんな感じです。

最後にモデルにテーブル同士の関係を書きましょう。これが無くても投稿できなくはないのですが、投稿した内容を引き出して扱う上で便利なので今済ませてしまいましょう。以下を追記します。

```ruby:app/models/message.rb
belongs_to :user
```

```ruby:app/models/user.rb
has_many :messages
```

## ⑤メッセージを投稿できるようにする

必要な編集は`ルーティング`、`ビュー`、`コントローラ` です。
まずルーティング。

```ruby:config/routes.rb
Rails.application.routes.draw do
  devise_for :users
  root 'messages#index'
  resources :messages, only: [:index, :new, :create]
end
```

resourcesを用いた記法に変えました。ここは大丈夫ですね？
次はビュー。newのビューファイルがないので、`app/views/messages/`ディレクトリに作り以下を作ります。

```app/views/messages/new.html.erb
<%= form_with(model: @message, local: true) do |f| %>
  <%= f.text_field :message %>
  <%= f.submit "投稿" %>
<% end %>
```

`<%= %>`と`<% %>`の違いはなんでしたか？そうです、「HTMLとして出力するか否か」です。

画面上はこんな感じになるはずです。
<img width="199" alt="form.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/c938e13e-2b0c-1929-9bfe-f6b132e1c9de.png">

`<%= form_with(model: @message, local: true) do |f| %>`はHTMLのform要素に対応しています。

最後はコントローラです。少し詳しく行きます。

```ruby:app/controllers/messages_controller.rb
class MessagesController < ApplicationController
  before_action :to_root, except: [:index]
  def index
  end
  def new
    @message = Message.new
  end
  def create
    @message = Message.new(message_params)
    @message.save
    redirect_to root_path
  end
  private
  def message_params
    params.require(:message).permit(:message).merge(user_id: current_user.id)
  end
  def to_root
    redirect_to root_path unless user_signed_in?
  end
end
```

完成品はこれですが、一からこれを構築してみます。
私であればまず

```ruby:app/controllers/messages_controller.rb
class MessagesController < ApplicationController
  def index
  end
  def new
    @message = Message.new
  end
  def create
    binding.pry
    @message = Message.new(??)
    @message.save
  end
end
```

ここまで書きます。`??`の値を知りたいですね。
おっと`binding.pry`ってなんですか？デバック用のコードです。ここでは詳しい説明は省きます。これを使うために

```ruby:Gemfile
gem 'pry-rails'
```

を追記して、

```bash:Terminal
$ bundle install
```

します。
この状態で、`rails s`でサーバを起動し（すでに起動していたら再起動を忘れずに）、ログイン状態にし、ブラウザで`http://localhost:3000/messages/new`にアクセスし、フォームに適当な文字を入れて、投稿ボタンを押してみます。
するとブラウザが待機状態になり、ターミナルで変数の中身を確認できるようになります。
ここで`params`と打ってみます。

```bash:Terminal
> params
```

paramsはページ遷移時のデータがハッシュ形式で格納されているActionController::Parametersクラスのインスタンスです。

```bash:Output
=> <ActionController::Parameters {"utf8"=>"✓", "authenticity_token"=>"BZIJ8UtNrNugQOI94y8iWX5dKc2Z2bZaKxKXm8X/tV1cvJxIPlC/MaL7o0WrfTZccD1dr7IPC8Nlq8vKHiFhdg==", "message"=>{"message"=>"こんにちは"}, "commit"=>"投稿", "controller"=>"messages", "action"=>"create"} permitted: false>
```

こんな感じで値が取れます。これがインスタンスの情報です。`<`直後がクラス名で、その先がハッシュ形式になっているので例えば`"✓"`の値を取得したければ、

```:Terminal
> params["utf8"]
```

普通にこうします。
ちなみに、

```:Terminal
> params[:"utf8"]
```

```:Terminal
> params[:utf8]
```

これらでも取得できます。親切に準備してくれたんですね。
さて自分が投稿した`"こんにちは"`を取得するには、二重ハッシュになっているので

```:Terminal
> params[:message][:message]
```

これですね。

あれ？これ`<>`はなんなの？値を取得するとき気にしなくていいの？
いいみたいですね。すみません、完璧には理解できていません。

ちなみに、試しに空っぽのMessageモデルのインスタンスを作ってみると

```bash:Terminal
>  Message.new
```

```bash:Output
=> #<Message:0x00007fcfcde17130 id: nil, message: nil, user_id: nil, created_at: nil, updated_at: nil>
```

ActionController::Parametersのインスタンスであれば

```bash:Terminal
> ActionController::Parameters.new
```

```bash:Output
=> <ActionController::Parameters {} permitted: false>
```

ふむふむ、最初の`#`はなんだろう？この`0x00007fcfcde17130`はなんだろう？
誰か教えてください(笑)

この待機状態から抜け出すには、コントローラ内の`binding.pry`を削除して、ターミナルで`exit`を打ちます。

さて、保存すべきは投稿されたmessageの内容と、投稿した自分のidです。もうすでに両方ともわかっていますね。
以下のようになります。

```ruby:app/controllers/messages_controller.rb
class MessagesController < ApplicationController
  def index
  end
  def new
    @message = Message.new
  end
  def create
    @message = Message.new({message: params[:message][:message], user_id: current_user.id})
    @message.save
  end
end
```

`current_user`はgemのdeviseが用意しているものですね。newの引数はハッシュ形式ですが、`{}`を省略して`@message = Message.new(message: params[:message][:message], user_id: current_user.id)`でも構いません。
これで一度投稿してみましょう。
<img width="372" alt="goodafternoon.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/5458ca75-71e0-c0dc-d329-85ac79cf1aab.png">
問題なさそうですね。投稿はうまくいくけれどそのままの画面で止まってしまうので、createアクションの中の最後に`redirect_to root_path`を加えます。

さてこれで一応できましたが、一般的にはストロングパラメータというものを使ってセキュリティを高めるようです。
以下に直します。

```ruby:app/controllers/messages_controller.rb
class MessagesController < ApplicationController
  def index
  end
  def new
    @message = Message.new
  end
  def create
    @message = Message.new(message_params)
    @message.save
    redirect_to root_path
  end
  private
  def message_params
    params.require(:message).permit(:message).merge(user_id: current_user.id)
  end
end
```

requireとpermitはActionController::Parametersクラスのメソッドで、mergeはHashクラスのメソッドです。permitでparamsから値をとり、他の情報をデータベースに保存するリストに加える場合はmergeを使うようですね。
private以下ではアクションでないメソッドを定義します。その他の詳しい説明は省きます。

あと、ログインしていない人が`localhost:3000/messages/new`をブラウザに直接入力して投稿することは想定していないですし、`current_user.id`の値がなくておかしなことになるので、ログインしていない人は投稿画面に入れずにroot画面に遷移するようにします。
コントローラのclass内の最後に

```ruby:app/controllers/messages_controller.rb
  def to_root
    redirect_to root_path unless user_signed_in?
  end
```

これを置き、最初に

```ruby:app/controllers/messages_controller.rb
  before_action :to_root, except: [:index]
```

これを置きます。詳しい説明は省きます。
これでコントローラの完成品にたどり着きました。

ここら辺でTOP画面の条件分岐のログイン中の側に投稿画面のリンクをのせます。PrefixやURIを調べるのは`rails routes`でしたね。

```app/views/messages/index.html.erb
<% if user_signed_in? %>
  <%= current_user.email %>
  <%= link_to 'ログアウト', destroy_user_session_path, method: :delete %>
  <%= link_to '投稿', new_message_path %>
<% else %>
  <%= link_to '新規登録', new_user_registration_path %>
  <%= link_to 'ログイン', new_user_session_path %>
<% end %>
```

ここで、ログイン状態で投稿できてTOP画面に戻ることと、ログアウト状態で`localhost:3000/messages/new`を打ってもTOP画面に遷移することをブラウザで確認しましょう。

この状態で、メッセージを投稿する機能が完成しました。
実際に投稿してデータベースで結果を確認してみましょう。大丈夫ですね。

## ⑥TOP画面にメッセージ一覧を表示する
TOP画面に、メッセージとそれと一緒に投稿された画像全てとそれらの投稿者のEmailを一覧で表示します。これができたら完成とします。

以下を追記します。

```ruby:app/controllers/messages_controller.rb
class MessagesController < ApplicationController
  〜省略〜
  def index
    @messages = Message.all
  end
  〜省略〜
end
```

```:app/views/messages/index.html.erb
<% if user_signed_in? %>
  <%= current_user.email %>
  <%= link_to 'ログアウト', destroy_user_session_path, method: :delete %>
  <%= link_to '投稿', new_message_path %>
  <% @messages.each do |m| %>
    <div><span style="color: red;"><%= m.user.email %></span><%= m.message %></div>
  <% end %>
<% else %>
  <%= link_to '新規登録', new_user_registration_path %>
  <%= link_to 'ログイン', new_user_session_path %>
<% end %>
```

まずメッセージと投稿者のEmailを表示してみました。今回はメッセージはログインした人のみに見れるようにしました。
簡単ですね。詳細は省きます。

以上で完成です。

この続きにあたる、[『メッセージと複数画像の投稿』ができる最低限のRailsアプリを丁寧に作る](https://qiita.com/annaPanda8170/items/b8ce5c1f155ba45d67b3)もよろしければどうぞ。