質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.50%

  • Ruby

    7897questions

    Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

  • Ruby on Rails

    7460questions

    Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

  • Devise

    252questions

    Deviseとは、Ruby-on-Railsの認証機能を追加するプラグインです。

devise gemでログインとサインアップに同一のフォームを作りたい

受付中

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 1,251

Taka624

score 32

ユーザ管理にdeviseを使用しています。
現在、ログインのためのフォームと、サインアップのためのフォームは別々のものになっています。
しかし、UX向上のため、同じ単一のフォームでログイン/サインアップどちらともできるようにしたいのですが、やり方がわかりません。

現在フォームのリクエスト先は、ログインフォームが

<%= form_for(:user, :url => session_path(:user)) do |f| %>
  <%= f.text_field :email, placeholder: "メールアドレスを入力" %>
  <%= f.password_field :password, placeholder: "パスワードを入力" %>
  <%= f.submit 'ログイン' %>
<% end %>

で、サインアップフォームが

<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
   <%= devise_error_messages! %>
   <%= f.email_field :email, placeholder: "メールアドレスを入力" %>
   <%= f.password_field :password, placeholder: "パスワードを入力(4文字以上)" %>
   <%= f.submit "サインアップ" %>
<% end %>

になっています。
これを、同一のフォームで両方の処理が可能にする方法はありますでしょうか。

どうぞよろしくお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

+1

想定は同じ画面に二つのフォームがある状況でしょうか。それとも,同じフォームファイルを共有して,それぞれのフォームを表示する方法を知りたいのでしょうか。前者は二つのフォームを同じフォームにまとめるだけで実現可能です。後者はフォームのコードをまとめると返って煩わしくなると思いますし可読性の面からお勧めしません。非表示のフォームにhidden属性を付けて,link clickなどでhiddenとshowの切替という方法などはできると思います。
自由度がそこそこある実装なので,これらの情報で解決方法が見えなけれ実装の方針をもう少し詳しく教えていただければと思います。

追記

返信していただいた内容の実装であればdeviseのRegistrationsControllerのcreateアクションをカスタマイズすればできると思います。

