こんにちは!まろんぼーいです。
今回は、「初心者がWebアプリを作ってみた」シリーズの第11回です。
このシリーズでは、過去に完全初心者の僕が作ったWebアプリを参考に、
初心者なりに試行錯誤しながらその改良版を一から作っていく過程をお見せしています。
このシリーズのモットーは、「同様の初心者が、アプリ作成の流れをつかみながら同様のアプリを作れるようになること」です。
ということで、基本的にアプリを作成していく過程を順を追って解説していますが、
「正確な内容」よりも、「わかりやすいなんとなくのイメージ」を重視して記事を書いています。
経験者の方からすると、「正確にはちがう!」と突っ込みたくなるような内容もあるかと思いますが、
難しい知識よりも、なんとなくでいいので流れをつかむ!
そんな内容となっていますので、同じ初心者の方の参考になればうれしいです!
それでは今回もはじめていきましょう!
〇前回までの進捗
前回は、ナビバーとフラッシュメッセージ、エラーメッセージの設定を行いました。
今回は、ログイン後のページ設定を行います。
また、ログアウト機能を実装し、新規登録→ログイン→トップページ→ログアウトの各ページへの遷移を行えるようにしていきたいと思います。
〇ログイン後のページ設定
今回やること
さて、表題のとおり始めていきますが、
正直ピンと来ていない方いらっしゃるかもしれません。
何をするかかみ砕いて説明したいと思います。
以前、トップページを作成しました。
トップページは、アプリを始めた際に最初に入るページ、いわゆるアプリの顔です。
現時点では、「ようこそ」と「始める」の文字のみが写っている状態になっています。
このページ自体は問題ないのですが、考慮する必要があるのが、「ログイン状態」の場合です。
一度ログインすると一定時間はログイン状態が保持されています。
一方で、現在のトップページはログインしていようがいまいがユーザログインから始めないといけない構造となってしまっています。
ということで、今回やることは以下の2つです。
①トップページについて、ログインしていない場合とログインしている場合の場合分けを行う
②ログインしている状態のページレイアウトを作成する
これを踏まえたうえで進んでいきましょう!
「始める」をボタンに
意気揚々と始めたところ出鼻をくじくようで申し訳ありませんが、ここで一つ謝らせてください(笑)。
「始める」の部分についてはログイン画面へのリンクボタンにするつもりだったのですが、
ただの見出し文になっていました。
まずは、「始める」をボタンにしちゃいます!
views/toppagesのindex.html.erbを開きます。
現在、「始める」は見出しを表すh3で囲まれていると思います。
それを以下のように変えましょう。
1 |
<%= link_to '始める', new_session_path, class: 'btn btn-lg btn-primary' %> |
これで、「始める」がログインページへ遷移するボタンとなりました。

