Nav
Nav

Reputation: 1185

Rails 3: Find parent of polymorphic model in controller?

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".

#PictureController (updated to include full listing)

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

Answers (3)

Chris Edwards
Chris Edwards

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

rickypai
rickypai

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

dogenpunk
dogenpunk

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

Related Questions