Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

fdusautoir's avatar

Query scope

Hi Everybody.

I have two tables : conversations and conversations_messages.

In my conversation model, I set three functions :


public function messages() 
{
    return $this->hasMany('CeProtelco\ConversationMessage', 'conversation_id');
}

public function lastMessage()
{
    if($this->messages())
        return $this->messages()->orderBy('created_at', 'DESC')->first();
    return false;
}
    
public function isAnswered()
{   
    if($this->lastMessage())
        return ($this->lastMessage()->auth_id) ? $this->lastMessage() : false;
    return false;
}
    // Get the last message of the conversation
    $conversations->find(2)->lastMessage();

    // Know if the conversation is answered or not
    $conversations->find(4)->isAnswered()

That's working but I don't think that is the best way to proceed. If there's a better approch to do it, I would really appreciate to hear it.

But my question is for another thing. I would like to get conversations orderBy unanswered conversations.

How can I do it ?

0 likes
7 replies
toniperic's avatar

What are you trying to accomplish? I don't see where scopes could be applied here. If you want to fetch all the answered conversations then yeah, but from what I see you only want to check whether the conversation has been answered?

public function lastMessage()
{
    return $this->messages()->latest()->first();
}
    
public function isAnswered()
{
    $message = $this->lastMessage();

    return ($message->auth_id) ? $message : false;
}

Not quite sure how do you check whether the conversation has been answered?

1 like
fdusautoir's avatar

Is this is the user who put the last message, then the auth_id field is NULL therefore the conversation is set as unanswered. Is this is the admin who put the last message, we consider the conversation as answered because the last message has auth_id field which is filled with his id. Perhaps the word "answered" is not really suitable for this case but this is how it works.

Firstly, query scope could help me to get the number of conversations which are 'unanswered'.

// Retrieve the number of unanswered conversations
$conversations->unanswered()->count()
public function scopeUnanswered($query)
{   
    // Some stuff here      
}

And after, I would like to have the ability to get all conversations order by unanswered. For me, that will use the query scope function to achieve this but not sure.

toniperic's avatar

If you want to get only the unanswered conversations, you could do this (given that the auth_id field is null if the conversation has no answers)

public function scopeUnanswered($query)
{
    return $query->whereNull('auth_id');
}

If you want to get all conversations but only order them by unanswered first, you could do

public function scopeUnanswered($query)
{
    // by default it's ordering ascending, which is what you want. you can override it by passing 2nd parameter
    return $query->orderBy('auth_id');
}
fdusautoir's avatar

Just tried to do it with SQL and it seems to be a particular request. Then, the easiest way to achieve it, is to set an answered field in my conversations table and update it each time a message is posted.

JarekTkaczyk's avatar

@fdusautoir A few suggestions:

// this is bad:
public function lastMessage()
{
    // always TRUE, since messages() returns an object.
    // if you instead tried $this->messages, result would be the same, 
    // because again, it's an object (collection), that evaluates to true as well.
    if($this->messages())
        return $this->messages()->orderBy('created_at', 'DESC')->first();
    // so it never happens
    return false;
}

public function isAnswered()
{   
    if($this->lastMessage()) // first query
        return ($this->lastMessage()->auth_id) // again the same query
             ? $this->lastMessage() : false; // and once again the same query = 3 queries
    return false;
}

Now, if you're working with Eloquent, then I suggest Eloquent way of handling it:

// Conversation model

/**
 * Conversation has one latest Message.
 * as dynamic property returns \CeProtelco\ConversationMessage|null
 *
 * @return \Illuminate\Database\Eloquent\Relations\HasOne
 */
public function latestMessage()
{
    return $this->hasOne('CeProtelco\ConversationMessage', 'conversation_id') // FK might be ommited here
        ->latest();
}

/**
 * Determine whether the conversation has been answered (by the admin?).
 *
 * @return bool
 */
public function isAnswered()
{
    // check whether latestMessage is not NULL, then check its auth_id
    return ($this->latestMessage && $this->latestMessage->auth_id);
}

then simply:

$conversation->latestMessage; // Message model OR null if no related messages were found
$conversation->isAnswered(); // bool
1 like
fdusautoir's avatar

@JarekTkaczyk Really appreciate your message. This is more cleaner. Thanks. Have you got an idea about the fact to retrieve all conversations orderBy unanswered conversations ? I really don't know how to proceed to achieve this

Please or to participate in this conversation.