Polymorphic Association with Multiple Associations on the Same Model

Rails Polymorphic Association with multiple associations on the same model

I have done that in my project.

The trick is that photos need a column that will be used in has_one condition to distinguish between primary and secondary photos. Pay attention to what happens in :conditions here.

has_one :photo, :as => 'attachable', 
:conditions => {:photo_type => 'primary_photo'}, :dependent => :destroy

has_one :secondary_photo, :class_name => 'Photo', :as => 'attachable',
:conditions => {:photo_type => 'secondary_photo'}, :dependent => :destroy

The beauty of this approach is that when you create photos using @post.build_photo, the photo_type will automatically be pre-populated with corresponding type, like 'primary_photo'. ActiveRecord is smart enough to do that.

Polymorphic Association with multiple associations on the same model

Yep. That's totally possible.

You might need to specify the class name for header_image, as it can't be inferred. Include :dependent => :destroy too, to ensure that the images are destroyed if the article is removed

class Article < ActiveRecord::Base
has_one :header_image, :as => :imageable, :class_name => 'Image', :dependent => :destroy
has_many :images, :as => :imageable, :dependent => :destroy
end

then on the other end...

class Image < ActiveRecord::Base
belongs_to :imageable, :polymorphic => true
end

Rails Polymorphic Association with multiple associations on the same model bug

image_type has a specific function in this polymorphic association... it identifies the associated model (just as image_id identifies the id).

You should not be changing image_type as that breaks the association.

Make a new boolean column in the picture model, say cover_picture, and you can do...

has_one :cover_picture, -> {where cover_picture: true} ...

The advantage of this is your cover picture is also included in your pictures association, but if you want that picture excluded from the has_many then you can apply a where clause to that as well...

has_many :pictures, -> {where.not cover_picture: true} ...

Rails two polymorphic associations in one model

Of course class_name: "Teamable" does not work as the whole point of a polymorphic association is that the class that the class (and more importantly target table) of the association is dynamic. Its not needed either.

A polymorphic association uses a separate association_name_type string column which contains the class name which its associated with.

Given the following data:

| id | team_one_id | team_one_type # ...
----------------------------------
1 | 1 | "HighSchoolTeam"
2 | 2 | "ClubTeam"
3 | 3 | "HighSchoolTeam"

When you do Game.find(1).team_one Rails knows to use the HighSchoolTeam class and join the high_school_teams table. But it needs to pull the rows out before it can make the connection and of course your database knows nothing about the relation and won't maintain referential integrity.

So all you need is:

class Game < ApplicationRecord
belongs_to :team_one, polymorphic: true
belongs_to :team_two, polymorphic: true
end

And make sure you have team_one_type and team_two_type string columns on the games table.

Rails Polymorphic Association with multiple models

There's no limit. A polymorphic association is an open interface that any other model can plug into. In your example, maybe you have a Contact model, which belongs_to :contactable, polymorphic: true. The contacts table will need two indexed columns: contactable_id:integer, and contactable_type:string. Any other model can be contactable, as long as it has_one :contact, as: :contactable.

As far as if it's a good idea, I'd say if you think you will need to work with contacts as a separate entity from the contactable models, then this is a good solution. However, if you won't need to deal directly with contacts then it might be overcomplicating when you could just add email and phone fields to these models.

Multiple polymorphic association on a same model in rails

by adding the condition in the relation, it is letting you retrieve the images with imageable_sub_type = cover_image when you call place.cover_image. It will not set the attribute for you when you add the image. That has to be done separately when the image is added based on some input from the view like a checkbox tag.

Update: You can override the default association= method , sthing like below in Place model:

 def cover_image=(img)
# add the img to tthe associated pictures
self.pictures << img

# mark current img type as cover
img.update_attribute(:imageable_sub_type, "cover_image")

# mark all other images type as nil, this to avoid multiple cover images,
Picture.update_all( {:imageable_sub_type => nil}, {:id => (self.pictures-[img])} )

end

Rails polymorphic association and has_many for the same model

You'll want to use another name for that association.

has_many :comments, as: :commentable
has_many :commented_on, class_name: 'Comment' # you might also need foreign_key: 'from_user_id'.

See has_many's documentation online.

The foreign_key should not be needed in your case, but I'm pointing it out Just In Case™. Rails will guess "{class_lowercase}_id" by default (so user_id in a class named User).

Then you can access both associations (The class_name is explicitly needed because Rails can't find Comment from commented_on).

Polymorphic Relationship with Multiple Associations

I came up with the following idea to utilize the existing Polymorphic Relationship to handle multiple associations.

First, I changed the schema to this:

Employees
id

NonEmployees
id

Sponsors
id
sponsored_id
sponsored_type
sponsorable_id
sponsorable_type

So, I removed the sponsor_id field from each of the account type tables and added a second polymorphic relationship to the Sponsors table.

I updated the models as follows:

Models/Employee.php & Models/NonEmployee.php

public function sponsorable()
{
return $this->morphOne('Sponsor', 'sponsorable');
}

public function sponsors()
{
return $this->morphMany('Sponsor', 'sponsor');
}

Models/Sponsor.php

public function sponsor()
{
return $this->morphTo();
}

public function sponsorable()
{
return $this->morphTo();
}

Now, because Laravel doesn't support a morphManyThrough() relationship type, you'll notice I changed some of the names of the functions so that it would read a little cleaner when using the relationships since I have to go from one table through an intermediary table and then to a 3rd table to get the information I want.

With this structure, I can do the following:

$employee = Employee::find(2)->sponsorable->sponsor; // Gets employee's sponsored party
$sponsors = $employee->sponsors; // Gets individual that the employee is sponsoring.
foreach ($sponsors as $sponsor)
echo $sponsor->sponsorable->first_name;
$employee->sponsors()->save(new Sponsor()); // New sponsor
$non_employee->sponsors()->save(new Sponsor()); // New sponsor

I can also perform a reverse lookup:

Sponsor::find(1)->sponsor->first_name; // Sponsoring party
Sponsor::find(1)->sponsorable->first_name; // Party being sponsored

Rails: Polymorphic Relationship needed multiple times for one model. How to differentiate? Is there a better way?

What separates the address categories? You mention that you may have a billing address and a site address.

If for example the categories are determined by an attribute called 'category', then all you have to do is set a condition on the association declarations on the addressable:

class Account < ApplicationRecord
has_one :billing_address, -> { where category: 'billing' }, class_name: "Address", as: :addressable
has_one :site_address, -> { where category: 'site' }, class_name: "Address", as: :addressable
end


Related Topics



Leave a reply



Submit