2011-09-04 4 views
34

私はGoogleとYahooで徹底的に検索し、本当に私が知る必要があるものをカバーしていません。Ruby On Railsを使用した複数のユーザーモデルで、別々の登録ルートを持つが、一般的なログインルートを1つ作成することを念頭に置いて

私のアプリにはいくつかのユーザーモデルがありますが、今のところそれは顧客、デザイナー、小売業者です。まだまだ来ているようです。彼らはすべて、自分のテーブルに格納されているさまざまなデータと、許可されているかどうかのサイト上のいくつかの領域を持っています。だから私は工夫+カンカンの道を行くことにし、ポリモーフィック団体との私の運を試して考え出したので、私は、次のモデルのセットアップを得た:私は、それぞれ異なるユーザータイプ用にカスタマイズしたビューを持って、私のしまし登録については

class User < AR 
    belongs_to :loginable, :polymorphic => true 
end 

class Customer < AR 
    has_one :user, :as => :loginable 
end 

class Designer < AR 
    has_one :user, :as => :loginable 
end 

class Retailer < AR 
    has_one :user, :as => :loginable 
end 

ルートは、このように設定されている:(「/登録を工夫」されている)今登録コントローラが標準として残っているため

devise_for :customers, :class_name => 'User' 
devise_for :designers, :class_name => 'User' 
devise_for :retailers, :class_name => 'User' 

が、私は別のモデルに格納するさまざまなデータを得たので、私は、考え出し私が持っていると思いますこの動作をカスタマイズするためにも!

しかし、この設定で、私はcustomer_signed_in?designer_signed_in?などのヘルパーを得たが、私は本当に必要だろうと、すべてのユーザーがアクセスしているサイト上の領域についてuser_signed_in?、どんなにユーザタイプのような一般的なヘルパーです。

new_*type*_session_pathの代わりにnew_user_session_pathなどのルートヘルパーも好きです。実際に私が違う必要があるのは、登録プロセスです...

私はこの問題のために行く方法は?または、これは良い/簡単/少ない必要があります - カスタマイズソリューションですか?事前に

おかげで、
ロバート

+0

提案がありますか? – Vapire

答えて

35

わかりましたが、私はそれを通じ働いて、次のソリューションに来ました。
私はちょっと気を遣う必要がありましたが、それほど複雑ではありません。

Userモデル

# user.rb 
class User < ActiveRecord::Base 
    devise :database_authenticatable, :registerable, 
     :recoverable, :rememberable, :trackable, :validatable 

    attr_accessible :email, :password, :password_confirmation, :remember_me 

    belongs_to :rolable, :polymorphic => true 
end 

カスタマー・モデル

# customer.rb 
class Customer < ActiveRecord::Base 
    has_one :user, :as => :rolable 
end 

デザイナーモデル

# designer.rb 
class Designer < ActiveRecord::Base 
    has_one :user, :as => :rolable 
end 

ので、Userモデルは、単純なポリモーフィックな関連を持っている、それが顧客の場合は定義したり、デザイナー。
私がしなければならなかったことは、rails g devise:viewsと私のアプリケーションの一部であるdeviseビューを生成することでした。私は登録をカスタマイズする必要があったので、私はapp/views/devise/registrationsフォルダのみを残して残りを削除しました。

次に、新しい登録の登録ビューをカスタマイズしました。これは、登録後にapp/views/devise/registrations/new.html.erbにあります。

<h2>Sign up</h2> 

<% 
    # customized code begin 

    params[:user][:user_type] ||= 'customer' 

    if ["customer", "designer"].include? params[:user][:user_type].downcase 
    child_class_name = params[:user][:user_type].downcase.camelize 
    user_type = params[:user][:user_type].downcase 
    else 
    child_class_name = "Customer" 
    user_type = "customer" 
    end 

    resource.rolable = child_class_name.constantize.new if resource.rolable.nil? 

    # customized code end 
%> 

<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %> 
    <%= my_devise_error_messages! # customized code %> 

    <div><%= f.label :email %><br /> 
    <%= f.email_field :email %></div> 

    <div><%= f.label :password %><br /> 
    <%= f.password_field :password %></div> 

    <div><%= f.label :password_confirmation %><br /> 
    <%= f.password_field :password_confirmation %></div> 

    <% # customized code begin %> 
    <%= fields_for resource.rolable do |rf| %> 
    <% render :partial => "#{child_class_name.underscore}_fields", :locals => { :f => rf } %> 
    <% end %> 

    <%= hidden_field :user, :user_type, :value => user_type %> 
    <% # customized code end %> 

    <div><%= f.submit "Sign up" %></div> 
<% end %> 

<%= render :partial => "devise/shared/links" %> 