ログイン有無の場合分け ①sessions_helper.rbの編集
それでは無事「始める」がボタンになったので、ログインしているか否かによる場合分けを行っていきます。
場合分けのためには、ログインしているか否かの判定を行う必要があります。
これはデフォルトで判別メソッドがあるわけではないので、まずはこれを自分で作ります。
ここでは、helperというファイルを使います。
helperはviewファイルの補助ファイルです。
viewファイルはページのレイアウトをつかさどっていますが、より凝ったレイアウトにしようとするとコードが複雑になってしまいがちです。
なので、できるだけ余計な情報はviewに書きたくないです。
今回の場合、
「ログインを判別するメソッドを定義するコードを書き、それをviewファイルで使う」
といった流れになりますが、定義の分までviewファイルに書きたくないです。
そんな時に使うのがこのhelperということです!!
ログインを判別するには、
変数current_user(現在アプリにログインしているユーザ)を定義し、
current_userに値が入っているか否かでログインの有無を判別します。
今回はhelperはsessions_helper.rbを使っています。
ログイン情報はsessionに保存されるので、sessionに格納されているデータを参照しcurrent_userを定義することからsessionのhelperを使っているというわけです。
ではcontrollers/helpersからsessions_helper.rbを開き、以下のように記述します。
1 2 3 4 5 6 7 8 9 |
module SessionsHelper def current_user @current_user ||= User.find_by(id: session[:user_id]) end def logged_in? !!current_user end end |
このシリーズで何回か出てきていますが、インスタンス変数@current_userは検索のfindを使って、
session[:user_id]、つまりsessionに保存されているユーザのidと同じidを持つユーザをuserモデルから探して入れています。
次にlogged_in?メソッドを定義しています。
これ、ちょいとややこしいのかみ砕いて書いときます。
このメソッドでは上で定義したcurrent_userメソッドを使っています。
「!」はnot演算子で、「!」の後ろに書かれているメソッドの逆を判別します。
つまり、current_userメソッドはsessionにユーザが格納されていた場合、
つまりログインユーザがいた場合にはユーザデータを返し、なかった場合にはfalseを返します。
!はこれの逆なので、ユーザがいた場合にfalse、いなかった場合にはtrueを返します。
さらにさらに、この文には!が二つ付いているので、逆の逆、
つまり、ユーザがいた場合にtrue、いなかった場合にはfalseを返します。
僕は最初に見た時、「わざわざ2回!を使わなくてもcurremt_userそのまま使えばよくね?」と思っていたのですが、
この2回!を行う理由は、ユーザがいた場合にtrueを返すためです。
logged_in?はログインの有無の判別なので、
ユーザがいた場合にユーザデータを返されてしまうとユーザによって値が変わってしまい判別しづらいです。
なので、ユーザがいた場合に一律でtrueを返すためにこの操作をしていたんです!
いやー素人ながらなんとか説明できてよかった(笑)。
ログイン有無の場合分け ①定義したメソッドをトップページに使用
viewで使うログイン有無の判別メソッドであるlogged_in?をhelperで定義できたので、
これを使ってtoppageのindex.html.erb内でログインの有無による条件分岐を行います。
基本的な使い方は以下の通りです。
1 2 3 4 5 |
<% if logged_in? %> <!-- ログインしている場合の記述 --> <% else %> <!-- ログインしていない場合の記述 --> <% end %> |
if文ですね。
logged_in?でtrueの場合、つまりログインしている場合とfalse、つまりログインしていない場合で条件分岐を行っています。
条件分岐を適用する前の現在のindex.html.erbを示しておきます。
1 2 3 4 5 6 |
<div class="center jumbotron"> <div class="text-center"> <h1>ようこそ!</h1> <%= link_to '始める', new_session_path, class: 'btn btn-lg btn-primary' %> </div> </div> |
これはログインする前の状態なので、条件分岐を適用させると以下のようになります。
1 2 3 4 5 6 7 8 9 10 |
<% if logged_in? %> <!-- ログインしている場合の記述 --> <% else %> <div class="center jumbotron"> <div class="text-center"> <h1>ようこそ!</h1> <%= link_to '始める', new_session_path, class: 'btn btn-lg btn-primary' %> </div> </div> <% end %> |
これで条件分岐ができました!
ログイン状態のレイアウト
さて、あとはログインしている状態のページレイアウトです。
今後投稿機能等を実装してく中でこちらのログインしている状態のレイアウトは改良していきますが、
とりあえず現在は以下のようにしておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<% if logged_in? %> <div class="center jumbotron"> <div class="text-center"> <h1><%= current_user.name %>さんがログイン中です</h1> </div> </div> <% else %> <div class="center jumbotron"> <div class="text-center"> <h1>ようこそ!</h1> <%= link_to '始める', new_session_path, class: 'btn btn-lg btn-primary' %> </div> </div> <% end %> |
これで条件分岐ができました!
〇ログイン→トップページの移行設定
ログイン後のトップページの設定ができたので、
今度はログインページから情報を入力してログインボタンを押した際にトップページにつながるよう設定を行います。
現在、ログインボタンを行った際のcreateメソッド(sessions_controller.rb)は以下のようになっています。
1 2 3 4 5 6 7 8 9 10 11 |
def create email = params[:session][:email] password = params[:session][:password] if login(email, password) flash[:success] = 'ログインに成功しました。' redirect_to root_url else flash.now[:danger] = 'ログインに失敗しました。' render :new end end |
ログインに成功した際はroot_url、つまりトップページに、
失敗した際はnew、これはsession自身のnewなのでsessionのnew.html.erbにそれぞれ遷移するように設定しています。
それではこれでログインページからログインしてみましょう!
(事前にてきとーにアカウントを作っておいてくださいね!)

あ、あれ…?
エラーが出てしもうた…
皆さんのでもエラーが出ましたかね。申し訳ないです。
内容見てみると、「Routing error」となっています。
「sessionのnew.html.erbにPostで遷移するRoutingがない」といったエラーのようです。
なぜだ(笑)。
とりあえずルートを確認。
1 2 |
sessions POST /sessions(.:format) sessions#create new_session GET /sessions/new(.:format) sessions#new |
たしかにRoutesを見てみるとnewのパスはgetしかないけど、普通newのファイルはgetしかな使わないからなあ。
このように、僕のような初心者はエラーが出てしまうと解決が大変なんです…
というわけで色々見てみた結果、原因がわかりました。
ログインのviewファイルはこのようになっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<div class="text-center"> <h1>ログイン</h1> </div> <div class="row"> <div class="col-sm-6 offset-sm-3"> <%= form_with(url: new_session_path, scope: :session, local: true) do |f| %> <div class="form-group"> <%= f.label :email, 'Email' %> <%= f.email_field :email, class: 'form-control' %> </div> <div class="form-group"> <%= f.label :password, 'パスワード' %> <%= f.password_field :password, class: 'form-control' %> </div> <%= f.submit 'Log in', class: 'btn btn-primary btn-block' %> <% end %> <p><%= link_to '新規登録', new_user_path %></p> </div> </div> |
この3行目の「url: new_sessions_path」という記述。これはsessionのnew.html.erbをgetするパスです。
form_with文で入力した情報をsessionに保存するためのメソッドはcreateメソッドです。
なので本来なら、このurlはcreateをpostするパスを指定しなくてはいけません!
なので、さっき確認したルートのうち、「new_session」のパスではなく「sessions」のパスを使わなくてはいけなかったんですね。
というわけで、以下のように変えました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<div class="text-center"> <h1>ログイン</h1> </div> <div class="row"> <div class="col-sm-6 offset-sm-3"> <%= form_with(url: sessions_path, scope: :session, local: true) do |f| %> <div class="form-group"> <%= f.label :email, 'Email' %> <%= f.email_field :email, class: 'form-control' %> </div> <div class="form-group"> <%= f.label :password, 'パスワード' %> <%= f.password_field :password, class: 'form-control' %> </div> <%= f.submit 'Log in', class: 'btn btn-primary btn-block' %> <% end %> <p><%= link_to '新規登録', new_user_path %></p> </div> </div> |
これでもう一度ログインしてみたところ、上手くログインできました!!

