この記事の基本的な方針
メッセージを投稿して一覧表示するだけの簡単なアプリを作ります。
完成するのは以下のたった4画面のアプリです。
【TOP画面(ログイン前)】 【TOP画面(ログイン後)】
【登録画面】
【ログイン画面】
【投稿画面】
これを丁寧に作って、初心者を卒業しましょう。
想定する読み手
既に一度Railsアプリをチュートリアルやスクール等で作ったことがある方を想定しております。
Mac使用で、パソコンの環境構築は完了していることが前提です。
具体的なコーディング手順
完成品GitHub(masterではなくこの記事のタイトルと同名のブランチなので注意して下さい)
①アプリを立ち上げる
まずターミナルを開きcd
コマンドを使い、アプリを立ち上げたい場所に移動します。
今回はデスクトップに作ります。
$ cd #ルートディレクトリに移動する
$ cd Desktop #デスクトップに移動する
そしてrails new
します。
$ rails _5.2.4.1_ new <名前> -d myspl
します。今回はのバージョンは5.2.4.1
にしました。
MySQLをデータベースとして使用しますので、-d mysql
を付けています。
これでアプリが立ち上げられたと思うので、そのアプリのルートディレクトリに移動します。
$ cd <名前>
データベースを作ります。
$ rails db:create
(中身が空であってもこの時点でデータベースは作っておかないと、ブラウザでアクセスした時にActiveRecord::NoDatabaseError
になってしまいます。)
そして
rails s
でサーバーを立ち上げて、ブラウザでlocalhost:3000/
にアクセスすると
これが表示されます。
続いて、コントローラを作ります。
$ rails g controller <名前>
名前は自由ですが一番関連のある(データベースの)テーブル名の複数形にするのが一般的です。
今回はTOP画面にmessage一覧を表示するので、
$ rails g controller messages index
今回index
を加えていますが、このように名前の後にアクションを並べれば、そのアクション用のhtml.erbファイルが作られ、ルーティングとコントローラファイルに必要なことが追記されます。アクションはもちろん複数並べてよく、コンマ無しで隙間だけ開けて並べていきます。
ここでエディタを起動します。
まずルーティングを設定をします。上でアクション名を並べていればすでに書かれています。
get "messages/index"
取り急ぎ、適当にビューを書きます。
<div>こんにちは</div>
ここでブラウザでlocalhost:3000/コントローラ名/index
にアクセスすれば、「こんにちは」が見れるはずです。
通常メインのindex画面をルート画面とするので
root "messages#index"
を追記しておきます。localhost:3000
でもアクセスできるようになります。
②ログイン機能を作る
ログイン機能は自作することもできますが、deviseというgemを使うことが極めて一般的であるのでこれを今回は使用します。
deviseをインストールします。
gem 'devise'
こちらを追記し、ターミナルで
$ bundle install
をし、そしてdeviseを使い始めるにはターミナルで
rails g devise:install
をする必要があります。これでconfigディレクトリにファイルができたので、サーバの再起動の必要があります。
さらに、
rails g devise <モデル名>
をすれば必要ないくつかのファイルが作られます。この時のモデル名はなんでもいいのですが、userとするのが一般的です。
続いて、上のrails g devise user
でマイグレーションファイルも作られているので、
$ rails db:migrate
します。これでデータベースにユーザー情報用のテーブルが作られます。
deviseはデフォルトではEmailとPasswordで登録する仕様になっています。EmailやPassword以外を含めて登録する場合やビューやコントローラを編集する場合は別の手続きが必要ですが、今回はこのままでいきます。
この時点でURIをブラウザに直接打ち込めば新規登録画面とログイン画面が表示される段階まで来ていますので確認します。URIは rails routes
で確認します。
会員登録はlocalhost:3000/users/sign_up
ですね。結果は以下です。
ログインのlocalhost:3000/users/sign_in
も同様ですね。
ここで適当に新規登録をしてみて、成功すると自動的にログイン状態になり、『こんにちは』のTOP画面に遷移します。これもログインしたらルート画面に遷移するdeviseのデフォルトの機能です。
これでログイン機能完成です。
③TOP画面作成
さてあとは今『こんにちは』になっているTOP画面を整えて、取り急ぎ会員登録画面
とログイン画面
とTOP画面
でぐるぐるできるだけの状態を作ります。
TOP画面は、
ログイン状態であれば、そのユーザーのEmailアドレスとログアウトへのリンク、
非ログイン状態であれば、新規登録へのリンクとログインへのリンクが表示されるようにします。
全体を一度に載せてみます。
<% 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画面(ログイン後)】
【登録画面】
【ログイン画面】
ここまでが難しい場合は過去記事に戻ってみてください。
次に行きます。
④メッセージテーブルを作る
マイグレーションファイル
とモデル
を作るため、
$ rails g model message
を打ちます。
マイグレーションファイル
はデータベースのテーブルを作るためのファイルで、モデル
はデータベースからデータを出し入れするときの設定やルールを書くファイルです。もう大丈夫ですね?
一般的にモデル名は単数形でコントローラ名は複数形です。
ちなみにこれらのファイルはActive Record
というライブラリを継承していて、そのおかげでデータベースに関することをRubyで記述できます。本来はMySQLデータベースとやりとりするにはSQLという言語で書かなければならないので、Active Record
が翻訳をしてくれているということになります。
続いてマイグレーションファイルを書いて、メッセージテーブルを完成させます。
今回はメッセージの内容とそれを誰が投稿したかの情報を保存できるようにしようと思うので、t.string :message
とt.references :user, foreign_key: true
を追記し
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
となるようにします。
ちなみにこの時点でブラウザからアクセスしようとするとエラーになります。理由はもちろん
$ rails db:migrate
していないからですね。これを済ませたら、メッセージテーブルは完成です。
一応ちゃんと出来ているかデータベースを見に行ってみましょう。
私はSequelProを使っていてこんな感じです。
最後にモデルにテーブル同士の関係を書きましょう。これが無くても投稿できなくはないのですが、投稿した内容を引き出して扱う上で便利なので今済ませてしまいましょう。以下を追記します。
belongs_to :user
has_many :messages
⑤メッセージを投稿できるようにする
必要な編集はルーティング
、ビュー
、コントローラ
です。
まずルーティング。
Rails.application.routes.draw do
devise_for :users
root 'messages#index'
resources :messages, only: [:index, :new, :create]
end
resourcesを用いた記法に変えました。ここは大丈夫ですね?
次はビュー。newのビューファイルがないので、app/views/messages/
ディレクトリに作り以下を作ります。
<%= form_with(model: @message, local: true) do |f| %>
<%= f.text_field :message %>
<%= f.submit "投稿" %>
<% end %>
<%= %>
と<% %>
の違いはなんでしたか?そうです、「HTMLとして出力するか否か」です。
<%= form_with(model: @message, local: true) do |f| %>
はHTMLのform要素に対応しています。
最後はコントローラです。少し詳しく行きます。
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
完成品はこれですが、一からこれを構築してみます。
私であればまず
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
ってなんですか?デバック用のコードです。ここでは詳しい説明は省きます。これを使うために
gem 'pry-rails'
を追記して、
$ bundle install
します。
この状態で、rails s
でサーバを起動し(すでに起動していたら再起動を忘れずに)、ログイン状態にし、ブラウザでhttp://localhost:3000/messages/new
にアクセスし、フォームに適当な文字を入れて、投稿ボタンを押してみます。
するとブラウザが待機状態になり、ターミナルで変数の中身を確認できるようになります。
ここでparams
と打ってみます。
> params
paramsはページ遷移時のデータがハッシュ形式で格納されているActionController::Parametersクラスのインスタンスです。
=> <ActionController::Parameters {"utf8"=>"✓", "authenticity_token"=>"BZIJ8UtNrNugQOI94y8iWX5dKc2Z2bZaKxKXm8X/tV1cvJxIPlC/MaL7o0WrfTZccD1dr7IPC8Nlq8vKHiFhdg==", "message"=>{"message"=>"こんにちは"}, "commit"=>"投稿", "controller"=>"messages", "action"=>"create"} permitted: false>
こんな感じで値が取れます。これがインスタンスの情報です。<
直後がクラス名で、その先がハッシュ形式になっているので例えば"✓"
の値を取得したければ、
> params["utf8"]
普通にこうします。
ちなみに、
> params[:"utf8"]
> params[:utf8]
これらでも取得できます。親切に準備してくれたんですね。
さて自分が投稿した"こんにちは"
を取得するには、二重ハッシュになっているので
> params[:message][:message]
これですね。
あれ?これ<>
はなんなの?値を取得するとき気にしなくていいの?
いいみたいですね。すみません、完璧には理解できていません。
ちなみに、試しに空っぽのMessageモデルのインスタンスを作ってみると
> Message.new
=> #<Message:0x00007fcfcde17130 id: nil, message: nil, user_id: nil, created_at: nil, updated_at: nil>
ActionController::Parametersのインスタンスであれば
> ActionController::Parameters.new
=> <ActionController::Parameters {} permitted: false>
ふむふむ、最初の#
はなんだろう?この0x00007fcfcde17130
はなんだろう?
誰か教えてください(笑)
この待機状態から抜け出すには、コントローラ内のbinding.pry
を削除して、ターミナルでexit
を打ちます。
さて、保存すべきは投稿されたmessageの内容と、投稿した自分のidです。もうすでに両方ともわかっていますね。
以下のようになります。
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)
でも構いません。
これで一度投稿してみましょう。
問題なさそうですね。投稿はうまくいくけれどそのままの画面で止まってしまうので、createアクションの中の最後にredirect_to root_path
を加えます。
さてこれで一応できましたが、一般的にはストロングパラメータというものを使ってセキュリティを高めるようです。
以下に直します。
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内の最後に
def to_root
redirect_to root_path unless user_signed_in?
end
これを置き、最初に
before_action :to_root, except: [:index]
これを置きます。詳しい説明は省きます。
これでコントローラの完成品にたどり着きました。
ここら辺でTOP画面の条件分岐のログイン中の側に投稿画面のリンクをのせます。PrefixやURIを調べるのはrails routes
でしたね。
<% 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を一覧で表示します。これができたら完成とします。
以下を追記します。
class MessagesController < ApplicationController
〜省略〜
def index
@messages = Message.all
end
〜省略〜
end
<% 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アプリを丁寧に作るもよろしければどうぞ。