Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

can model "belong_to" two other models and have a nested relationship?

Is it possible for a model to belong_to, two models and have a nested relationship?

i.e of what i want

class trainer
has_many :appointments
end

class appointment
belong_to :trainer, :customer
end

class customer
has_many :appointments
end

at the moment i have only the customer and appointment models which are nested e.g of what i have:

create method looks like this:

  def create
    @appointment = @customer.appointments.build(params[:appointment])

    respond_to do |format|
      if @appointment.save
        format.html { redirect_to([@customer, @appointment], :notice => 'Appointment was successfully created.') }
        format.xml  { render :xml => @appointment, :status => :created, :location => @appointment }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @appointment.errors, :status => :unprocessable_entity }
      end
    end
  end

in routes i have:

  map.resources :patients, :has_many => [ :appointments, :visits ]

is it possible to have 2 nested relationships for 1 model? what would i have to change my create method to, if appointment also belonged to trainer as well as customer?

thanks

like image 779
Mo. Avatar asked Aug 03 '10 23:08

Mo.


1 Answers

Assuming that you're using ActiveRecord: Having a model belong to more than one other model is possible of course (however you need to specify one belongs_to statement for each relation).

class Appointment < ActiveRecord::Base
  belongs_to :trainer
  belongs_to :customer
end

A belongs_to relation does not necessarily mean that the record actually has that other record related; it can also be nil. So you can have appointments that belong to a trainer but no customer and vice versa.

Actually you can even have neither a trainer nor a customer or both a trainer and a customer as well this way - if this violates your business logic, you might want to add a validation to prevent this.

Your existing controller create method should continue to work like it is, you just need to add the handling of trainer records. You can even use the same controller for handling appointment of trainers and customers by abstracting trainers and customers, e.g. into a person like this:

class AppointmentsController < ApplicationController

  def create
    @appointment = person.appointments.build(params[:appointment])
    # ...
  end

protected

  def person
    @person ||=
      if params[:trainer_id]
        Trainer.find(params[:trainer_id])
      elsif params[:customer_id]
        Customer.find(params[:customer_id])
      end
  end
end

This way, you can use the same AppointmentsController for both routes

# Use AppointmentsController for /trainers/123/appointments
# as well as for /customers/123/appointments
map.resources :trainers, :has_many => :appointments
map.resources :customers, :has_many => :appointments

Of course, this only makes sense if the logic and views behind trainer appointments and customer appointments are almost the same. If not, you can also use different controllers

# Use TrainerAppointmentsController for /trainers/123/appointments and
# CustomerAppointmentsController for /customers/123/appointments
map.resources :trainers do |trainer|
  trainer.resources :appointments, :controller => 'trainer_appointments'
end
map.resources :customers do |customer|
  customer.resources :appointments, :controller => 'customer_appointments'
end
like image 102
Zargony Avatar answered Oct 18 '22 19:10

Zargony