Group Conversation by latest message Laravel - mysql

I can't seem to figure this out since mysql is not my strong suit. I've done a bit of research here but can't put two and two together, so I'm turning to the community for some help.
I am building a chat function inside my Laravel app where two users can talk to each other. I can't figure out how to build the inbox (group the conversations together) portion of things. I've figured out how to get last message grouped by sending it.
The following is inside my User Model:
public function lastMessages() {
return $this->hasMany('App\Message', 'recipient_id')
->select('*')
->join(DB::raw('(Select max(id) as id from messages group by author_id) LatestMessage'), function($join) {
$join->on('messages.id', '=', 'LatestMessage.id');
})
->orderBy('created_at', 'desc');
}
My messages table consists of the following: author_id, recipient_id, messaged, viewed.
What I need to do is group messages, whether incoming our outgoing and display the latest message as inbox entry and once I click on that messages, the rest of the conversation pops up on the page.
My current sql shown above only gives me 1 last message, not the whole conversation.
Thanks in advance for all the help.

Firstly, I think you should keep the relationship function separate from logical(query) function.
If I understood your question correctly , you should do something like this :
Message::where('recipient_id',$userid)->orWhere('author_id',$userid)->orderBy('created_at','desc')->get();
This will give you messages send or received by user. Please clarify your question with Author and Message models if this is not what you want.

Related

A specific way to pull out message list

I have a project where I am making a virtual phone and currently I need to have a list of people i've messaged. Basically the list you see before going into a private chat with X person.
My problem is currently I can't seem to find the solution to only get latest message I have with X person.
The issue with this is that if I've sent a message to X person and the X person have sent a message to me, i basically get 2 messages in the database when trying to pull out the information, where I only need to get the latest.
This is what I get when doing following and I ALMOST get what I want.
In JQUERY if I remove all where sender = my phone number i would run into a different issue.
https://i.imgur.com/JJzRl6M.png
I've tried following sql
SELECT msg_id, sender, receiver, sender_msg, receiver_read, MAX(msg_date)
FROM nl_phone_messages WHERE sender = '545-3169' OR receiver = '545-3169' GROUP BY sender, receiver
In this case you have to look at the numbers 114-5437 and 545-3169
msg_id 5 should not be in this list as it's not the latest message with this person where msg_id 24 is the latest with that specific person
https://i.imgur.com/WTzRmYv.png
I hope you understand my issue, ask if got any question - Thank you!

Not like on Array field

here is the problem I'm stuck with:
I'm using Rails 4 & MySQL
I've Message which have one sender and one recipient.
I want to be able to archive messages but if sender archive a message, the recipient still can access to the message until he archive it too.
I've serialize a field :
serialize :archived_by, Array
which contains which user archived the message
but I can't figure out how to query with it.
Message.where("archived_by like ?", [1].to_yaml)
works well, returning messages archived by User '1'
Message.where.not("archived_by like ?", [1].to_yaml)
won't work, returning nothing
I would like to find something else than using a classic many to many ...
Thanks!
UPDATE
I finally decided to add 2 fields, one for the sender & one for the recipient to know which archived the message. If someone has the proper way to do this, tell us :)
If you are using postgresql you could query the informations.
As in answer Searching serialized data, using active record described, the downsize of serializer at least under mysql is, that you byepass native db abstraction.

Group and sort results by max and count in joined table using QueryBuilder

I'm making an app for selling books but I'm struggling with this part of it.
When a user wants to buy a book he can send a message to the book's seller. I have a table called conversations and another table called messages, which holds the messages for a conversation.
I have a page where a user can see a list of conversations he's involved. Conversations with the latest messages should be shown first. I solved this already but I also want to show the number of unread messages next to each conversation (messages have a "read" boolean field). I haven't been able to add the count as part of my original query; I know I could make a query for each item in the conversations collection but that would be inefficient as hell.
This is my query so far with the QueryBuilder:
$conversations = DB::table('conversations')
->select([
'books.title as bookTitle', 'conversations.id',
DB::raw('max(messages.created_at) as lastMessage'),
// DB::raw('(select count(id) from messages')
])
->where('conversations.from_user', $this->user->id)
->orWhere('conversations.to_user', $this->user->id)
->join('books', 'conversations.book_id', '=', 'books.id')
->join('messages', 'conversations.id', '=', 'messages.conversation_id')
->groupBy('messages.conversation_id')
->orderBy('lastMessage', 'DESC')
->get();
If i add the part commented out to attempt to get a field with the number of unread messages for that conversation I get a SQL syntax error. I also been thinking if maybe is not possible to add the count field since it might be exclusive to my original query: I sort the conversations taking into account all messages (read or unread) and pick the latest message for each conversation; the count field I want to add should only count unread messages.
Any ideas? Hope I explained myself.
Thanks.
It seems that instead of:
DB::raw('(select count(id) from messages')
you should simply use:
DB::raw('(select count(id) from messages) as messageCount')
You were missing here )

