(Rails) How to create both sides of a friendship from one nested form insert in Rails?

41 Views Asked by At

Basically I have a User model, and a Friendship join table to make the Users friends with each other.

class User < ApplicationRecord
  has_many :friendships
  has_many :friends, through: :friendships, class_name: 'User'
class Friendship < ApplicationRecord
  belongs_to :user
  belongs_to :friend, class_name: 'User'

Simple, right? Now I have a collection check box for each user's edit page to decide who is their friend:

<%= f.collection_check_boxes( :friend_ids, @other_users, :id, :full_name) do |b| %>
  <%= b.label(class:"form-check-label") { b.check_box(class: 'form-check') + b.text } %>
<% end %>

When I check off John on Timmy's edit page, I would like for two join tables to be created, one linking the User (Timmy) to the Friend (John), and another linking the Friend to the User. At the moment, there is only one table being created:

Friendship Create (0.7ms)  INSERT INTO `friendships` (`user_id`, `friend_id`, `created_at`, `updated_at`) VALUES (48, 49, '2023-09-21 14:24:36', '2023-09-21 14:24:36')

I tried adding a callback to the friendship model

  after_create do |ship|
    friend_friendship = Friendship.create(user_id: ship.friend_id, friend_id: ship.user_id)
  end

But obviously that failed, creating an endless loop!

How do I also create the friend-to-user join without a fuss? Thanks

1

There are 1 best solutions below

3
Milind On

accept_nested_attributes is something that will do the trick. You also need to create a join model too.

Lets see an example, to undersand the basic -

#==== 3 model associations here===
class User < ActiveRecord::Base
  has_many :friends
  accepts_nested_attributes_for :friends
  has_many :friendships, through: :friends
end

class Friend < ActiveRecord::Base
  belongs_to :users
  belongs_to :Friendship
end

class Friendship < ActiveRecord::Base
  has_many :friends
  has_many :users, through: :lines
end


#===== View ====== 
<%= nested_form_for @user do |f| %>
...#user attributes
<%= f.fields_for :friends do |friend| %>
<%= friend.label :name %>
<%= friend.collection_select(:friendship_id, Friendship.all, :id, :name , {include_blank: 'Select friends'} ) %>

The association class Friend will join users and Friendship through the friendship_id and user_id: