I am trying to figure out how to structure this please help:
I need users to be able to sign up as either an employer or employee. The employer basically is the company and the employees are the people employed at that job. I want to know how to keep a record of when/if the employee was hired and terminated from the job. Would a has_many:through
assotiation work for this or better yet is there a gem that I am overlooking that could help me with this?

- 3,315
- 5
- 30
- 44

- 26,853
- 21
- 75
- 114
-
2Have you looked into STI (Single Table Inheritance)? – pepe Jun 26 '12 at 02:34
-
STI would mean duplicate fields in separate tables which may not be used – Igbanam Jul 09 '12 at 05:40
-
Yasky, what's up with the "may not be used?" Is this a homework question? It's fine if it is; most people will still give you advice on solving the problem, but you might as well put all of your cards on the table. Otherwise, you'll probably keep getting unusable answers. – Bob Gilmore Jul 09 '12 at 06:12
4 Answers
This is a classic case of the hierarchical object model, so you have to think about this problem in terms of the objects involved and their relationships to each other. Think about the way a business works in real life:
Employer -> Employees NOT Employer -> Manager etc. -> Employees
A good example of this system model is that of GitHub. On GitHub, users can belong to Organisations. They can also create them and administrate them, managing members etc. (hiring and firing in your case). Therefore a much better way of modelling this system is having it be User-centric rather than a split of two different classes of User.
As stated in a previous answer, Employers (or Businesses in this sense) should not be considered as users of the system as they won't be acting on the state of the program, users will. Therefore, I personally think STI is a little overkill for this system model.
So...
All people employed in a business are employees, but not all employees have the same level of authority (Managers will have more authority than Junior employees for example). Therefore you should model a Business
, which has many Employee
s. Those Employee
s will have varying levels of authority based on their position:
- can_hire?
- can_fire?
- etc.
You could also create a method that would tell you when this Employee
was hired/fired. These could return a date/time if they were hired/fired or nil
if not. nil
in this sense would obviously imply they haven't yet been hired/fired.
When it comes to managing the abilities of different users, their level of authority is defined by what they can and cannot do. You can of course apply preset roles to this situation with a simple method that checks the above methods. For example:
def manager?
self.can_hire? and self.can_fire?
end
You would then also define an ability that would allow the creation of a Business:
def can_create_business?
self.manager?
end
Note: you can use scopes to achieve this elegantly.
You can also sub-class a basic User model for your roles, or create a Role class that defines the above methods and acts on a given User model. You can then create individual roles and subsets of roles.
Arguably, there are some cases where allowing the creation of employers/businesses and employees as separate entities that can both act on the state of the program are useful. If the Business
can perform more than just simple administration functions then this might be worth it. In which case, I would still not consider the business as a User model. I would require a special user account be created along with the Business
which would have a role of Business Administrator
or something similar.
This model follows some of the most basic principles in programming and Computer Science as a whole, so I hope it sheds a little light on your problem. Of course there are a number of gems available that have this functionality built-in, namely Devise and CanCan/CanTango, although I would personally build this system myself.