Order all queries

I'm creating a new chat system with ColdFusion, but I'm having a lot of trouble with sorting out a little problem.
First of all I'll explain how the system works. When creating a new chat with someone, it creates a conversation row in the MySQL table conversations. After that, users can send each other messages. When sending a message, a row is inserted into the table conversations_messages with the timestamp, the user that sent the message, the message, and of course the id of the message. Sounds logical, but now comes the big problem: organizing all those messages.
Users can view their messages with others on a page, this is the code of that page:
// Check conversation ID based on page url
<cfif IsDefined('URL.chat') and URL.chat neq "">
// Load conversation based on page url
<cfquery name = "getconv" datasource = "#DSN#">
SELECT *
FROM `conversations`
</cfquery>
// Get all messages from this conversation
<cfoutput query="getconv">
<cfquery name = "getmessages" datasource = "#DSN#">
SELECT *
FROM `conversations_messages`
WHERE `conversation` = '#chat#'
ORDER BY `conversations_messages`.`id` DESC
</cfquery>
// Get messages sent by 'user_one'
<cfquery name = "my" datasource = "#DSN#">
SELECT *
FROM `conversations_messages`
WHERE `conversation` LIKE '#chat#'
AND `user` LIKE '#user.id#'
ORDER BY `conversations_messages`.`id` DESC
</cfquery>
// Get messages sent by 'user_two'
<cfquery name = "friend" datasource = "#DSN#">
SELECT *
FROM `conversations_messages`
WHERE `conversation` LIKE '#chat#'
AND `user` LIKE '#getconv.user_two#'
</cfquery>
<div class="content">
// Messages sent by 'user_one' aka. user that created conversation
You: <cfoutput><cfloop query="my">#my.message#<br></cfloop></cfoutput>
// Messages sent by 'user_two'
Friend: <cfoutput><cfloop query="friend">#friend.message#</cfloop></cfoutput>
</cfoutput>
</div>
</cfif>
This is how the results show up right now: (as you can see it's sorted by user)
I want it to be sorted by message ID, so it shows up like this:
I hope it's a bit clearer now!
Help would be appreciated!
(not sure why everyone is answering in comments rather than as "an answer"?)
The order that your results are returned from the DB are purely down to the order you tell them to be returned. So if you want them chronologically, then don't fetch them ordered by name; order them chronologically.
So don't get user1's messages, and then user2's messages, just get the messages for the entire conversation in conversation order. Then when you're outputting them, look at which user made which comment and do the "you" / "friend" stuff.
That said, I think the approach you're taking is less than ideal. What you should perhaps be doing is fetching the messages incrementally, as they happen, rather than waiting to get all of them (if you see what I mean). So on each person's UI get all the messages since the last displayed message, and display 'em (in chronological order), then wait for some polling interval, and then fetch the next lot of messages that have occurred since the last time (etc).
If you're using ColdFusion 10 all this stuff has already been done for you via the Web Sockets technology CF has. So that would be the way to go if that's an option for you.
(as Adam suggested here is an answer)
It seems to me that your query getmessages has what you are after. It looks to be all messages from this conversation = chat ordered by conversations_messages.id.
You really shouldn't query for * either, specify the needed columns only. You will get better performance that way.
Also, always use <cfqueryparam> tags in your cfquery blocks.
Also, you cannot nest <cfoutput> tags without a group attribute. Your code is already in a <cfoutput> so no need for another.
If you are on ColdFusion 10 I would definitely give #Adam's answer a look.

CakePHP Displaying Field Names via Two Associations

I apologize for the confusing title, I was a little stumped as to how to word my question.
I am new to CakePHP, but am following along through the cookbook/tutorials nicely, however I have come up against something which I cannot find an answer to.
My structure is as follows:
'Invoices' hasMany 'InvoiceHistory'
'InvoiceHistory' belongsTo 'InvoiceHistoryDeliveryStatus'
Whereby, an invoice can have multiple invoice histories, and each history contains a delivery status id, which links to a name.
On the Invoice view (index.ctp) I am displaying a list of all invoices but wish to display the Most Recent Delivery Status Name (InvoiceHistory contains a date field so it can be sorted) - thereby displaying the 'current Delivery Status'.
When I do:
$this->set('invoices', $this->Invoice->find('all'));
It does not go deep enough in what it returns to provide me with Delivery Status Names, nor have I deduced a way of only returning the most recent Invoice History within my result. I know how to do this manually with a MYSQL query but I figured that is probably just plain wrong.
What is the correct way of going about this while following CakePHP conventions?
Use Containable
$this->Invoice->Behaviors->attach('Containable');
$this->set('invoices', $this->Invoice->find('all', array(
'contain' => array(
'InvoiceHistory' => array(
'InvoiceHistoryDeliveryStatus'
)
)
));
From what I can tell, I think you should check out the Containable behavior.