簡単にはsuperの前にfindメソッドでUserの存在判定を行い,存在すればsign_inメソッドを実行して,リダイレクトし,存在しなければsuperメソッド(継承元クラスの同じ名前のメソッドを呼び出す)を呼ぶだけで実装できると思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/04/19 21:49 編集

    ありがとうございます。
    理想としては、
    もし入力されたメールアドレスとパスワードがすでに存在するものであればログインとしてフォーム送信をし、存在しなければサインアップとしてフォーム送信をしたいと考えております。

    キャンセル

  • 2016/04/19 22:07

    なるほどですね。
    そういった実装を試したことがないのでコードを提示はできませんがdeviseのregistrationコントローラーのcreateアクションをオーバーライドして,paramsのユーザが存在したらsign_in,しなかったらuser createを継続するという方針で実装できる気がします。情報を追記しておきます。

    キャンセル

  • 2016/04/20 15:07

    追記ありがとうございます。
    いろいろトライしているのですが、
    「findメソッドでUserの存在判定を行い」
    という部分ができておりません。どのように書くことができそうでしょうか。
    たとえば、
    def create
    @user = User.find(email: params[:email])
    if @user.present?
    sign_in(resource_name, resource)
    else
    super
    end
    end

    フォームから送られてくるパラメータをどのようにどのように表現できて、どのように既存のレコードと照らし合わせるのか、理解ができておりません。

    もしできましたら具体的な例を提示していただけると助かりますが、どういったことをまず学習すべきなどといったアドバイスもいただけたら嬉しいです。お忙しいようでしたら簡単なヒントだけでもお願いします。

    キャンセル

  • 2016/04/20 21:45 編集

    ほぼその方針で、おっけーです。
    何が送られて来てるかわからない場合はbinding.pryで中身がどうなってるのかみると良いでしょう。
    githubでdeviseの該当のコントローラーの処理を見るといいでしょう。
    ただsuperメソッドにはparamsを引数に入れたほうがいいかもしれません。

    キャンセル

  • 2016/04/21 20:40

    今createメソッドが、

    def create
    if User.where(email: params[:email]).present?
    binding.pry
    sign_in(resource_name, resource)
    else
    build_resource(sign_up_params)

    resource.save
    yield resource if block_given?
    if resource.persisted?
    if resource.active_for_authentication?
    set_flash_message! :notice, :signed_up
    sign_up(resource_name, resource)
    respond_with resource, location: after_sign_up_path_for(resource)
    else
    set_flash_message! :notice, :"signed_up_but_#{resource.inactive_message}"
    expire_data_after_sign_in!
    respond_with resource, location: after_inactive_sign_up_path_for(resource)
    end
    else
    clean_up_passwords resource
    set_minimum_password_length
    respond_with resource
    end
    end

    こういう状態になっているのですが、
    存在するemailでフォーム送信しても
    binding.pryで止まらず、
    結局新規登録画面に戻ってしまい、


    ログを見ると、

    Processing by Users::RegistrationsController#create as HTML
    Parameters: {"authenticity_token"=>"WyRbexD6rKYPPcpDpQQu5y3XYhzo7h6mDTZIVs4BxZltFtXggmiSc0huvgVG70PH5UIdEnq+KXYjbuXOj2gVZg==", "user"=>{"email"=>"c@gmail.com", "password"=>"[FILTERED]"}, "commit"=>"\xE3\x81\xAF\xE3\x81\x98\xE3\x82\x81\xE3\x82\x8B"}
    User Load (0.4ms) SELECT `users`.* FROM `users` WHERE `users`.`email` IS NULL
    (0.9ms) BEGIN
    User Exists (2.9ms) SELECT 1 AS one FROM `users` WHERE `users`.`email` = BINARY 'c@gmail.com' LIMIT 1
    (0.2ms) ROLLBACK


    こうなっていて、すでに存在するためcreateはできないという感じの処理になってしまいます。登録画面に戻ってきます。

    superをあえて使わずに、createメソッドまるごとコピーしているのは、今後の実装でもこの部分をカスタマイズしようと思っているからです。

    いろいろ試しているのですが、例えば上記のコードですとどこに問題がありそうか、ご指摘お願いできませんでしょうか。

    キャンセル

  • 2016/04/21 21:36 編集

    わかりました。
    params[:user][:email]でアドレスを呼んでください。

    キャンセル

  • 2016/04/22 11:43

    ありがとうございます。
    以下のように書き換えて実行してみたところ、
    if User.where(params[:user][:email]).present?


    以下のエラーが出ました。
    Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '@gmail.com)' at line 1: SELECT `users`.* FROM `users` WHERE (c@gmail.com)

    のように出ました。使用しているmysqlのバージョンは5.6.27なのですが、
    このバージョンでは
    if User.where(params[:user][:email]).present?
    こちらの書き方が使えない文法でしたでしょうか?

    キャンセル

  • 2016/04/22 20:20

    email:が抜けてます。
    それでも動かなければidでfindでしてください。

    キャンセル

  • 2016/04/22 21:44

    それで行けました!!!!!
    本当に有難うございます!

    それによって、ユーザが存在する場合の分岐の方に進むことができました!
    しかし、ここで、

    NoMethodError in Users::RegistrationsController#create
    undefined method `to_key' for :user:Symbol

    def create
    if User.where(email: params[:user][:email]).present?
    sign_in(resource_name, resource)
    else
    build_resource(sign_up_params)

    こういうエラーがでてしまいます。一体何のエラーなのでしょうか。
    sign_in(resource_name, resource)
    の行がハイライトされているので、この部分がおかしいのかもしれません。

    引数の渡し方はこれだとおかしいでしょうか?
    フォームは
    <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
    こうなっています。

    sign_in(resource_name, resource)
    こちらの部分は、deviseのソースコード(https://github.com/plataformatec/devise/blob/master/app/controllers/devise/registrations_controller.rb)の106行目を参考にしているのですがうまく行かず、
    代わりに56行目部分の
    sign_in resource_name, resource, bypass: true
    これを使ってみたのですが、この行にて同じエラーが出ました。

    原因がわかりませんでしょうか。

    何度も何度も本当に有難うございます。

    キャンセル

  • 2016/04/22 22:34

    あーなるほど:userがないときはそれ以下のキーも参照できないのでparams.key? :userをいれてください。

    キャンセル

  • 2016/04/23 01:13 編集

    ありがとうございます!
    すみません、
    params.key?
    をどこに入れたらよいのでしょうか?

    今止まっているのは、ユーザが存在する場合の、(サインアップでなく)ログインをする処理で止まっていると思います。すでにサインアップを過去にしてある存在するユーザのemailとパスワードでフォーム送信している状況です。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.50%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Ruby

    7897questions

    Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

  • Ruby on Rails

    7460questions

    Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

  • Devise

    252questions

    Deviseとは、Ruby-on-Railsの認証機能を追加するプラグインです。