0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-03-13

この記事の基本的な方針

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

【TOP画面(ログイン前)】     【TOP画面(ログイン後)】
a0.png a4.png
【登録画面】
a1.png
【ログイン画面】
a2.png
【投稿画面】
a3.png
これを丁寧に作って、初心者を卒業しましょう。

想定する読み手

既に一度Railsアプリをチュートリアルやスクール等で作ったことがある方を想定しております。
Mac使用で、パソコンの環境構築は完了していることが前提です。

具体的なコーディング手順

完成品GitHub(masterではなくこの記事のタイトルと同名のブランチなので注意して下さい)

①アプリを立ち上げる

まずターミナルを開きcdコマンドを使い、アプリを立ち上げたい場所に移動します。
今回はデスクトップに作ります。

Terminal
$ cd    #ルートディレクトリに移動する
$ cd Desktop   #デスクトップに移動する

そしてrails newします。

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

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

Terminal
$ cd <名前>

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

Terminal
$ rails db:create

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

そして

Terminal
rails  s

でサーバーを立ち上げて、ブラウザでlocalhost:3000/にアクセスすると
rails new.jpg
これが表示されます。

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

Terminal
$ rails g controller <名前>

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

Terminal
$ rails g controller messages index

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

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

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

config/routes.rb
get "messages/index"

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

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

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

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

config/routes.rb
root "messages#index"

を追記しておきます。localhost:3000でもアクセスできるようになります。

②ログイン機能を作る

ログイン機能は自作することもできますが、deviseというgemを使うことが極めて一般的であるのでこれを今回は使用します。

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

Gemfile
gem 'devise'

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

Terminal
$ bundle install

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

Terminal
rails g devise:install

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

Terminal
rails g devise <モデル名>

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

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

Terminal
$ rails db:migrate

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

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

この時点でURIをブラウザに直接打ち込めば新規登録画面とログイン画面が表示される段階まで来ていますので確認します。URIは rails routesで確認します。
会員登録はlocalhost:3000/users/sign_upですね。結果は以下です。
in.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画面(ログイン後)】
a0.png a9.png
【登録画面】
a1.png
【ログイン画面】
a2.png

ここまでが難しい場合は過去記事に戻ってみてください。
次に行きます。

④メッセージテーブルを作る

マイグレーションファイルモデルを作るため、

Terminal
$ rails g model message

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

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

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

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

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

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

Terminal
$ rails db:migrate

していないからですね。これを済ませたら、メッセージテーブルは完成です。
一応ちゃんと出来ているかデータベースを見に行ってみましょう。
mysql.png
私はSequelProを使っていてこんな感じです。

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

app/models/message.rb
belongs_to :user
app/models/user.rb
has_many :messages

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

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

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として出力するか否か」です。

画面上はこんな感じになるはずです。
form.png

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

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

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

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

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ってなんですか?デバック用のコードです。ここでは詳しい説明は省きます。これを使うために

Gemfile
gem 'pry-rails'

を追記して、

Terminal
$ bundle install

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

Terminal
> params

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

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モデルのインスタンスを作ってみると

Terminal
>  Message.new
Output
=> #<Message:0x00007fcfcde17130 id: nil, message: nil, user_id: nil, created_at: nil, updated_at: nil>

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

Terminal
> ActionController::Parameters.new
Output
=> <ActionController::Parameters {} permitted: false>

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

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

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

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)でも構いません。
これで一度投稿してみましょう。
goodafternoon.png
問題なさそうですね。投稿はうまくいくけれどそのままの画面で止まってしまうので、createアクションの中の最後にredirect_to root_pathを加えます。

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

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内の最後に

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

これを置き、最初に

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を一覧で表示します。これができたら完成とします。

以下を追記します。

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アプリを丁寧に作るもよろしければどうぞ。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?