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

bryantDinh's avatar

How do query scopes work exactly?

So I know there are differences from 5.6 and 5.4 but after looking through the documentation I can't figure out why this works.

public function scopeFilter($query, $filter) {

    if ($month = $filter['month']) {

        $query->whereMonth('created_at', Carbon::parse($month)->month);

    }

   if ($year = $filter['year']) {

        $query->whereYear('created_at', $year);

    }

}

I'm not returning $query and I'm not passing $query by reference so how is this working? Is Laravel making $query be passed by reference under the hood?

Also, for whatever reason I have to pass the $filter array to the query scope like this

['month' => request('month'), 'year' => request('year')]

even though request([month, year]) also returns an array of the exact same thing. The documentation doesn't explain why either of this is happening.

0 likes
5 replies
bryantDinh's avatar

This is from episode 20 of the 5.4 from scratch laravel series.

Yorki's avatar

It's all thanks to magic methods. Method prefixed with scope always get query as first argument then the rest of expected input. Notice the difference in method names. In short filter method does call scopeFilter from inside so it can pass whatever it wants.

bryantDinh's avatar

@Yorki Ok, so after messing with this for a bit I'm still confused about how I can get away with not returning "$query" in scopeFilter.

I get that it is a query scope but I am confused on how it is returning the query. Just messing around I set $query to null and the returned it but it doesn't change anything.

Meaning that this works.

 public function scopeFilter($query, $filter)
    {
        
        if ($month = $filter['month']) {

            $query->whereMonth('created_at', Carbon::parse($month)->month);

        }


       if ($year = $filter['year']) {

            $query->whereYear('created_at', $year);

        }

        $query = null;

        return $query;

    }

But why and how? I've tested it out a couple of times and this works 100%. And by work I mean it is filtering the results to get the "Post" that I want.

36864's avatar

It shouldn't be returning anything. Query scopes aren't called directly, they are called by the query builder's magic methods.

When you define scopeFilter, you don't call Model::scopeFilter(), you call Model::filter(), which will internally call scopeFilter() with the current query and return itself, after the scope's done its thing.

Snapey's avatar
Snapey
Best Answer
Level 122

remember you are calling a method on the query object which then adds the where conditions, its not about what is returned, its about what you called on query whilst you had it

1 like

Please or to participate in this conversation.