Xavier
Xavier

Reputation: 3513

ActiveRecord: many-to-many and one-to-many between same two models

Looking at guides.rubyonrails.com, I see the syntax for a self join:

class Employee < ActiveRecord::Base
  has_many :subordinates, :class_name => "Employee",
    :foreign_key => "manager_id"
  belongs_to :manager, :class_name => "Employee"
end

What I want to do is something more like this:

class Project < ActiveRecord::Base
  has_many :workers, :class_name => "Employee",
    :foreign_key => "manager_id"
  belongs_to :manager, :class_name => "Employee"
end

Or perhaps I can define the relationship polymorphically:

class Project < ActiveRecord::Base
  belongs_to :manager, :polymorphic => true
  has_many :employees
end

class Employee < ActiveRecord::Base
  has_many :projects, :as => :manager
end

So I guess the relationship I'm looking for is sort of like a HABTM but with a concrete distinction between having and belonging to. Any ideas?

Upvotes: 0

Views: 675

Answers (1)

tamersalama
tamersalama

Reputation: 4143

In layman terms - A Manager would have many Projects, each Project would have many Workers. Right? If so:

class Project < ActiveRecord::Base
  belongs_to :manager, :class_name => 'Employee', :foreign_key => 'manager_id'
  has_many :employees
end

class Employee < ActiveRecord::Base
  belongs_to :project
  has_many :managed_projects, :class_name => 'Project', foreign_key => 'manager_id'
  scope :managers, includes(:managed_projects).where('manager_id IS NOT NULL).order('NAME ASC')

end

This would allow Employees to be both Managers and Workers in Projects simultaneously (say for multi-tiered projects).

#The projects of the first manager (managers sorted by name) 
Employee.managers.first.project

# The workers (Employees) working on the project with id 1
Project.find(1).workers

# The manager (Employee) of the project with id 1
Project.find(1).manager

#Employees that are both Workers and Managers (left as an exercise)
Employee.workers_and_managers

Another way of attempting the relation is to use STI (single table inheritance), where a field names 'type' would determine if an Employee is either a Worker or a Manager (mutually exclusive)

class Employee < ActiveRecord::Base
  #table Employees includes 'type' field
end

class Worker < Employee
  belongs_to :project
end

class Manager < Employee
  has_many :projects
end

Now - you can do:

Manager.create(:name => 'John Doe') 
#you don't have to specify type - Rails will take care of it

#Find Projects managed by Employee with id 1
Manager.find(1).projects

#Find the project the Employee with id 2 is working on
Worker.find(2).project

#Find the manager of Project with id 1
Project.find(1).manager

#Find workers of Project with id 1
Project.find(1).worker

Upvotes: 2

Related Questions