- 808
- 6
- 13
I think I will combine STI with has many through relationship.
I will first start by making a single table inheritance of Employers and Employees.
class User < ActiveRecord::Base
...
end
class Employer < User
end
class Employee < User
end
Employer hires employees. One employer can have many employees and with employment come other related properties like date_of_hire
, designation
, status
, manager_id
, department
etc. Hence I would model this as a separate object and hence store this information in a separate table. Let's call that Employment
, shall we?
rails g model employment employer_id:integer, employee_id:integer, date_of_hire:date, designation:string, status:string, manager_id:integer
Let's establish relationships now.
class Employment < ActiveRecord::Base
belongs_to :employer # Foreign key options should not required because of User STI
belongs_to :employee
belongs_to :manager, :foreign_key => :manager_id, :class_name => "User"
end
class Employee < User
has_many :employments
has_many :employers
has_one :manager, :through => :employments, :foreign_key => :manager_id
end
class Employer < User
has_many :employments, :foreign_key => :employer_id
has_many :employees, :through => :employments
end
Based on business rules we can implement elegant scopes.
class Employee < User
...
scope :fired, :where => {:status => 'fired'}
scope :recently_joined, :where => {:date_of_hire => 3.months.ago...Date.today}
scope :developers, :where => {:designation => 'developer'}
...
end
and...
Employee.recently_joined
Employee.recently_joined.developers
Employee.fired
Please understand that this is obviously not a tested code and may have some glitches.
However!
I would highly encourage you to re-consider the need of modeling employers as users. From my personal experience, it turned out to be disaster in the future (May be I was inexperienced then but I won't really take that route again). I would really make separate models for Employer and Employee and establish relationships as above but with foreign_key and class_name attributes. The major reason is that for STI to work, in your domain, the employer and employee should have an "IS A" relationship with User. You might be tempted to think that it is an "IS A" relationship but also think whether it's the same "TYPE" of "IS A" relationship. In the application I worked on, it did not make sense and yet we settled with STI. The employers had completely different set of features, identity and treatment in the application from the employees. Although there was some data that was the same for both, it existed for different purposes and was used different. That is a reason enough (Single Responsibility) to model them separately. STI is a tricky tool that can solve a problem. But use it incorrectly and it will create more problems than what it would solve.

- 15,304
- 8
- 49
- 75
-
I've found that in almost all cases STI is a bit hacky and eventually gets pulled out and refactored away. – Kevin Bedell Jul 10 '12 at 00:38
-
We're using STI quite effectively to differentiate between different types of companies in our app. There are no Company rows which are not a subclass of Company, such as AwesomeCompany or NotSoGreatCompany. Each type of Company also has a related profile, such as AwesomeCompanyProfile, to store information unique to that type of company. It works pretty well for us. – Caleb Hearth Jul 11 '12 at 14:49
-
@CalebThompson The case you have mentioned sounds like a good example of when to use STIs. All the STI subclasses you have share the same kind of "IS A" relationship with company. And I guess I said the same in my answer above. But modeling employers (which are usually companies) and employees as Users doesn't seem right as an Employer does not share the same kind of "IS A" relationship as an Employee does with the User. However, it always depends on your business rules. But from my personal experience it's _almost_ always a mistake to do that. – Chirantan Jul 11 '12 at 15:48
-
Chirantan, I agree with you that Employee/Employer are likely sufficiently different that they should not use STI. I was directing my comment to @KevinBedell whose comment made it sound like there is _never_ a case for STI. – Caleb Hearth Jul 11 '12 at 15:52
-
I agree with you @CalebThompson, there are cases where it makes sense but they are very rare. I was happy with my decision to use STI only once out of the 5 times I have used it. It's not that it's totally useless. – Chirantan Jul 11 '12 at 15:55
-
@CalebThompson I'm glad you've found a case where it's useful. I agree that there can be times it's useful, but I've actually never seen it implemented successfully. I've seen it put in and then ripped out later more than once. But you're correct in that I should probably not have made such a sweeping statement. Thanks for sharing your success story. – Kevin Bedell Jul 11 '12 at 15:59
Just use has_many, :through. It will get the job done. Say you need to get all the active employees for a single company you can do
class Employers
has_many :hired_employees, :through => :employments,
:class_name => "Employees",
:source => :employee,
:conditions => ['employment.status= ?',"hired"]
end
Then you can do Employers.first.hired_employees
. You can add more and use different conditions so that you can get 'terminated', 'dismissed' etc.
Of course this assumes you have a third model called Employment where it belongs to Employee and Employer. Your Employee and Employment classes could look like this:
class Employee
has_many :employments
end
class Employment
belongs_to :employee
belongs_to :employer
end

- 497
- 3
- 11
From what you said, you may need the following model: User, Job, Assignment, Role
class User
... some devise declarations here ...
... some role here. rolify gem is my suggestion ...
scope :employee, :where => {user.has_role? :employee}
scope :employer, :where => {user.has_role? :employer}
has_many :jobs, :through => :assignments
has_many :assignments
end
class Job
belongs_to :user # employer
has_many :assignments
has_many :users, :through => :assignments # employee
end
class Assignment
belongs_to :job
belongs_to :user # employee
def fired?
... stuff to check if the employee is fired
end
def hired?
... stuff to check if the employee is hired
end
end
Pros:
- you can add more roles such as manager, admin ... easily. Using rolify gem or you can just create the role model for yourself
- the assignment class will stored anything you need to store such as fired, hired about the employee
- since the roles are clearly predefined, access control can be added easily using authorization gem such as cancan.
Cons:
- Using this model, use must check whether the current user you are working on is an employer or an employee before doing anything else. However, this can be easily be checked using roles
For simplicity, you can use rails_app_composer gem to generate the initial app with devise, cancan and rolify installed.
NOTE: this answer is based on many similar questions such as: Polymorphism or STI