Reputation: 1185
I'm trying to find an elegant (standard) way to pass the parent of a polymorphic model on to the view. For example:
class Picture < ActiveRecord::Base
belongs_to :imageable, :polymorphic => true
end
class Employee < ActiveRecord::Base
has_many :pictures, :as => :imageable
end
class Product < ActiveRecord::Base
has_many :pictures, :as => :imageable
end
The following way (find_imageable
) works, but it seems "hackish".
class PictureController < ApplicationController
#/employees/:id/picture/new
#/products/:id/picture/new
def new
@picture = imageable.pictures.new
respond_with [imageable, @picture]
end
private
def imageable
@imageable ||= find_imageable
end
def find_imageable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
end
Is there a better way?
EDIT
I'm doing a new
action. The path takes the form of parent_model/:id/picture/new
and params include the parent id (employee_id
or product_id
).
Upvotes: 12
Views: 7435
Reputation: 3602
This is the way I did it for multiple nested resources, where the last param is the polymorphic model we are dealing with: (only slightly different from your own)
def find_noteable
@possibilities = []
params.each do |name, value|
if name =~ /(.+)_id$/
@possibilities.push $1.classify.constantize.find(value)
end
end
return @possibilities.last
end
Then in the view, something like this:
<% # Don't think this was needed: @possibilities << picture %>
<%= link_to polymorphic_path(@possibilities.map {|p| p}) do %>
The reason for returning the last of that array is to allow finding the child/poly records in question i.e. @employee.pictures or @product.pictures
Upvotes: 0
Reputation: 4016
I just ran into this same problem.
The way I 'sort of' solved it is defining a find_parent method in each model with polymorphic associations.
class Polymorphic1 < ActiveRecord::Base
belongs_to :parent1, :polymorphic => true
def find_parent
self.parent1
end
end
class Polymorphic2 < ActiveRecord::Base
belongs_to :parent2, :polymorphic => true
def find_parent
self.parent2
end
end
Unfortunately, I can not think of a better way. Hope this helps a bit for you.
Upvotes: 1
Reputation: 4382
I'm not sure exactly what you're trying to do but if you're trying to find the object that 'owns' the picture you should be able to use the imageable_type field to get the class name. You don't even need a helper method for this, just
def show
@picture = Picture.find(params[:id])
@parent = @picture.imagable
#=> so on and so forth
end
Update For an index action you could do
def index
@pictures = Picture.includes(:imagable).all
end
That will instantiate all 'imagables' for you.
Update II: The Wrath of Poly For your new method you could just pass the id to your constructor, but if you want to instantiate the parent you could get it from the url like
def parent
@parent ||= %w(employee product).find {|p| request.path.split('/').include? p }
end
def parent_class
parent.classify.constantize
end
def imageable
@imageable ||= parent_class.find(params["#{parent}_id"])
end
You could of course define a constant in your controller that contained the possible parents and use that instead of listing them in the method explicitly. Using the request path object feels a little more 'Rails-y' to me.
Upvotes: 7