Which model association for messages and many users - ruby-on-rails

Im new to Rails and actually im trying to write my own messaging application. So far i have many users with username, password, etc. And now im figuering out how i should best write the migration for the message model. I thought that a message needs:
Message
sender_id => integer
recipent_id => integer
created_at => time
updated_at => time
The first problem im facing is that of course sender_id is unique but what is with recipent_id, there are often messages that should go to several people!
Next problem is i dont know how i have to refer form the user model to the message model i mean normaly i would write:
User has_many :messages
Message belongs_to :user
To do this i would need a coulmn named user_id in the message model , but now i have two cloumns sender_id and reciepent_id!
I hope you can give my some hints! Thanks

Is this what you are looking for?
class User < ActiveRecord::Base
has_many :messages, :foreign_key => "sender_id" #this only gives the messages sent by the user.
end
class Message < ActiveRecord::Base
#sender_id
belongs_to :sender, :class => "User", :foreign_key => "sender_id"
has_many :recipients, :class => "MessageRecipient"
end
class MessageRecipient < ActiveRecord::Base
belongs_to :message
belongs_to :recipient, :class => "User", :foreign_key => "recipient_id"
end
If you want to get all the recipients for the message, you could do
message.recipients.collect(&:email)

Related

Rails Unknown attribute error, but attribute does exist

I'm getting the error unknown attribute id when I run
#user.payments.create(payment_params)
I know this means I need a user_id in my payments table. But I'm using polymorphic associations (perhaps incorrectly?) and my payments table has a payee_id and a payer_id (for each "type" of user). In the above method the #user instance is a payer.
Here are the payment and user models:
class Payment < ActiveRecord::Base
belongs_to :payee, :class_name => 'User', :foreign_key => 'payee_id'
belongs_to :payer, :class_name => 'User', :foreign_key => 'payer_id'
end
class User < ActiveRecord::Base
has_many :payments
end
And the create action in the payment controller:
def create
#user = User.find_or_create_by(user_params)
#payment = #user.payments.create(payment_params)
end
The polymorphic associations are confusing me. How can I correct this error?
This is not a polymorphic association. You have to define relations correctly to get the payments you need to get. From the code i understand that this is kind of loan application.
The user in the above model does not have a single type of payment. There are two types of payments, the ones user gets money for, lets say incoming_payments and the one which user has to give money outgoing_payments. The relation should be defined like
class Payment < ActiveRecord::Base
belongs_to :payee, :class_name => 'User', :foreign_key => 'payee_id'
belongs_to :payer, :class_name => 'User', :foreign_key => 'payer_id'
end
class User < ActiveRecord::Base
has_many :incoming_payments, :class_name => 'Payment', :foreign_key => 'payee_id'
has_many :outgoing_payments, :class_name => 'Payment', :foreign_key => 'payer_id'
end
So if the user you are querying payments for is a payer then you should call user.outgoing_payements and if the user is a lender then you should call user.incoming_payments

Rails - How to give user notification messages on certain events

