If there’s one thing I’ve learned about Rails 3 is if I’m having a hard time doing something, I’m probably doing it wrong. So I’m looking for help.
I have a few models which are related in a many to many relationship.
I am able to create the associations in the models without a problem. My problem lies in how to build the controllers to work with these relationships. I’ll try and give an example if you don’t see where I’m going with this.
For instance…
class Account < ActiveRecord::Base
has_many :locations
end
class Contact < ActiveRecord::Base
has_many :locations
end
class Location < ActiveRecord::Base
has_and_belongs_to_many :accounts
has_and_belongs_to_many :contacts
end
Let’s say I have the above models. This would be my resources…
resources :accounts do
resources :locations
end
resources :contacts do
resources :locations
end
resources :locations do
resources :accounts
resources :contacts
end
So just to keep this shortened a bit, let’s say I want a list of all locations for an account. The above routes would presumably be account/1/locations. Thus landing me at locations#index.
Hopefully I haven’t screwed up my example at this point but what’s the best way to build this action out as it really has multiple jobs… at a minimum the locations for an account, contact, and all locations.
So I end up with something like this…
class LocationController < ApplicationController
def index
if params[:account_id]
@locations = Location.find_all_by_account_id(params[:account_id])
elsif params[:contact_id]
@locations = Location.find_all_by_contact_id(params[:account_id])
else
@locations = Location.all
end
respond_with @locations
end
end
Update #1: To clarify, as I am getting some answers that suggest I change my Model relationships. I am working with a legacy system in which I can NOT change the relationships at this point. It is ultimately my goal to clean up the database and the relationships but for now I can not. So I need to find a solution that works with this configuration.
Your current approach is not DRY, and would give you a headache if say, for example, you wanted to impose additional scopes on the index; e.g. pagination, ordering, or searching by a field.
Consider an alternative: Note how your if/elsif/else conditional essentially is just finding the lookup scope to send
findto? Why not move that responsibility to a method that does just that? Thus simplifying your actions and removing redundant code.inherited_resources is a great gem for cleanly handling scenarios like this. Written by Jose Valim (of Rails). I believe it should work with HABTM, but honestly I’m not positive if I’ve ever tried it.
The above exmaple is essentially how inherited_resources works, but mostly it works its magic behind the scenes, and you only overwrite methods if you need to. If it works with HABTM (I think it should), you could write your current controller something like this: