【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'
これで終わりです!
確かめてみると、ログイン前に入力したデータをきちんとセッションに保持してログイン後に自動で投稿をしてくれているのが確認できます。
一瞬白い画面が出ていきますが、割とすぐにリダイレクトするので気にならないかなと思います。
地味な努力がユーザーのためになりますねぇ・・・。