Improving IntelliSense for Laravel projects

Laravel has a lot of "magic" to help provide a developer-friendly framework. This "magic" can also be a roadblock for many IDE's to provide good IntelliSense suggestions. Here's how to fix that.

Screenshot of VSCode editor with an error message, saying that there’s no suggestions for the “Thread” keyword.

It is well known that Laravel works in magical ways to be a developer-friendly framework and make it super easy for you to bootstrap your ideas to fruition with as little friction as possible. With all that magic, though, comes the difficulty for your code editor to provide meaningful IntelliSense suggestions. Here’s how to fix it.

The example project

To illustrate what we're dealing with and progress looks like in solving the IntelliSense issues for Laravel projects, I have created a dummy project to showcase a few simple examples.

This very fictitious and a pretty standard project has the default App\User model which has a couple of extra relationships:

  • A User has many forum Threads through ->threads() relationship;
  • A Thread belongs to an owner user User through ->owner();
  • a Thread has many Posts by various users through ->posts() relationship.

We also have a SearchService for the sake of illustrating some parts other than just model relationships. Bear with me.

The examples I'll give will be from various ThreadsController methods to illustrate different kinds of missing IntelliSense and how it's solved later on.

The example code editor

For this example I will be using Visual Studio Code. With no support for Laravel out of the box and VS Code being one of the most popular code editors makes it the perfect choice for this article.

What it looks like at the very beginning

On a fresh VS Code install, you'll most likely notice that even the most basic IntelliSense for PHP is missing. It does not even find the related class that's part of the project.

Screenshot of VSCode editor with an error message, saying that there’s no suggestions for the “Thread” keyword.
No suggestions given = the Thread class was not recognised by the editor

Let's fix that.

PHP Intelephense

Open up your VS Code Extensions tab and search for PHP Intelephense. It's a fast and powerful code IntelliSense provider for your PHP language files. It is by far much faster than other alternatives you'll find on the VS Code Extension marketplace. Go ahead and install it.

Screenshot of VSCode Extensions tab and the “PHP Intelephense” extension selected.
Install "PHP Intelephense" extension

Once installed, it's going to start indexing the project (searching for PHP classes, methods, symbols, etc) so it can have a better understanding of the project's structure as well as making it super fast.

Let's look at where it gets us!

Screenshot of VSCode with the “Thread” keyword now being suggested after installing “PHP Intelephense"
The "PHP Intelephense" extension already provides us with a better experience

We're now getting some help from VS Code! IntelliSense now recognises the existing Thread and ThreadsController classes and bumps them up to the top as the most relevant - in which case, they are! Once I select the first result and press <Enter>, it will automatically import the use App\Thread statement at the top as well. Helpful!

There's something missing

Now let's try to use some relationships.

Screenshot of VSCode not understanding the relationship on a Laravel model.
We're expecting a User, but getting "mixed"

Looking at the code here we know clearly that the $thread->owner relationship returns a User model, but the PHP Intelephense extension can't yet make sense of that. It has no idea that trying to access owner as a property on a Laravel's Model instance will actually fallback to the owner() method on the instance, resolve the actual relationship, fetch the data from the database, and return a new instance of the related model. That's a lot of Laravel's magic in one place.

Let's look at another example. Let's resolve the SearchService class using the Laravel's Service Container:

Screenshot of VSCode not understanding the class being resolved from the service container.
There are no helpful suggestions here at all

We can see that the SearchService has two methods: findPosts() and findThreads() - none were suggested after resolving the service from the Service Container. How can we fix that?

Laravel IDE Helper

Barryvdh is a legendary contributor to the Laravel community and Open Source in general. You are most likely using at least one of his open source packages already, such as Laravel Debugbar or Laravel CORS.

To help us today with the IntelliSense problems we have been experiencing, we will be installing another one of Barry's legendary packages - Laravel IDE Helper.

First, let's install it via Composer:

$ composer require --dev barryvdh/laravel-ide-helper

Next, we will need to run a few commands to generate a few helper files. These will help by providing more information to IntelliSense for it to understand more of the Laravel's magic. Let's run these commands:

$ php artisan ide-helper:generate
$ php artisan ide-helper:meta
$ php artisan ide-helper:models --nowrite

If all went well, you will now have three new files in your project's root:

  • _ide_helper.php
  • .phpstorm.meta.php
  • _ide_helper_models.php

With any luck, PHP Intelephense will look at these files immediately and begin indexing the classes and methods available in your project. Let's see what that gets us.

Screenshot of VSCode now understanding the Laravel model relationship correctly.
We're now getting a User model!

That's better. Now accessing a relationship as a property returns the appropriate model. What about the Service Container?

Screenshot of VSCode now understanding the resolved service class correctly.
Now we're talking!

Much, much better. IntelliSense recognises the class instance being returned from the app() helper method is now the SearchService and it correctly suggests the available methods on this class.

We have gone from no suggestions at all - to meaningful suggestions. This makes for a much faster and a more enjoyable coding experience while working on a Laravel project using Visual Studio Code. All thanks to the PHP Intelephense extension and the Laravel IDE Helper package!

Cleaning up

It can be a little tedious to constantly re-generate these helpers files in order to keep up with new changes, especially when working in a large team. Let's make it better.

.gitignore

First, let's add these files to your .gitignore so they're not committed to the project's repository. There is no need to have these helper files as part of the repository because it will definitely pollute your Pull Requests will changes to these helper files and nobody likes the extra unrelated code in a PR.

_ide_helper.php
_ide_helper_models.php
.phpstorm.meta.php

Composer hook

Another way to automatically update the helper files is to call the IDE Helper commands automatically after Composer updates. Just insert this into your "scripts" array in your composer.json file:

"scripts":{
    "post-update-cmd": [
        "Illuminate\\Foundation\\ComposerScripts::postUpdate",
        "@php artisan ide-helper:generate",
        "@php artisan ide-helper:meta",
        "@php artisan ide-helper:models --nowrite"
    ],
    "post-install-cmd": [
        "@php artisan ide-helper:generate",
        "@php artisan ide-helper:meta",
        "@php artisan ide-helper:models --nowrite"
    ]
},

Now whenever you install or update your Composer packages, the helper files be automatically re-generated. Because these files are now also ignored by Git, there is no harm in constantly updating them.

As an added bonus, your colleagues will automatically get these helpers as well and their IDEs will pick up on them to make their development experience better as well! How cool is that :)

I hope this has helped. If you do have issues with the Laravel IDE Helper package, please refer to it's documentation here.