I am うるぞー

コードの書ける恋愛スペシャリスト

【Ruby on Rails】deviseでログインした後のリダイレクト先を変更し、POSTする方法

どうも、Ruby on Railsエンジニアのうるぞーです。

今日は珍しくエンジニアらしいことを書きます。
仕事でdeviseの処理に困ったので、それをまとめていきまっす。

やりたかったこと

以下の処理をしたい。

『未ログイン=>投稿ページ=>諸々入力して投稿(POST)=>ログイン画面=>ログインする=>登録完了=>投稿詳細』

RailsはGETリクエストにしかリダイレクトできないので、deviseでログインした後はトップ画面に戻っちゃうんです。

『未ログイン=>投稿ページ=>諸々入力して投稿(POST)=>ログイン画面=>ログインする=>トップページにリダイレクトしちゃう』

↑せっかく入力したのにまた最初から入力し直さなきゃいけないので、ユーザーにとって不親切。

これを解消しよう!というのがこの記事の目的です。

リダイレクト先をroot_pathから別のページに変更して、合わせてPOSTをしていきます。

どうするのか

こちらの記事を参考にさせていただきました。
▶︎devise ログイン後のリダイレクトについて(POST編) | Railsの小技 | DoRuby

同じ流れで進めていきますが、(僕がヘボエンジニアだからか)わからなかったところがあったので少し解説を加えて書いていきます。

参考サイトによると、

POSTなので、直接遷移が出来なくて、after_login_to_postを作成して、その後自動サブミットすれば、POSTリクエストにリダイレクトを実現します。

ということなので、順にやっていきましょう。

1. ログインしていない時にcreateをしたらセッション作成

# app/controllers/posts_controller.rb
 class PostsController < ApplicationController
  before_action :authenticate_customer!, only: [:edit, :update, :destroy]
 # ↑createは含めないように

  def create
    if user_signed_in? #ログインしていたら登録処理
      @post = Post.new(post_params)
      @post.user_id = current_user.id
      respond_to do |format|
        if @post.save
          format.html { redirect_to @post, notice: '投稿完了!' }
          format.json { render :show, status: :ok, location: @post }
        else 
          format.html { render :new }
          format.json { render json: @post.errors, status: :unprocessable_entity }
        end
      end
    else # ログインしていなかったらセッション作成
      #セッションに POST リクエストのパラメータを保存
      session[:after_login_to_post] = {
        controller_name: controller_name,
        action_name: action_name,
        params: { 
           title: params[:post][:title],
           content: params[:post][:content]
        }
      }
      # ログインページにリダイレクト
      redirect_to new_user_session_path 
    end
  end
end

セッションにコントローラー名, アクション名, パラメータを持たせます。

params:{
    hoge: params[:hoge]
}

↑ここの部分は各自変更してくださいね。
初心者でここがどうなってるのかわからない人はターミナルのログをみたりgem『Better Errors』を使って確認してみてください。

2. ログイン後のリダイレクト先を変更

ログイン後、セッションにPOST情報があれば、after_login_to_post_pathにリダイレクトさせます。


参考サイトではsessions_controllerに書いていましたが、全ページで素直に読み込んでくれるapplication_controllerに記述していきます。

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base

  # ログイン後、リダイレクト先メソッドafter_sign_in_path_forをオーバーライドする。
  def after_sign_in_path_for(resource)
    # POSTの場合、after_login_to_post_pathに遷移
    if session[:after_login_to_post]
      after_login_to_post_path
    # そうじゃない時はtopへリダイレクト
    else
      root_path
    end
  end
end


これはリダイレクトさせるpathを指定するメソッドで、pathだけ書いておけば元あるものを書き換えて指定したpathにリダイレクトしてくれるようになります。

頭に『redirect_to』はつけなくて大丈夫です。
むしろつけちゃダメです。
昔はよくここでつまづいていました。。

3. ログインしたらセッション情報を使ってログイン直前のページ情報を復元する

ログイン前に入力した情報をインスタンス変数に代入します。
この情報を使ってviewにアクセスした時の自動submitを行います。

# app/controllers/home_controller.rb
class HomeController < ApplicationController
  def after_login_to_post
      # セッションにafter_login_to_postがあれば、
     if session[:after_login_to_post]
       @back_controller = session[:after_login_to_post]['controller_name']
       @back_action = session[:after_login_to_post]['action_name']
       @params = session[:after_login_to_post]['params']
      # セッションをクリア
       session[:after_login_to_post] = nil
      # viewを呼び出し
       render :after_login_to_post, layout: false
     else
      # 直接アクセスが来たら、TOPに遷移する。
       redirect_to root_path
     end
  end
end

4. リダイレクト先ページ作成

白紙のページにリダイレクトしたらすぐにformをsubmitするviewを作成します。
参考サイトだとslimで書いていましたが、あまり得意ではないのでerbで書きました。

# app/views/home/after_login_to_post.html.erb
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>戻る</title>
</head>
<body onload="document.forms[0].submit()">
  <% if @back_controller && @back_action && @params %>
  <%= form_tag({ controller: @back_controller, action: @back_action }, { method: 'post' }) do %>
  <%= hidden_field_tag 'post[image]', @params['image'] %>
  <%= hidden_field_tag 'post[image_pos]', @params['image_pos'] %>
  <%= hidden_field_tag 'post[title]', @params['title'] %>
  <%= hidden_field_tag 'post[first_task]', @params['first_task'] %>
  <%= hidden_field_tag 'post[second_task]', @params['second_task'] %>
  <%= hidden_field_tag 'post[third_task]', @params['third_task'] %>
  <%= hidden_field_tag 'post[status]', @params['status'] %>
  <% end %>
  <% end %>
  <noscript>
    <%=%>
  <%= button_tag '戻る', class: 'btn_sub'%>
  </noscript>
</body>
</html>

どうやって自動でsubmitしてるの?!と思う方がいるかもしれませんので解説しておきますね。
以下の部分が自動subitをするポイントになっています。

<body onload="document.forms[0].submit()">

JavaScriptでformの一番最初のものを取得してsubmitしろ!と書いているだけですが、これで自動的にsubmitしてくれています。
便利だなぁ。

<%= form_tag({ controller: @back_controller, action: @back_action }, { method: 'post' }) do %>

formを生成するためにform_tagを利用します。
controllerとactionを指定して、methodをpostで送ってあげればOKです。

パラメータで渡したいデータはhidden_field_tagで渡してあげればcontrollerで受け取れます。

 <%= hidden_field_tag 'post[title]', @params['title'] %>
 <%= hidden_field_tag 'post[content]', @params['content'] %>

ちなみに、以下の形式でパラメータを受け取りたい場合は上記のコードのままで問題ありません。

post:{title:hoge, content:fuga}

以下のようにするとパラメータの受け取り方は変わるのでご注意を。

<%= hidden_field_tag 'title', @params['title'] %>
# ↑ これだと :title=> "hoge"
<%= hidden_field_tag 'post[title]', @params['title'] %>
# ↑ これだと post:{title: hoge}

5. 仕上げにroutes.rbを追加したら完成

# config/routes.rb
get 'after_login_to_post', to: 'home#after_login_to_post'


これで終わりです!

確かめてみると、ログイン前に入力したデータをきちんとセッションに保持してログイン後に自動で投稿をしてくれているのが確認できます。
一瞬白い画面が出ていきますが、割とすぐにリダイレクトするので気にならないかなと思います。

地味な努力がユーザーのためになりますねぇ・・・。