I'm creating a Rails application where users can sign up by checking a box in a form where they are either a "person" or "organization". I'm struggling to find a way to implement this into Rails. Both of these user types would have the same authorization. I have no idea if I want to use a string or a boolean as a data type in my ActiveRecord database. Also, what would I need to put in my model (User.rb) and my controller in order to validate it and implement it respectively?
3 Answers
There are many ways to implement this; it depends on what your needs are. Ask yourself: "Do people and organizations share the same attributes?"
AR Enum
If they do, the only thing that differentiates the two is role (or whatever you want to call it), i.e., person or organization. For that scenario, Rails 4.1 provides AR enums. This is the simplest solution, it could go something like this:
class User < ActiveRecord::Base
enum role: [ :person, :organization ] # @user.role => 'person', @user.person? => true
end
Polymorphic Association
On the other hand, if people and organizations share only some attributes, you might consider using a polymorphic association (If people and organizations share no attributes—not even role—they should be two different models). The base model should contain the attributes that both people and organizations share. The person/organization models should contain attributes specific to that model.
# common attributes
class User < ActiveRecord::Base
belongs_to :profile, polymorphic: true
def self.roles
%w(person organization)
end
end
# person-specific attributes
class PersonProfile < ActiveRecord::Base
has_one :user, as: :profile, dependent: :destroy
end
# organization-specific attributes
class OrganizationProfile < ActiveRecord::Base
has_one :user, as: :profile, dependent: :destroy
end
For user signup, you can create users#new and users#create actions. In your user signup form (perhaps app/views/users/new.html.erb), you could use a select_tag to let the user specify their role. Then, use that to determine what kind of profile to attach to your user model. For example (users#create):
def create
@user = User.new(user_params)
if role = params[:role]
# return HTTP 400
head :bad_request and return unless User.roles.include?(role)
# Assign the User's profile
@user.profile = "#{role.capitalize}Profile".constantize.new
else
# enter your own logic here
end
@user.save ? redirect_to(@user) : render(:new)
end
The handling of sessions (user signin/signout), in my opinion, should be handled in a separate SessionsController.
4 Comments
PersonProfile and OrganizationProfile always belong to a User, so those relationships are not polymorphic. Also, I don't think you can use has_one in the way you're intending here -- there's no way for AR to know which table to look in to find the associated record.Add a new table to the database named user_types with fields role and id. And in users table you need to add user_type_id column. Then, in UserType model
class UserType < ActiveRecord::Base
has_many :users
end
And in User model you need to add
class User < ActiveRecord::Base
belongs_to :user_type
end
You can create UserType records in seeds, but make sure it runs everytime the database is reset, add this to seeds.rb
UserType.create!(role: "person")
UserType.create!(role: "organization")
Hope this makes sense!
Comments
If you have only two types of users (Person and Organization) as indicated in your question, you could just have a Person model, and add a bool field is_organization to it. For more details, See how devise, a popular authentication gem, handles this here (this approach is option 2 on the linked page, you can also check out option 1, which is to create an entire new model).