【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'

これで終わりです!

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

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

by
関連記事