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

jpinto.ptn's avatar

Query scopes and IDE inspections

Hello everyone,

I've been thinking of using query scopes on my projects, but so far there's one nasty side-effect to that change: the IDE (in my case PhpStorm)'s inspections flag it as an undefined method. I know that you can disable inspections for those statements, but 1) it reduces legibility, 2) it's extra work I'd rather avoid, and most importantly 3) may prevent actual errors from being properly identified.

I tried to find a way to code-hint that with PhpDoc comments, but I'm not an experienced programmer, so it's more likely than not that I just don't know how to do it.

Is there any way to fix this for example, properly code-hinting query scopes so that inspections still run? Thanks in advance!

0 likes
8 replies
jpinto.ptn's avatar

Thank you for your reply!

* @method static Illuminate\Database\Eloquent\Builder active()
* @method static Illuminate\Database\Eloquent\Builder popular()

gets you other chained methods, as long as they're not other local scopes; i.e., that takes care of Model::active() calls, but Model::active()->popular() triggers a warning on popular().

Is code-hinting that works for both static and chained (i.e., from a Builder instance) calls even possible? I suppose it would be possible for global scopes, but I really don't know what can be done about local ones.

jpinto.ptn's avatar

@keizah7 I used a different method: I created a custom Builder class for the scopes.

For example, I have students who attend different classes on different dates. I made a StudentBulder class like this:

namespace App\Builders;

use Illuminate\Database\Eloquent\Builder;

class StudentBuilder extends Builder
{

    protected function withCurrentClass(): self
    {
        return $this->withClass();
    }

    public function withClassAsOf($date, ?string $prefix = null): self
    {
        return $this->withClass($date, $prefix);
    }

private function withClass($date = null, ?string $prefix = null): self
    {
        // extremely complex query here
        return $this;
    }
}

Then, on my Student class, I overrode the query() method:

public function newModelQuery() : StudentBuilder
    {
        $eloquentBuilder = new StudentBuilder($this->newBaseQueryBuilder());

        return $eloquentBuilder->setModel($this);
    }

Finally, I added a PhpDoc line for the IDE to know that query() is returning the custom builder class:

* @method static StudentBuilder query()

I've been pretty happy with this setup. For example, I made it load students' current class on every query, unless when explicitly told not to. I don't have to do Student::query()->withCurrentClass()->find(1) , just doing Student::find(1) is enough, so the data I get from my queries is much more consistent. And, yes, as long as you start with Student::query() the IDE's autocomplete works perfectly.

2 likes
Rikaelus's avatar

A couple years later I find myself in this same boat. I'm point on building a Laravel application on top of an existing 300+ table database and I really wanted to enrichen autocompletion to help others acclimate but I'm thinking autocomplete on the static methods is as close as I can get.

Igor Kozhevnikov's avatar

Make an empty Eloquent builder class with method descriptions.

/**
 * @method static self active()
 * @method static self popular()
 */
class PostBuilder extends \Illuminate\Database\Eloquent\Builder
{
    //
}

Add a description of the "query()" method to the model class.

/**
 * @method static PostBuilder query()
 */
class Post extends \Illuminate\Database\Eloquent\Model {
    //
}

Use the query builder starting with the "query() " method.

class PostController extends \App\Http\Controllers\Controller
{
    public function index(): View
    {
        $posts = Post::query()->active()->popular()->get();
		return view('post::index', compact('posts'));
    }
}
3 likes
keizah7's avatar

@Igor Thanks. Using your way IDE doesn't show inspection, but scope is not visible in autocomplete

BTW, if you will not use query you can stick with

/**
 * @method static Illuminate\Database\Eloquent\Builder active()
 */
class Post extends Model
AccountDeleted's avatar

Boy did I stumble across a gold mine reading this topic. I took jpinto.ptn's advise, and built upon it a little. For anyone that finds this post in the future. Use the below code to gain autocompletion on queries in IDE (In my case PHPStorm). It works so well, I will probably stop using Laravel local scopes and just use these instead.

/**
 * You need the below for autocompletion:
 * @method static OrderBuilder query()
 */
class Order extends \Illuminate\Database\Eloquent\Model
{
	/**
	 * This overrides Laravel's default Eloquent Builder with our own
	 */
	public function newEloquentBuilder($query): OrderBuilder
	{
		return new OrderBuilder($query);
	}
}

class OrderBuilder extends \Illuminate\Database\Eloquent\Builder
{
    public function forCompany(Company $company): static
    {
        return $this->where('company_id', '=', $company->getId());
    }
}

# Run this somewhere
Order::query()->forCompany($company)->first();
2 likes

Please or to participate in this conversation.