How to Model a multi-participant conversation system - ruby-on-rails

I am new to Rails (and programming) and after going through a few tutorials, I am designing a messaging app to test my skills.
The situation I am modelling is where a user can message 2+ other users. Here's what I came up with
A conversation has many participants (users) and many messages
(message).
A user has many conversation and many messages.
A message belongs to users (sender + recipients) and belongs to a conversation.
The ActiveRecord model is then:
class User < ActiveRecord::Base
has_many :messages, :through :conversation
has_many :conversations # or is belongs_to :conversation
end
class Message < ActiveRecord::Base
belongs_to :user
belongs_to :conversation
end
class Conversation < ActiveRecord::Base
has_many :messages
belongs_to :user # or is it has_many :users
end
Or do I have to add a 4th interface inbox
class Inbox < ActiveRecord::Base
belongs_to :user
has_many :conversations
end
and I would change the user and conversation models to be
class User < ActiveRecord::Base
has_one :inbox
has_many :conversations, :through :inbox
has_many :messages, :through :conversation
end
class Conversation < ActiveRecord::Base
belongs_to :inbox
has_many :messages
belongs_to :users
end
The second option looks redundant.
So yeah, I am fuzzy about the relationship/association between a conversation and users. All input to enlighten me is greatly appreciated.

Your first way is better in my opinion, but it should look like this:
class User < ActiveRecord::Base
has_many :messages
has_many :user_conversations
has_many :conversations, through: :user_conversations
end
class Message < ActiveRecord::Base
belongs_to :user
belongs_to :conversation
end
class Conversation < ActiveRecord::Base
has_many :messages
has_many :user_conversations
has_many :users, through: :user_conversations
end
# join table between users and conversations
class UserConversation < ActiveRecord::Base
belongs_to :user
belongs_to :conversation
end
The reason is that a user can have many conversations, and a conversation can have many users. You need to create the join table UserConversation to accommodate this.
Also, the easiest way to find out which model should belong to the other model is to ask which model should have the foreign key of the other model. The one with the foreign keys belongs_to the other model.

Related

Association between 3 models