ユーザータイプごとに、その特定のユーザータイプのカスタムフィールドを別に作成しました。デザイナー - >_designer_fields.html

<div><%= f.label :label_name %><br /> 
<%= f.text_field :label_name %></div> 

はその後、私のセットアップの登録

devise_for :users, :controllers => { :registrations => 'UserRegistrations' } 

上のカスタムコントローラを使用するための工夫のためのルートは、それから私は、元のソースコードをコピーし、カスタマイズされた登録プロセスを処理するためのコントローラを生成しましたDevise::RegistrationsControllercreateメソッドから、私の場合には適切なフォルダにビューファイルを移動することを忘れないでください。app/views/user_registrations

class UserRegistrationsController < Devise::RegistrationsController 
    def create 
    build_resource 

    # customized code begin 

    # crate a new child instance depending on the given user type 
    child_class = params[:user][:user_type].camelize.constantize 
    resource.rolable = child_class.new(params[child_class.to_s.underscore.to_sym]) 

    # first check if child instance is valid 
    # cause if so and the parent instance is valid as well 
    # it's all being saved at once 
    valid = resource.valid? 
    valid = resource.rolable.valid? && valid 

    # customized code end 

    if valid && resource.save # customized code 
     if resource.active_for_authentication? 
     set_flash_message :notice, :signed_up if is_navigational_format? 
     sign_in(resource_name, resource) 
     respond_with resource, :location => redirect_location(resource_name, resource) 
     else 
     set_flash_message :notice, :inactive_signed_up, :reason => inactive_reason(resource) if is_navigational_format? 
     expire_session_data_after_sign_in! 
     respond_with resource, :location => after_inactive_sign_up_path_for(resource) 
     end 
    else 
     clean_up_passwords(resource) 
     respond_with_navigational(resource) { render_with_scope :new } 
    end 
    end 
end 

これは基本的にコントローラが、コントローラのcreateメソッドに渡されたuser_typeパラメータに従って作成する必要のあるユーザタイプを、単純なGETパラメータでパラメータを使用するビューの非表示フィールドで決定することですURL。

例:
/users/sign_up?user[user_type]=designerに行くと、デザイナーを作成できます。
/users/sign_up?user[user_type]=customerに行くと、顧客を作成できます。

my_devise_error_messages!方法は、元devise_error_messages!方法

module ApplicationHelper 
    def my_devise_error_messages! 
    return "" if resource.errors.empty? && resource.rolable.errors.empty? 

    messages = rolable_messages = "" 

    if !resource.errors.empty? 
     messages = resource.errors.full_messages.map { |msg| content_tag(:li, msg) }.join 
    end 

    if !resource.rolable.errors.empty? 
     rolable_messages = resource.rolable.errors.full_messages.map { |msg| content_tag(:li, msg) }.join 
    end 

    messages = messages + rolable_messages 
    sentence = I18n.t("errors.messages.not_saved", 
         :count => resource.errors.count + resource.rolable.errors.count, 
         :resource => resource.class.model_name.human.downcase) 

    html = <<-HTML 
    <div id="error_explanation"> 
    <h2>#{sentence}</h2> 
    <ul>#{messages}</ul> 
    </div> 
    HTML 

    html.html_safe 
    end 
end 

UPDATEに基づいて、また連想モデルで検証エラーを処理するヘルパーメソッドである:

/designer/sign_up/customer/sign_up等ルートをサポートすることができるようにしますルートファイルで次の操作を行うことができます:

# routes.rb 
match 'designer/sign_up' => 'user_registrations#new', :user => { :user_type => 'designer' } 
match 'customer/sign_up' => 'user_registrations#new', :user => { :user_type => 'customer' } 

ルート構文で内部的に使用されていないパラメータはすべてparamsハッシュに渡されます。したがって、:userがparamsハッシュに渡されます。

だから...それです。ちょっとピンキーで、私はこれをかなり一般的な方法で動作させました。これは、共通のUserテーブルを共有する他の多くのUserモデルでは容易に拡張できます。

誰かが役に立つと願っています。

+0

このソリューションを投稿していただきありがとうございます - 私はそれが非常にエレガントだと思います。私が嫌いなのは、パラメータを持つsign_up URLだけです。私はサイトのユーザーにパラメータを見てもらいたいとは思っていませんし、/ customers/sign_upや/ designers/sign_upのようなルートを好むでしょう。それは簡単に提供できるものですか?おそらく、適切なパラメータを設定して適切なビューをリダイレクトまたはレンダリングする、CustomersおよびDesignersコントローラでsign_upアクションを実行することによって、それとも、いくつかのレールの魔法を使って行うことができますか? – OzBandit

+5

