最初に、私はグーグルとヤフーで集中的に検索しました、そして、私は私のようなトピックに関するいくつかの返事を見つけました、しかしそれらはすべて私が知る必要があることを本当にカバーしていません。
私のアプリにはいくつかのユーザーモデルがありますが、今のところそれは顧客、デザイナー、小売業者であり、まだまだあるようです。それらはすべて、許可されているかどうかに関係なく、テーブルとサイトのいくつかの領域に異なるデータが格納されています。そこで、devise + CanCanの方法を使用して、多形の関連付けを試してみることにしました。そのため、次のモデルを設定しました。
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_user_session_path
などではなく、new_*type*_session_path
のようなルートヘルパーが必要です。実際、私が違う必要があるのは登録プロセスだけです...
だから私はこれがISこの問題のために行く方法ですか?それともこれのためのより良い/より簡単な/より少なくカスタマイズする必要がある解決策があるかどうか?
前もって感謝します、
ロバート
さて、私はそれを実行し、次の解決策に到達しました。
デバイスを少しコスチューム化する必要がありましたが、それほど複雑ではありません。
ユーザーモデル
# 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
したがって、ユーザーモデルには単純な多態的な関連付けがあり、それが顧客であるかデザイナーであるかを定義します。
次にやらなければならなかったのは、アプリケーションの一部となるRails g devise:views
を使用してデバイスビューを生成することでした。登録をカスタマイズするだけでよいので、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-> _designer_fields.html
<div><%= f.label :label_name %><br />
<%= f.text_field :label_name %></div>
次に、登録時にカスタムコントローラーを使用するようにデバイスのルートを設定します
devise_for :users, :controllers => { :registrations => 'UserRegistrations' }
次に、カスタマイズされた登録プロセスを処理するコントローラーを生成し、Devise::RegistrationsController
のcreate
メソッドから元のソースコードをコピーして、自分のやり方で動作するように変更しました(ビューファイルを移動することを忘れないでください)適切なフォルダ、私の場合は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
パラメーターに従って作成する必要があるユーザータイプを決定することです。 URLの単純なGET-param。
例えば:/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
更新:
/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ハッシュに渡されます。
だから...それだけです。あちこちで少し調整するだけで、非常に一般的な方法で機能するようになりました。これは、共通のユーザーテーブルを共有する他の多くのユーザーモデルで簡単に拡張できます。
誰かがそれが役に立つと思うことを願っています。
受け入れられた答えにコメントする方法を見つけることができなかったので、ここに書くつもりです。
おそらくそれが古くなっているために、受け入れられた回答が述べているように正確に機能しないことがいくつかあります。
とにかく、私が自分で解決しなければならなかったことのいくつか:
render_with_scope
はもう存在しません。render :new
を使用するだけです。作成関数の最初の行も、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
の代わりに。変更しないと、いくつかの一括割り当てエラーが発生していました。
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
</ code>
私は上記の指示に従っていましたが、いくつかのギャップがあり、それを実装したときに指示が古くなっていることがわかりました。
ですから、一日中苦労した後、私のために働いたことをあなたと共有させてください-そしてうまくいけば、それはあなたに数時間の汗と涙を節約するでしょう
まず、RoRポリモーフィズムにあまり詳しくない場合は、このガイドを参照してください: http://astockwell.com/blog/2014/03/polymorphic-associations-in-Rails-4-devise/ それに従うと、デバイスとユーザーユーザーのモデルがインストールされ、作業を開始できるようになります。
その後、すべてのpartailsでビューを生成するための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
デバイスのフラッシュメッセージが機能し続けるためには、UserRegistraionsControlloerによってオーバーライドされたRegistraionsControlloerの代わりにconfig/locales/devise.en.yml
を更新する必要があります。必要なのは次の新しいセクションを追加することだけです。
user_registrations:
signed_up: 'Welcome! You have signed up successfully.'
それがあなたたちを数時間節約することを願っています。