I would like a user be able to create a course(so it should belong to one user) and also be able to join another course that it haven't created by him.What is the proper associations between the course and the user ? I want to make the following model associations:
Class User < ActiveRecord::Base
has_many :courses
has_many :comments ,through: :courses
end
Class Course < ActiveRecord::Base
has_and_belongs_to_many :users #here i am not sure
has_many :comments
end
Class Comment < ActiveRecord::Base
belongs_to :courses
end
I think what you should be able to do something like:
Class User < ActiveRecord::Base
has_many :courses
has_many :course_users
has_many :subscribed_courses, through: :course_users, source: :course # I think you should be able to do foreign_key: :course_id, class_name: 'Course'
has_many :comments ,through: :courses
end
Class Course < ActiveRecord::Base
belongs_to :user
has_many :course_users
has_many :participants, through: :course_users, source: :user # I think you should be able to do foreign_key: :user_id, class_name: 'User'
has_many :comments
end
Class Comment < ActiveRecord::Base
belongs_to :courses
end
#course_users is a join table for courses and users
class CourseUser < ActiveRecord::Base
# inside here you could have several other connections e.g grade of a user in a course within this join model
belongs_to :user
belongs_to :course
end
If I'm understanding what you're saying - you need to have a third model - you can call it enrollment
For Course you would use belongs_to :user if each course is created as a user.
Your Enrollment model with have two HABTAM
Class Enrollment < ActiveRecord::Base
has_and_belongs_to_many :users
has_and_belongs_to_many :courses
end
(An Aside, if a course is going to be offered more than once, you'll have to add an additional model for each instance of the course and the enrollment will belong to that model, and not courses)

Rails activerecord deep eager loading

Here is my models:
class User < ActiveRecord::Base
has_many :products
has_many :comments
end
class Product < ActiveRecord::Base
belongs_to :user
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :product
end
I need to get comment records from current user products only
How do I do that? thanks
If we move the relationships to use a has_many: comments, through: products you can probably get what you're after:
class User < ActiveRecord::Base
has_many :products
has_many :comments, through: products
end
class Product < ActiveRecord::Base
belongs_to :user
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :product
end
Now you can do user.comments.
The rails docs are here, which say:
A has_many :through association is often used to set up a many-to-many
connection with another model. This association indicates that the
declaring model can be matched with zero or more instances of another
model by proceeding through a third model. For example, consider a
medical practice where patients make appointments to see physicians.

Active Record Associations: has_and_belongs_to_many, has_many :through or polymorphic association?

The Ruby on Rails app I am working on allows users to create and share agendas with other users.
In addition, we must be able to:
Display a list of agendas for each user, on his profile
Display a list of users associated with an agenda, on the agenda's page
When sharing an agenda with another user, define a role for this user, and display the role of this user on the list mentioned right above
I was going to go with a has_and_belongs_to_many association between the user and the agenda models, like that:
class User < ActiveRecord::Base
has_and_belongs_to_many :agendas
end
class Agenda < ActiveRecord::Base
has_and_belongs_to_many :users
end
But then I wondered whether this would let me get and display the #user.agenda.user.role list of roles on the given agenda page of a given user.
And I thought I should probably go with a has_many :through association instead, such as:
class User < ActiveRecord::Base
has_many :roles
has_many :agendas, through: :roles
end
class Role < ActiveRecord::Base
belongs_to :user
belongs_to :agenda
end
class Agenda < ActiveRecord::Base
has_many :roles
has_many :users, through: :roles
end
And although I was pretty comfortable about the idea of a user having several roles (one for each agenda), I am not sure about the idea of an agenda having several roles (one for each user?).
Finally, to add to the confusion, I read about the polymorphic association and thought it could also be a viable solution, if done this way for instance:
class Role < ActiveRecord::Base
belongs_to :definition, polymorphic: true
end
class User < ActiveRecord::Base
has_many :roles, as: :definition
end
class Agenda < ActiveRecord::Base
has_many :roles, as: :definition
end
Does any of the above solutions sound right for the situation?
UPDATE: Doing some research, I stumbled upon this article (from 2012) explaining that has_many :through was a "smarter" choice than has_and_belongs_to_many. In my case, I am still not sure about the fact that an agenda would have many roles.
UPDATE 2: As suggested in the comments by #engineersmnkyn, a way of solving this would be to go with two join tables. I tried to implement the following code:
class User < ActiveRecord::Base
has_many :agendas, through: :jointable
end
class Agenda < ActiveRecord::Base
end
class Role < ActiveRecord::Base
end
class Jointable < ActiveRecord::Base
belongs_to :user
belongs_to :agenda
has_many :agendaroles through :jointable2
end
class Jointable2 < ActiveRecord::Base
belongs_to :roles
belongs_to :useragenda
end
I am not sure about the syntax though. Am I on the right track? And how should I define the Agenda and the Role models?
UPDATE 3: What if I went with something like:
class User < ActiveRecord::Base
has_many :roles
has_many :agendas, through: :roles
end
class Role < ActiveRecord::Base
belongs_to :user
belongs_to :agenda
end
class Agenda < ActiveRecord::Base
has_many :roles
has_many :users, through: :roles
end
and then, in the migration file, go with something like:
class CreateRoles < ActiveRecord::Migration
def change
create_table :roles do |t|
t.belongs_to :user, index: true
t.belongs_to :agenda, index: true
t.string :privilege
t.timestamps
end
end
end
Would I be able to call #user.agenda.privilege to get the privilege ("role" of creator, editor or viewer) of a given user for a given agenda?
Conversely, would I be able to call #agenda.user.privilege ?
Okay I will preface by saying I have not tested this but I think one of these 2 choices should work well for you.
Also if these join tables will never need functionality besides a relationship then has_and_belongs_to_many would be fine and more concise.
Basic Rails rule of thumb:
If you need to work with the relationship model as its own entity, use has_many :through. Use has_and_belongs_to_many when working with legacy schemas or when you never work directly with the relationship itself.
First using your example (http://repl.it/tNS):
class User < ActiveRecord::Base
has_many :user_agendas
has_many :agendas, through: :user_agendas
has_many :user_agenda_roles, through: :user_agendas
has_many :roles, through: :user_agenda_roles
def agenda_roles(agenda)
roles.where(user_agenda_roles:{agenda:agenda})
end
end
class Agenda < ActiveRecord::Base
has_many :user_agendas
has_many :users, through: :user_agendas
has_many :user_agenda_roles, through: :user_agendas
has_many :roles, through: :user_agenda_roles
def user_roles(user)
roles.where(user_agenda_roles:{user: user})
end
end
class Role < ActiveRecord::Base
has_many :user_agenda_roles
end
class UserAgenda < ActiveRecord::Base
belongs_to :user
belongs_to :agenda
has_many :user_agenda_roles
has_many :roles, through: :user_agenda_roles
end
class UserAgendaRoles < ActiveRecord::Base
belongs_to :role
belongs_to :user_agenda
end
This uses a join table to hold the relationship of User <=> Agenda and then a table to join UserAgenda => Role.
The Second Option is to use a join table to hold the relationship of User <=> Agenda and another join table to handle the relationship of User <=> Agenda <=> Role. This option will take a bit more set up from a CRUD standpoint for things like validating if the user is a user for that Agenda but allows a little flexibility.
class User < ActiveRecord::Base
has_many :user_agendas
has_many :agendas, through: :user_agendas
has_many :user_agenda_roles
has_many :roles, through: :user_agenda_roles
def agenda_roles(agenda)
roles.where(user_agenda_roles:{agenda: agenda})
end
end
class Agenda < ActiveRecord::Base
has_many :user_agendas
has_many :users, through: :user_agendas
has_many :user_agenda_roles
has_many :roles, through: :user_agenda_roles
def user_roles(user)
roles.where(user_agenda_roles:{user: user})
end
end
class Role < ActiveRecord::Base
has_many :user_agenda_roles
end
class UserAgenda < ActiveRecord::Base
belongs_to :user
belongs_to :agenda
end
class UserAgendaRoles < ActiveRecord::Base
belongs_to :role
belongs_to :user
belongs_to :agenda
end
I know this is a long answer but I wanted to show you more than 1 way to solve the problem in this case. Hope it helps

Rails: How to store chat conversations

I am writing a rails application with conversations between clients and administrators. And i want the administrators respond to questions sent it by clients.
The administrators should be able to see the previous conversations with a reply box.
What is the best way to save these coversations:
Single table with marks of question and answer and view them as table entries?
Different tables and simply organise them by time?
class Administrator < ActiveRecord::Base
has_many :messages, as: :author
end
class Client < ActiveRecord::Base
has_many :messages, as: :author
end
class Message < ActiveRecord::Base
belongs_to :author, polymorphic: true
belongs_to :chat
end
class Chat < ActiveRecord::Base
has_many :messages
belongs_to :client
belongs_to :administrator
end
If you want to show messages of a particular chat you can do it this way:
#chat = Chat.find_by_client_id_and_administrator_id(client_id, administrator_id)
#messages = #chat.messages.order(:created_at)
If client and administrator belong to the same model, for instance User, then:
class User < ActiveRecord::Base
has_many :messages
end
class Message < ActiveRecord::Base
belongs_to :user
belongs_to :chat
end
class Chat < ActiveRecord::Base
has_many :messages
belongs_to :client, class_name: "User"
belongs_to :administrator, class_name: "User"
end

has_many inheritance

I have a model called company that has_many users then users belongs_to company.
class Company < ActiveRecord::Base
has_many :users
end
class User < ActiveRecord::Base
belongs_to :company
end
If something belongs to users will it also belong to company?
You have to use has_many :through association for this.
class Comment < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
belongs_to :company
has_many :comments
end
class Company < ActiveRecord::Base
has_many :users
has_many :comments, :through => :users
end
Now you can do the following:
c = Company.first
c.users # returns users
c.comments # returns all the comments made by all the users in the company

Resources