Creating Custom Friendship Associations Based Around an "Event" Model

53 Views Asked by At

I've been researching friendship models using roles, custom associations, etc. But I haven't been able to connect my project to the concepts in a clear way.

I want a "User" to be able to create an event I'm calling a "Gather". A User can also attend a Gather created by other Users. By attending a Gather, the "User" can also be a "Gatherer".

The list of Gatherers will technically be considered friends of the "creator". This is how far I've gotten:

Models: User Gather Gatherer (?)

User

class User < ApplicationRecord
    has_many :gathers_as_creator,
        foreign_key: :creator_id,
        class_name: :Gather
    
    has_many :gathers_as_gatherer, 
        foreign_key: :gatherer_id,
        class_name: :Gather
    

end

Gather

class Gather < ApplicationRecord

    belongs_to :creator, class_name: :User 
    belongs_to :gatherer, class_name: :User

end

My question is, do I need to a join table, such as Gatherer, to allow multiple attendees and then later pull a friend list for the user/creator ?

Gatherer

belongs_to :gather_attendee, class_name: "User" 
belongs_to :attended_gather, class_name: "Gather"

Here's what I think that schema would look like:

create_table "gatherers", force: :cascade do |t|
    t.bigint "attended_gather_id"
    t.bigint "gather_attendee_id"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["attended_gather_id"], name: "index_gatherers_on_attended_gather_id"
    t.index ["gather_attendee_id"], name: "index_gatherers_on_gather_attendee_id"
  end

Help, my head is spinning trying to understand the connections and how to proceed.

Previous planning:

Schema:

create_table "activities", force: :cascade do |t|
    t.string "a_type"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

  create_table "gatherers", force: :cascade do |t|
    t.bigint "attended_gather_id"
    t.bigint "gather_attendee_id"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["attended_gather_id"], name: "index_gatherers_on_attended_gather_id"
    t.index ["gather_attendee_id"], name: "index_gatherers_on_gather_attendee_id"
  end

  create_table "gathers", force: :cascade do |t|
    t.integer "creator_id"
    t.integer "activity_id"
    t.text "gather_point"
    t.boolean "active"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

  create_table "interest_gathers", force: :cascade do |t|
    t.string "gather_id"
    t.string "interest_id"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

  create_table "interests", force: :cascade do |t|
    t.string "i_type"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

  create_table "users", force: :cascade do |t|
    t.string "username"
    t.string "img"
    t.string "first_name"
    t.string "last_name"
    t.string "state"
    t.string "city"
    t.string "bio"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

  add_foreign_key "gatherers", "gathers", column: "attended_gather_id"
  add_foreign_key "gatherers", "users", column: "gather_attendee_id"
end
class User < ActiveRecord::Base 
  has_many :gatherers, foreign_key: gather_attendee_id
  has_many :attended_gathers, through: :gatherers
  has_many :created_gathers, foreign_key: :creator_id, class_name: "Gather"
 end
class Gather < ActiveRecord::Base 
  has_many :gatherers, foreign_key: :attended_gather_id 
  has_many :attendees, through: :gatherers, source: :gather_attendee 
  belongs_to :creator, class_name: "User" 
end

class Gatherer < ActiveRecord::Base 
  belongs_to :gather_attendee, class_name: "User" 
  belongs_to :attended_gather, class_name: "Gather" 

end

2

There are 2 best solutions below

0
On

The naming here is not great. When naming your models choose nouns as models represent the actual things in your buisness logic - choosing verbs/adverbs makes the names of your assocations very confusing.

class User < ApplicationRecord
  has_many :gatherings_as_creator, 
    class_name: 'Gathering',
    foreign_key: :creator_id
  has_many :attendences
  has_many :gatherings, 
    through: :attendences
end

# think of this kind of like a ticket to an event
# rails g model Attendence user:references gathering:references
class Attendence < ApplicationRecord
  belongs_to :user
  belongs_to :gathering
end

# this is the proper noun form of gather
class Gathering < ApplicationRecord
  belongs_to :creator, 
    class_name: 'User'
  has_many :attendences
  has_many :attendees,
    though: :attendences,
    class_name: 'User'
end

My question is, do I need to a join table, such as Gatherer, to allow multiple attendees and then later pull a friend list for the user/creator ?

Yes. You always need a join table to create many to many assocations. Gatherer is a pretty confusing name for it though as that's a person who gathers things.

If you want to get users attending Gatherings created by a given user you can do it through:

User.joins(attendences: :groups)
    .where(groups: { creator_id: user.id })
5
On

You're on the right track.

If I understand what you're looking for correctly, you want a Gather to have many Users and a User to have many Gathers (for the attending piece). So you need a join table like this (this is similar to your gatherers table, but is in a more conventional Rails style):

create_join_table :gathers, :users do |t|
  t.index [:gather_id, :user_id]
  t.index [:user_id, :gather_id]
end

And then you'd want your User model to be like this:

class User < ApplicationRecord
  has_many :gathers_as_creator, foreign_key: :creator_id, class_name: "Gather"
    
  has_and_belongs_to_many :gathers
end
class Gather < ApplicationRecord
  belongs_to :creator, class_name: "User"
  has_and_belongs_to_many :users
end

(You can change the name of that :users association if you really want, by specifying extra options -- I just like to keep to the Rails defaults as much as I can.)

That should be the bulk of what you need. If you want to pull all the friends of a creator for a specific gather, you would just do gather.users. If you want to pull all of the friends of a creator across all their gathers, that will be:

creator = User.find(1)
friends = User.joins(:gathers).where(gathers: { creator: creator }).all