First, I've searched intensely with Google and Yahoo and I've found several replies on topics like mine, but they all don't really cover what I need to know.
I've got several user models in my app, for now it's Customers, Designers, Retailers and it seems there are yet more to come. They all have different data stored in their tables and several areas on the site they're allowed to or not. So I figured to go the devise+CanCan way and to try my luck with polymorphic associations, so I got the following models setup:
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
For the registration I've got customized views for each different User type and my routes are setup like this:
devise_for :customers, :class_name => 'User'
devise_for :designers, :class_name => 'User'
devise_for :retailers, :class_name => 'User'
For now the registrations controller is left as standard (which is "devise/registrations"), but I figured, since I got different data to store in different models I'd have to customize this behaviour as well!?
But with this setup I got helpers like customer_signed_in?
and designer_signed_in?
, but what I'd really need is a general helper like user_signed_in?
for the areas on the site that are accessible to all users, no matter which user type.
I'd also like a routes helper like new_user_session_path
instead of the several new_*type*_session_path
and so on. In fact all I need to be different is the registration process...
So I was wondering IF THIS IS THE WAY TO GO for this problem??? Or is there a better/easier/less must-customize solution for this???
Thanks in advance,
Robert
I didn't manage to find any way of commenting for the accepted answer, so I'm just gonna write here.
There are a couple of things that don't work exactly as the accepted answer states, probably because it is out of date.
Anyway, some of the things that I had to work out myself:
render_with_scope
doesn't exist any more, just userender :new
The first line in the create function, again in the UserRegistrationsController doesn't work as stated. Just try using
instead of simply
build_resource
. Some mass-assignment error was coming up when unchanged.class ApplicationController < ActionController::Base protect_from_forgery
I was following the above instructions and found out some gaps and that instructions were just out of date when I was implementing it.
So after struggling with it the whole day, let me share with you what worked for me - and hopefully it will save you few hours of sweat and tears
First of all, if you are not that familiar with RoR polymorphism, please go over this guide: http://astockwell.com/blog/2014/03/polymorphic-associations-in-rails-4-devise/ After following it you will have devise and user users models installed and you will be able to start working.
After that please follow Vapire's great tutorial for generating the views with all the partails.
What I found most frustrating was that dut to using the latest version of Devise (3.5.1), RegistrationController refused to work. Here is the code that will make it work again:
and also add these overrides so that the redirections will work fine:
In order that devise flash messages will keep working you'll need to update
config/locales/devise.en.yml
instead of the overridden RegistraionsControlloer by UserRegistraionsControlloer all you'll need to do is add this new section:Hope that will save you guys few hours.
Okay, so I worked it through and came to the following solution.
I needed to costumize devise a little bit, but it's not that complicated.
The User model
The Customer model
The Designer model
So the User model has a simple polymorphic association, defining if it's a Customer or a Designer.
The next thing I had to do was to generate the devise views with
rails g devise:views
to be part of my application. Since I only needed the registration to be customized I kept theapp/views/devise/registrations
folder only and removed the rest.Then I customized the registrations view for new registrations, which can be found in
app/views/devise/registrations/new.html.erb
after you generated them.For each User type I created a separate partial with the custom fields for that specific User type, i.e. Designer -->
_designer_fields.html
Then I setup the routes for devise to use the custom controller on registrations
Then I generated a controller to handle the customized registration process, copied the original source code from the
create
method in theDevise::RegistrationsController
and modified it to work my way (don't forget to move your view files to the appropriate folder, in my caseapp/views/user_registrations
What this all basically does is that the controller determines which user type must be created according to the
user_type
parameter that's delivered to the controller'screate
method by the hidden field in the view which uses the parameter by a simple GET-param in the URL.For example:
If you go to
/users/sign_up?user[user_type]=designer
you can create a Designer.If you go to
/users/sign_up?user[user_type]=customer
you can create a Customer.The
my_devise_error_messages!
method is a helper method which also handles validation errors in the associative model, based on the originaldevise_error_messages!
methodUPDATE:
To be able to support routes like
/designer/sign_up
and/customer/sign_up
you can do the following in your routes file:Any parameter that's not used in the routes syntax internally gets passed to the params hash. So
:user
gets passed to the params hash.So... that's it. With a little tweeking here and there I got it working in a quite general way, that's easily extensible with many other User models sharing a common User table.
Hope someone finds it useful.