お騒がせしました(笑)。
〇ログアウト機能の実装
では最後にログアウト機能だけ実装してしまいましょう。
例によってMCRVの順で作っていきます。
Modelはsessionを使用するので必要なし。
controllerはsessions_controllerのdestroyメソッドを使いますが、こちらについては以前以下のように作成しておきましたね。
1 2 3 4 5 |
def destroy session[:user_id] = nil flash[:success] = 'ログアウトしました。' redirect_to root_url end |
特に追記する必要はないです!
続いてRouterですが、現在のRoutes.rbを見てみると、
1 2 3 4 5 6 |
Rails.application.routes.draw do root to: 'toppage#index' resources :users resources :sessions, only: [:new, :create, :destroy] end |
このように、sessionのdestroyについてはすでにルーティング済みとなっているのでオッケーです。
というわけで、残りはviewのみです。
今回は、ナビバーにログアウトのメニューを追加してみます。
以下のように、ナビバーのパーシャルファイルのリストの場所にログアウトを追加しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<header class="mb-4"> <nav class="navbar navbar-expand-md navbar-light bg-light"> <div class="col-1"> <a class="navbar-brand" href="/">MyTravel</a> </div> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse justify-content-end col-9 flex-column" id="navbarSupportedContent"> <ul class="navbar-nav w-100 nav-justified"> <li class="nav-item"><%= link_to 'Signup', new_user_path, class: 'nav-link' %></li> <li class="nav-item"><%= link_to 'Login', new_session_path, class: 'nav-link' %></li> <li class="nav-item"><%= link_to 'Logout', session_path(current_user), class: 'nav-link', method: :delete %></li> </ul> </div> </nav> </header> |
ファイルを保存して再度アプリを見てみると、以下のようにログアウトのメニューが追加されました!

それではログアウトの項目をクリックして、ちゃんとログアウトされるか確認しましょう。

またエラーかーーーい。
内容はですね。
「idがないですよ」という内容見たいです。
destroyのメソッドの際にsessionを空にするので、userデータが消えidも空になってしまいます。
一方で、ルーティングを見てみると、
1 |
DELETE /users/:id(.:format) users#destroy |
usersの下層に「:id」とあります。
つまり、destroyメソッドの際にidが必要になってしまっています。
なので、ちょっとルーティングを工夫します。
まず、以下のようにroutes.rbに書かれているsessionのdestroyを消しちゃいます。
1 2 3 4 5 6 |
Rails.application.routes.draw do root to: 'toppage#index' resources :users resources :sessions, only: [:new, :create] end |
次に、以下の一文を追加します。
1 |
delete 'logout', to: 'sessions#destroy' |
このように、resoucesを使わなくても
「メソッド ‘パス名’, to: ‘パスを通す場所’」でルーティングが可能です。
ちなみに先ほどのresoucesのログアウトのパスは「session_path」となっていましたが、
わかりやすくするためにパス名を「logout_path」としました。
これで、ログアウトのパスにユーザのidが必要でなくなりました。
ナビバーのログアウトのパス名のところも「logout_path」に変えます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<header class="mb-4"> <nav class="navbar navbar-expand-md navbar-light bg-light"> <div class="col-1"> <a class="navbar-brand" href="/">MyTravel</a> </div> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse justify-content-end col-9 flex-column" id="navbarSupportedContent"> <ul class="navbar-nav w-100 nav-justified"> <li class="nav-item"><%= link_to 'Signup', new_user_path, class: 'nav-link' %></li> <li class="nav-item"><%= link_to 'Login', new_session_path, class: 'nav-link' %></li> <li class="nav-item"><%= link_to 'Logout', logout_path, class: 'nav-link', method: :delete %></li> </ul> </div> </nav> </header> |
再度ログアウトをクリックしたところ、無事ログアウトできました!!

これで、新規登録→ログイン→トップページ→ログアウトの一連の動作をエラーなく実行できるようになりました!!
〇次回 ~投稿機能の実装~
今回はちょっとボリュームがありましたが、これで終了です。
これで基本的なユーザ機能については実装できました!
ということで、次回からは投稿機能の実装に入っていきたいと思います。
本シリーズでまろんぼーいが実際に作成中のアプリについては、以下のURLで公開しています。
各viewページを直接確認する場合は、本記事と同様のURLを末尾につければ飛ぶことができます。
ご参考までに!
今回もご覧いただきありがとうございました!