Zach Williams
Zach Williams

Reputation: 63

When using devise in rails, how do I add two different types of users that will use the site in two different ways?

I'm building a job board application. I'm new to programming and am teaching myself the rails framework.

I'm using Devise for authentication. I will have two different types of users; Job Seeker and Employer. The job seeker will create a profile and search for job postings and the employer will create a company profile and post job listings. In the future, the employer will also be able to search for employees based on qualifications, experience, education, etc. but for now I'm just building my MVP.

Upvotes: 2

Views: 2104

Answers (3)

Richard Peck
Richard Peck

Reputation: 76774

This type of functionality is tricky, namely because you have to put functionality before implementation (IE most people get hung up about Devise, whereas it might not feature at all)


You have two ways:

Devise is an authentication system (user logged in); you may be better using authorization (can user do x or y). Authorization is out of Devise's scope.

Whilst you could use multiple models (Devise), I think it creates too much unnecessary bloat for what you need.

Instead, I would use a very simple role system (using enum):

#app/models/user.rb
class User < ActiveRecord::Base
   enum role: [:job_seeker, :employer]

   has_one :profile
   before_create :build_profile

   has_many :applications
   has_many :listings, through: :applications
end

#app/models/application.rb
class Application < ActiveRecord::Base
    belongs_to :listing
    belongs_to :user
end

#app/models/listing.rb
class Listing < ActiveRecord::Base
   has_many :applications
   has_many :applicants, through: :applications, class_name: "User", foreign_key: :user_id
end

You'll need to add a role column (int) to your users table. You'll create the default role by using a default: [x] switch when creating your column:

def change
   add_column :users, :role, :integer, default: 0 #-> defaults to job seeker
end

--

You've described several factors which would lend themselves perfectly to this:

  • Job seeker will create a profile
  • Employer will create a profile and post listings

... all meaning your "flow" will remain similar for both user types. You'd just have to manage what each user can do with authorization.


Setup

#config/routes.rb
resource  :profile, controller: :users, only: [:show, :update] #-> url.com/profile
resources :listings, only: [:show] do
   post :apply, on: :member #-> url.com/listings/:id/apply
end
resources :companies, controller: :users, only: [:show]

#app/controllers/users_controller.rb
class UsersController < ApplicationController
   #show will automatically be loaded

   def update
      current_user.update profile_params
   end

   private

   def profile_params
      params.require(:user).permit(profile_attributes: [:name, :etc, :etc])
   end
end

#app/views/users/show.html.erb
<%= form_for current_user do |f| %>
   <%= f.fields_for :profile do |p| 
      <% if current_user.job_seeker? %>
         <%= f.text_field :name, placeholder: "Your name" %>
      <% elsif current_user.employer? %>
         <%= f.text_field :name, placeholder: "Company name" %>
      <% end %>
   <% end %>
   <%= f.submit %>
<% end %> 

You'd then be able to use the following to check whether a user can create listings, or just view:

#app/controllers/listings_controller.rb
class ListingsController < ApplicationController
   before_action :check_seeker, only: [:apply]
   before_action :check_employer, only: [:new, :create, :destroy]

   def new  #-> employers
      @listing = current_user.listings.new
   end

   def apply #-> job seekers
      @listing     = Listing.find params[:id]
      @application = current_user.applications.new
      @application.listing = @listing
      redirect_to @listing, notice: "Application successful!" if @application.save 
   end

   private

   def check_seeker
      redirect_to listings_path, notice: "Only Job Seekers Allowed" unless current_user.job_seeker?
   end

   def check_employer
      redirect_to root_url, notice: "Only Employers Allowed" unless current_user.employer?
   end
end

Hopefully this gives you the gist.


Devise

To get Devise working with your new column, you'll need to extend the Devise Sanitizer:

#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.for(:sign_up) << :role
  end
end

This will allow you to change the role field on signup:

#app/views/devise/registrations/new.html.erb
.....
  <%= f.select :role, User.roles %>

Upvotes: 5

Gokul
Gokul

Reputation: 3231

For roles you can use CanCanCan gem and RoleModel gem with devise which will be better for user with different roles.

CanCanCan

Defining abilities using cancancan

Role model gem

Example for user roles with above gems..

Upvotes: 1

errata
errata

Reputation: 26802

Devise is good for authentication but for role and access control you may want to look into Rolify: https://github.com/RolifyCommunity/rolify.

This will allow you to keep a single user model and control access to different features with Role queries, ie:

unless current_user.has_role?(:admin)
  redirect_to ...
else
  render ...
end 

Upvotes: 2

Related Questions