デイヴィッド! 実際に私は同じことを疑問に思っていました。私はレールルートで少し演奏し、いくつかの "魔法"を試みました。 私が知りませんでした:任意のシンボル/値のペアをmatchメソッドに渡すことができます。予約されたシンボル名でない場合は、単にparamsハッシュのキーとして渡されます。したがって、 が「一致」designer/sign_up =>「user#new」の場合:type => 'Designer'' と 'match' customers/sign_up '=>' user#new '、:type => 'Customer'' ルートファイル内の は、paramsハッシュのtypeキーをユーザコントローラの新しいメソッドに渡します。 – Vapire

+0

Thanks Vapire、これはルートのドキュメントをより完全に読んだ後にやったのと同じことです。しかし、一般的な解決策をもう一度ありがとう - それは素晴らしい動作します! – OzBandit

6

私は受け入れられた回答のコメント方法を見つけることができなかったので、ここで書きます。

受け入れられた回答状態と正確には動作しない2つのことが考えられます。おそらく古くなっているからです。

とにかく、私は自分自身をうまくしなければならなかったことのいくつか:

  1. UserRegistrationsControllerについては、render_with_scopeはちょうどrender :new
  2. に作成する機能の最初の行を使用して、任意のより多く存在しませんUserRegistrationsControllerの中で再び述べたように動作しません。ただ、

    # Getting the user type that is send through a hidden field in the registration form. 
    user_type = params[:user][:user_type] 
    
    # Deleting the user_type from the params hash, won't work without this. 
    params[:user].delete(:user_type) 
    
    # Building the user, I assume. 
    build_resource 
    

の代わりに、単にbuild_resourceを使用してみてください。変更されていない場合、一部の大量割り当てエラーが発生していました。あなたは工夫のCURRENT_USERメソッド内のすべてのユーザー情報を持っているしたい場合は

  1. 、これらの変更を行います。

class ApplicationController < ActionController::Base protect_from_forgery

# Overriding the Devise current_user method 
    alias_method :devise_current_user, :current_user 
    def current_user 
    # It will now return either a Company or a Customer, instead of the plain User. 
    super.rolable 
    end 
end 

2

を私は上記の指示に従って、いくつかのギャップを見つけたとの命令があったとされました私はそれを実装していた時代遅れです。そうでない場合は、うまくいけば、それはあなたの汗の数時間の節約になりますし、すべての

  • まず涙 -

    だから、一日それで苦労した後、私のために働いていたものを私があなたと共有しましょうRoRの多型に精通している方は、このガイドを参照してください。 http://astockwell.com/blog/2014/03/polymorphic-associations-in-rails-4-devise/ それに続いて、あなたとユーザーのユーザーモデルがインストールされ、作業を開始することができます。

  • その後、Vapireの偉大なチュートリアルにしたがって、すべてのパーツセットでビューを生成してください。

  • 私が最も不満を感じたのは、最新のバージョンのDevise(3.5.1)を使用していたのですが、RegistrationControllerは動作を拒否しました。リダイレクトが正常に動作するように、これらのオーバーライドを追加するも

    def create 
    
        meta_type = params[:user][:meta_type] 
        meta_type_params = params[:user][meta_type] 
    
        params[:user].delete(:meta_type) 
        params[:user].delete(meta_type) 
    
        build_resource(sign_up_params) 
    
        child_class = meta_type.camelize.constantize 
        child_class.new(params[child_class.to_s.underscore.to_sym]) 
        resource.meta = child_class.new(meta_type_params) 
    
        # first check if child intance is valid 
        # cause if so and the parent instance is valid as well 
        # it's all being saved at once 
        valid = resource.valid? 
        valid = resource.meta.valid? && valid 
    
        # customized code end 
        if valid && resource.save # customized code 
        yield resource if block_given? 
        if resource.persisted? 
         if resource.active_for_authentication? 
         set_flash_message :notice, :signed_up if is_flashing_format? 
         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}" if is_flashing_format? 
         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 
    end 
    
  • と::ここ はそれが再び動作するようになるコードです

    protected 
    
        def after_sign_up_path_for(resource) 
        after_sign_in_path_for(resource) 
        end 
    
        def after_update_path_for(resource) 
        case resource 
        when :user, User 
         resource.meta? ? another_path : root_path 
        else 
         super 
        end 
        end 
    
  • フラッシュメッセージを工夫ために作業を続けますUserRegistraionsControlloによってオーバーライドされたRegistraionsControlloerの代わりにconfig/locales/devise.en.ymlを更新する必要があります。この新しいセクションを追加するだけです。

    user_registrations: 
        signed_up: 'Welcome! You have signed up successfully.' 
    

ほんの数時間節約できますか?

関連する問題