barryvdh / laravel-ide-helper

IDE Helper for Laravel
MIT License
14.26k stars 1.17k forks source link

I believe I've been able to improve some of the intellisense for model queries, please implement this (but maybe expand it first) #1536

Open Evertt opened 8 months ago

Evertt commented 8 months ago

So I'm using VSCode with every extension I could install that gives my IDE the best possible understanding of how most of Laravel's magic methods work and what types they return. However, it's still not enough and luckily this package makes the autocompletion in my IDE better still.

However, I still found a scenario where my IDE doesn't understand what a certain method-call is referring to. Let me show you an example.

I have this model:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

/**
 * @mixin IdeHelperTrainingDate
 */
class TrainingDate extends Model
{
    public function scopeSearch($query, $search) {}
}

And then this in my _ide_helper_models.php file:

namespace App\Models{
/**
 * App\Models\TrainingDate
 * 
 * @method static \Illuminate\Database\Eloquent\Builder|TrainingDate query()
 * @method static \Illuminate\Database\Eloquent\Builder|TrainingDate search($search)
 * @mixin \Eloquent
 */
    class IdeHelperTrainingDate {}
}

Strictly speaking, I don't even need the line for search there, because one of my extensions is already smart enough to be able to link search() to scopeSearch(), but it's fine. So when I do this, things work completely fine:

$training_dates = TrainingDate::query()
    ->search("foo")
    ->get();

I can hover the search() call, and it will give me the desired type information and I can cmd + click on it and it will refer me directly to scopeSearch(). However, that breaks as soon as I do this:

$training_dates = TrainingDate::query()
    ->with("training")
    ->search("foo")
    ->get();

Because with() simply returns Builder and now neither my extensions nor your _ide_helper_models.php file is able to make my IDE understand that search() is still available. I thought maybe running php artisan ide-helper:eloquent and / or php artisan ide-helper:generate would fix that issue, but it didn't. However, I've been able to fix it myself! 😃 By adding the following lines at the top of _ide_helper_models.php:

namespace Illuminate\Database\Eloquent {
    /**
     * @template TModel of \Eloquent
     * @mixin TModel
     */
    class Builder {}
}

And then changing docblock of IdeHelperTrainingDate to the following:

namespace App\Models{
/**
 * App\Models\TrainingDate
 * 
 * @method static \Illuminate\Database\Eloquent\Builder<TrainingDate> query()
 * @method static \Illuminate\Database\Eloquent\Builder<TrainingDate> search($search)
 * @mixin \Eloquent
 */
    class IdeHelperTrainingDate {}
}

Now I can easily mix and match methods coming from Builder and scope methods coming from TrainingDate and my IDE will never forget that it can keep using both.

Now of course, this was the quickest fix I was able to do, because I'm on a deadline and I'm sure there are lots of caveats here that I haven't thought of yet. And maybe the intellisense of IDEs could be expanded even more through methods similar to these.

So basically my request is, would you be willing to check out whether this approach is viable. As in, whether this doesn't create more problems than it solves. And if it does seem like a viable approach to extend IDE's intellisense then would you be willing to experiment what else could be improved with methods like these?

Edit

Okay after experimenting some more I'm now a bit confused. Because one of the things I was using to improve VSCode's understanding of my Laravel code was phpstan with an extension for it called larastan. And well, maybe some of all those many libraries and extensions have been feeding VSCode contradictory information or something? Because at some point, I was just disabling some extensions here and there to see whether I would lose important intellisense, but well, not always. Sometimes intellisense stayed the same and sometimes it even got a little better. And well now I'm not sure if my solution actually solved a problem that needed solving, if you know what I mean.

Still I'd appreciate if anyone here would be willing to take a little time to experiment with this and see what's what. 😬 🙏

mfn commented 8 months ago

Using templates certainly looks like a direction worth exploring, given nothing else breaks, including PhpStorm, phpstan, psalm, etc. Or at least have some sense of assessment if it causes collateral changes.

mreduar commented 8 months ago

Hello @Evertt Have you found a solution?

Please can you tell me more about the extension you use to detect the scopes without the with?

Strictly speaking, I don't even need the line for search there, because one of my extensions is already smart enough to be able to link search() to scopeSearch(), but it's fine. So when I do this, things work completely fine:

Evertt commented 8 months ago

@mreduar Well I'm starting to think that I should just completely and only rely on phpstan with larastan.

Please can you tell me more about the extension you use to detect the scopes without the with?

It seems I was mistaken. I just turned off the extension that I thought was making that work (which was this one), but it turns out that extension was not making the making possible, cause it still worked when I turned it off. Then I thought maybe phpstan / larastan was doing it, but I disabled those too and then it still worked. Turns out that somehow _ide_helper_models.php was making it work, because it stopped working when I deleted that file, even though I don't completely understand how. Because I can't see how that file, which defines the scoped method simply like @method static \Illuminate\Database\Eloquent\Builder|TrainingDate search($search), can make VSCode understand that that refers to the scopeSearch method on the model.

I think I'm making things very confusing for myself by having so many extensions and packages installed to improve VSCode's intellisense and now I find it hard to determine what extension or package is doing what exactly.

Edit

But in the end, I've been talking to the maintainers of larastan, and it seems like the features that they are missing (which laravel-ide-helper is currently complementing) is because some of their templates / stubs are a little out of date. But they intend to update them and then their thing should be able to offer all the intellisense that laravel-ide-helper is also offering.

mfn commented 8 months ago

Because I can't see how that file, which defines the scoped method simply like @method static \Illuminate\Database\Eloquent\Builder|TrainingDate search($search), can make VSCode understand that that refers to the scopeSearch method on the model

It doesn't: ide-helper finds scopeSearch and therefore creates the method in the phpdoc you see, that's it.

The IDE (that file format was "invented" by IntelliJ for PhpStorm, other might/might not follow) then reads this file and off you go. Besides ide-helper being the generator, there's no "connection" that ::search goes to scopeSearch.

FWIW, IMHO it's recommended to not use the _ide_helper_models.php file and just let the PHP doc write directly to the models. That might also increase compatibility with other IDEs not knowing about that file but which are able to look at the phpdoc attached to the class directly.