4

I'm trying to achieve the following, relationships and object calls

A User can send many messages (user.sent_messages) A Message can have one Sender (message.sender)

A User can receive many messages (user.received_messages) A Message can have many receivers (message.receivers)

My schema looks like this:

create_table "activities", force: true do |t|
 t.integer  "sender_id"
 t.integer  "message_id"
 t.datetime "created_at"
 t.datetime "updated_at"
 t.integer  "receiver_id"
end

create_table "messages", force: true do |t|
 t.text     "body"
 t.datetime "created_at"
 t.datetime "updated_at"
 t.boolean  "read",       default: false
end

My Models look like this:

class User < ActiveRecord::Base
 has_many :activities, class_name: 'Activity', foreign_key: 'sender_id', dependent: :destroy
 has_many :sent_messages, through: :activities

 has_many :reverse_activities, class_name: 'Activity', foreign_key: 'receiver_id'
 has_many :received_messages, through: :reverse_activities
end

class Activity < ActiveRecord::Base
belongs_to :sent_messages, class_name: 'User'
belongs_to :received_messages, class_name: 'User'
belongs_to :message
end

class Message < ActiveRecord::Base
 has_many :activities, foreign_key: 'sender_id'
 has_one :sender, through: :activities, foreign_key: 'sender_id', class_name: 'User'

 has_many :reverse_activities, foreign_key: 'receiver_id', class_name: 'User'
 has_many :receivers, through: :reverse_activities, source: :receiver

end

The methods sent_messages & received_messages work, however they point straight back to the User table and return the details of that user, not the message.

I haven't yet tried to get the Message model working as the User model is incorrect.

Thanks!


Thanks to both suggestions i've got the following working

class User < ActiveRecord::Base

 has_many :activities, class_name: 'Activity', foreign_key: 'sender_id'
 has_many :sent_messages, through: :activities, foreign_key: 'message_id', class_name:     'Message', source: :sender

 has_many :reverse_activities, class_name: 'Activity', foreign_key: 'receiver_id'
 has_many :received_messages, through: :reverse_activities, foreign_key: 'message_id', class_name: 'Message', source: :receiver
end

class Message < ActiveRecord::Base
has_one :sent_activities, class_name: 'Activity', foreign_key: 'message_id'
has_one :sender, through: :sent_activities, foreign_key: 'sender_id', class_name: 'User'

has_many :receiver_activities, class_name: 'Activity', foreign_key: 'message_id'
has_many :receivers, through: :receiver_activities, foreign_key: 'receiver_id', class_name: 'User'

validates :body, presence: true
end

class Activity < ActiveRecord::Base
belongs_to :sender, class_name: 'User'
belongs_to :receiver, class_name: 'User'
belongs_to :receiver, class_name: 'Message'
belongs_to :sender, class_name: 'Message'
end

As a result the method's i desired are working.

Now just to get the create actions working!

2 Answers2

6

Too complicated

Why don't you try this:

#app/models/message.rb
Class Message < ActiveRecord::Base
    belongs_to :sender, class_name: "User", primary_key: "sender_id"
    belongs_to :recipient, class_name: "User", primary_key: "recipient_id"
end

#app/models/user.rb
Class User < ActiveRecord::Base
    has_many :sent_messages, class_name: "Message", foreign_key: "sender_id"
    has_many :received_messages, class_name: "Message", foreign_key: "recipient_id"
end

users
id | name | email | created_at | updated_at

messages
id | sender_id | recipient_id | title | body | created_at | updated_at

This will allow you to load the data like this:

@message.sender
@message.recipient 

@user.sent_messages
@user.received_messages

To save the data, you can use:

#app/controllers/messages_controller.rb
def new
    @message = Message.new
end

def create
    @message = Message.new(message_params)
end

private

def message_params
    params.require(:message).permit(:recipient_id, :title, :body).merge(sender_id: current_user.id)
end

#app/views/messages/new.html.erb (user has to be logged in)
<%= form_for @message do |f| %>
    <%= f.collection_select(:recipient_id, User.all, :id, :name) %>
    <%= f.text_field :title %>
    <%= f.text_area :body %>
    <%= f.submit %>
<% end %>
Richard Peck
  • 76,116
  • 9
  • 93
  • 147
  • Hi Rich, thanks for that it all looks great apart from that will limit me to either a) a message can only be sent to one person or b) having to duplicate the entire message contents as well to send to multiple people. Which is what i was trying to abstract out with my Activities join table. – benlaplanche Feb 20 '14 at 13:40
  • Ohh I was under the impression you only wanted single recipients / senders? I can refactor to include a join table if you want – Richard Peck Feb 20 '14 at 15:20
  • If possible that would be great! – benlaplanche Feb 20 '14 at 17:32
  • 1
    for rails 5 progress i was get error i have solved it as belongs_to :sender, class_name: "User", foreign_key: "sender_id" belongs_to :recipient, class_name: "User", foreign_key: "recipient_id" – Shoaib May 11 '17 at 15:12
0

There are some mistakes in Model Message:

class Message < ActiveRecord::Base

Shouldn't the foreign_key be : "message_id"?

 has_many :activities, foreign_key: 'sender_id'

same problem in foreign_key: 'sender_id', and other likely thing in following

 has_one :sender, through: :activities, foreign_key: 'sender_id', class_name: 'User'

 has_many :reverse_activities, foreign_key: 'receiver_id', class_name: 'User'
 has_many :receivers, through: :reverse_activities, source: :receiver

end

You looks like use message's id to join other table's sender id and receive id.

class Activity < ActiveRecord::Base

The class name is ‘User', so sent_messages, received_messages return user information

belongs_to :sent_messages, class_name: 'User'
belongs_to :received_messages, class_name: 'User'
belongs_to :message
end

Hope it help.

lalameat
  • 754
  • 3
  • 10