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

Bandab's avatar

Latest record from relationship in whereHas()

I'm currently working on the project of a stock management application (Laravel). I came to the point where anything I do doesn't work, so now I beg for help with it.

I have a table with products, of which some are in the relationship with the others. Everything happens in one table. If the product has a child, the child overwrites the parent. http://i.stack.imgur.com/F0Zso.png

Then, all the queries I run on them use the following logic:

  • If the item doesn't have any child, use it.
  • If the item has children, use the latest child (highest id)

Now I have the relationships created in model file:

public function childItems(){
    return $this->hasMany('\App\OrderItem','parent_id');
}

public function parentItem(){
    return $this->belongsTo('\App\OrderItem','parent_id');
}

public function latestChild(){
    return $this->hasOne('\App\OrderItem','parent_id')->orderBy('id','desc')->limit(1);
}

The problem with latestChild() relationship is, that when you run this query:

\App\OrderItem::find(7)->latestChild()->get();

It works fine and returns only one (latest)(id 6) record in relationship - to do it I had to add orderBy and limit to hasOne().

But when I want to use this relationship in scopes, so in whereHas() method, it doesn't work properly, as takes any of the children instead of the latest one.

public function scopeDue($query){
    return $query->where(function($q){
                            $q->has('childItems','==',0)->has('parentItem','==',0)->whereDate('due_date','=', Carbon::today()->toDateString())->whereNull('return_date');
                        })->orWhere(function($q2){
                            $q2->has('childItems')->has('parentItem','==',0)->whereHas('childItems',function($q3) use($q2){
                                $q3->whereDate('due_date','=', Carbon::today()->toDateString())->whereNull('return_date');
                            });
                        })->with('latestChild');

}

However, with() at the end returns the right record.

I think, the reason it works so is because my relationship latestChild() returns all the children (despite hasOne()) and when I use it in whereHas it ignores the filtering functions I applied.

I know it's a little bit complex from what I describe, but to explain it better I will use an example. Executing the following in tinker

\App\OrderItem::due()->get();

Should return only record id 2, as the number seven has children, where of course id 5 is due, but the latest child is id 6 which is not due.

I hope I've explained it enough to let you help me, as I'm already going crazy with it. If you have any ideas on how I could achieve what I need by changing existing one or changing the whole logic of it, please help!

Thanks

0 likes
12 replies
Bandab's avatar

Maybe there is a different method to do it or should I just completely change the logic way I want to do it? Any ideas?

Bandab's avatar

@Prez, Thank you very much for your reply! Your code looks much better but unfortunately doesn't seem to solve my issue. I searched the Laravel API and I couldn't find the latest() method. But it definitely exists as Laravel doesn't throw any errors and I've already seen it somewhere else :D I modified your code a little bit

public function scopeDue($query)
{
    return $query->where('return_date', null)
        ->whereDate('due_date','=', Carbon::today()->toDateString())
        ->has('parentItem', 0)
        ->has('childItems', 0)
        ->orWhereHas('childItems', function($q)
        {
            $q->latest()->whereDate('due_date','=', Carbon::today()->toDateString());
        });
}
    

It still takes any of the children instead of the last one. Of course, I changed the dates in DB for testing :)

Bandab's avatar

@Prez maybe there is a way to create a scope where it gets the highest id where('parent_id', 'x') and then whereDate()? I'm going crazy with this one. There must be a way to take the last one from relationship!

Bandab's avatar

@Prez All right, I have rebuilt my database and structures but have exactly the same problem. The only thing I need now is to get all the Items which are due or have children (they are now in a different table) which are due. The rest will not be a problem. It just still checks if the item has any due children instead of the latest one.

Bandab's avatar

@Prez There are two tables:

  1. Items
  2. ItemExtensions (each record has item_id to create relationship) - I separated them because I don't need to overwrite all details but only due_date.

I always want to get all items, which are due. It means that either they are due by itself or their latest child is due.

Zalo's avatar

I'm having the same problem... I think you can't filter by ONLY the last related record...

Please or to participate in this conversation.