Im having a problem in rails. Im actually solving it but I guess theres a easier way out there.
I got user/membership/group models and user/invitation/event models. Membership joins user and group. Invitation joins user and event.
The membership and invitation model are equal. Group and event do have some equal some different columns. The membership/invitation model both have a boolean column "accepted", meaning the user which is invited to a group/event has to accept this invitation before he is a member/participant.
Now if a user signs in all group and event invitations should appear in a list. In fact I want to add more notifications to the system later on and events aren't even included in mine yet.
My solution is to add a notification model which belongs to user. So every user has many notifications. Additionally, this model is polymorphic and belongs to membership AND invitation.
#user model
class User < ActiveRecord::Base
has_many :memberships, :dependent => :destroy
has_many :groups, :through => :memberships
has_many :invitations, :dependent => :destroy
has_many :events, :through => invitations
has_many :notifications
#membership model (equal to invitation model)
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :group
has_one :notifications, :as => :noticeable
#group model (equal to event model but participants for members and invitation for membership)
class Group < ActiveRecord::Base
has_many :memberships
has_many :users, :through => :memberships
has_many :members, :through => :memberships, :source => :user,
:conditions => ['memberships.accepted = ?', true]
#notification model
class Notification < ActiveRecord::Base
belongs_to :user
belongs_to :noticeable, :polymorphic => true
Ive added some data to the database and Im testing it in the console
myUser = User.find(6) # will be exchanged with current_user in the actual program
I will run through all notifications with each do... but for the start I test all further actions on one notification
myNotice = myUser.notifications.first
so whether the noticeable_type of myNotices is membership or invitation I will render it as group or event notification
in this case noticeable_type=membership
myGroup = Group.find(Membership.find(myNotice.noticeable_id).group_id)
--> Do you want to join the Group "myGroup.name"? Yes | No
On Yes: Membership.find(myNotice.noticeable_id).accepted = true
On No: Membership.find(myNotice.noticeable_id).destroy
And: myNotice.destroy
Thats the my idea.
Is this the way to solve the problem?
The "each do" which goes through all notifications will be in a view file. Which means "Group.find(Membership.find(myNotice.noticeable_id).group_id)" has to be in the view file or a partial aswell. Isn't that a bit ugly?
I think Ive used a lot of "find" which means many SQL queries. Isn't there a way to reduce them with any "Ruby on Rails"-magic?
Thank you :)
Needed something similar for my app, so updating the answer here in case someone finds it useful.
#user model
has_many :membership_notices, :through => :notifications, :source => :membership, :conditions => {"notifications.noticeable_type" => "membership"}
has_many :event_notices, :through => :notifications, :source => :event ,:conditions => {"notifications.noticeable_type" => "event"}
The notifications are now accessible as
user.membership_notices
user.event_notices
Finally, I found how to solve this! I bumped into this problem again and googled "reverse polymorphic". I found the following post:
https://gist.github.com/runemadsen/1242485
Polymorphic Associations reversed.
It's pretty easy to do polymorphic associations in Rails: A Picture
can belong to either a BlogPost or an Article. But what if you need
the relationship the other way around? A Picture, a Text and a Video
can belong to an Article, and that article can find all media by
calling #article.media
This example shows how to create an ArticleElement join model that handles the polymorphic relationship. To add fields that are common to all polymorphic models, add fields to the join model.
class Article < ActiveRecord::Base
has_many :article_elements
has_many :pictures, :through => :article_elements, :source => :element, :source_type => 'Picture'
has_many :videos, :through => :article_elements, :source => :element, :source_type => 'Video'
end
class Picture < ActiveRecord::Base
has_one :article_element, :as =>:element
has_one :article, :through => :article_elements
end
class Video < ActiveRecord::Base
has_one :article_element, :as =>:element
has_one :article, :through => :article_elements
end
class ArticleElement < ActiveRecord::Base
belongs_to :article
belongs_to :element, :polymorphic => true
end
t = Article.new
t.article_elements # []
p = Picture.new
t.article_elements.create(:element => p)
t.article_elements # [<ArticleElement id: 1, article_id: 1, element_id: 1, element_type: "Picture", created_at: "2011-09-26 18:26:45", updated_at: "2011-09-26 18:26:45">]
t.pictures # [#<Picture id: 1, created_at: "2011-09-26 18:26:45", updated_at: "2011-09-26 18:26:45">]

Write a migration with reference to a model twice

I have a message model (Message) and this models as a userTo and userFrom, so two references to User. How can i write the migration? My user model is User.
Thank you
Here's a complete answer to this issue, in case people visiting this question are having a hard time putting everything together (as I was when I first looked into this).
Some parts of the answer take place in your Migrations and some in your Models:
Migrations
class CreateMessages < ActiveRecord::Migration
create_table :messages do |t|
def up
t.references :sender
t.references :recipient
end
end
end
Here you are specifying that there are two columns in this table that will be referred to as :sender and :recipient and which hold references to another table. Rails will actually create columns called 'sender_id' and 'recipient_id' for you. In our case they will each reference rows in the Users table, but we specify that in the models, not in the migrations.
Models
class Message < ActiveRecord::Base
belongs_to :sender, :class_name => 'User'
belongs_to :recipient, :class_name => 'User'
end
Here you are creating a property on the Message model named :sender, then specifying that this property will be referencing an instance of the User class. Rails, seeing the "belongs_to", will look for a column in your database called "sender_id", which we defined above, and use that to store the foreign key. Then you're doing the exact same thing for the recipient.
This will allow you to access your Sender and Recipient, both instances of the User model, through an instance of the Message model, like this:
#message.sender.name
#message.recipient.email
Here is your User Model:
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
Here you are creating a property on the User Model named :sent_messages, specifying that this property is related to the Message Model, and that the foreign key on the Message model which relates to this property is called 'sender_id'. Then you are doing the same thing for received messages.
This allows you to get all of a users sent or received messages by doing something like this:
#user.sent_messages
#user.received_messages
Doing either of these will return an array of instances of the Message model.
In the migration, create two different columns for each kind of user. For example:
add_column :messages, :sender_id, :integer
add_column :messages, :receiver_id, :integer
Then in the model, that's where the logic to map each column to the User class happens:
belongs_to :sender, :class_name => 'User'
belongs_to :receiver, :class_name => 'User'
Of course, use your own words for sender and receiver, but Rails will automatically associate sender to the sender_id column (and the same logic for receiver)
You will then be able to interact with both user user.sender and user.receiver.

How do I do this query using metawhere and joins?

I have User.rb:
has_many :sent_messages, :class_name => "Message", :foreign_key => "sent_messageable_id"
Each Message.rb has an account_id and a :givereceive method:
belongs_to :account
I want to create a method for the Account.rb model so that I can show all the Users (uniquetly) who sent a message where the account_id is the same as that account and :givereceive = "Give"
I tried the following:
User.joins(:sent_messages).where(
{:sent_messages => [:account_id => self.account_id,
:givereceive => "Give"]})
But I get an error that there is no association with Messages.
Also, this wouldn't remove duplicate instances (for example, the same user created several messages with the same Account).
I am testing it in the console and have the metawhere gem.
Thanks.
Try changing joins(:messages) to joins(:sent_messages)
User.joins(:sent_messages).where(
{:sent_messages => [:account_id => self.account_id,
:givereceive => "Give"]})
Add these associations to your models:
class Message < ActiveRecord::Base
belongs_to :account
belongs_to :user, :foreign_key => "sent_messageable_id"
end
class Account < ActiveRecord::Base
has_many :give_messages, :class_name => "Message", :conditions => {:givereceive => "Give"}
has_many :users, :through => :give_messages
end
Now to get the users you are looking for just call #account.users.

Two foreign keys with ActiveRecord? [rails]

I have a User class with reference to a Message class. The message class has a user_id (which is the sender) and a receiver_id. So in the User class I have
has_many :messages
has_many :messages, :foreign_key => "receiver_id"
and then in the Message class I have
belongs_to :user
The first relationship -- via user_id -- goes perfectly well. I haven't the slightest idea what to put in the Message class for the second relationship. The messages table is built with both user_id and receiver_id, so the support is there.
Is this even possible?
Also, then I'd have no idea how to get to the messages RECEIVED by a user... or the User who received a message :)
[I know that I can work around this by having a sender table and a receiver table and a messages table and maybe a bunch of other tables (a conversations table!), but I'd like to do it like this, for the fun of it. This application will be used for learning only.]
Also important: where would the docs be for this? This is not very helpful.
In your User class:
has_many :messages
has_many :received_messages,
:foreign_key => "receiver_id", :class_name => "Message"
In your Message class:
belongs_to :user
belongs_to :receiver, :class_name => "User"
#user = User.first
#user.messages
#user.received_messages

Resources