37

So I'm just starting off with Laravel (using v5) and Eloquent. I'm working on getting some basic APIs up and running and noticing that a lot of working methods don't show up in PhpStorm's code hinting

So I have this model:

namespace Project\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;

class User extends Model 
    implements AuthenticatableContract, CanResetPasswordContract {
}

And in one of my controllers I try to do

User::query()->orderBy('id', 'desc');

User::query() creates a Eloquent Builder object and orderBy() behave properly and without error. However, PhpStorm does not show orderBy() (or take(), skip(), and I'm sure others) when I type User::query()-> and gives warnings when I actually do use it.

I am using Laravel IDE Helper which has helped immensely with bringing code hints to the Facades, but not to the models/builders it would seem.

Does anyone have a solution to this?

Josh
  • 8,079
  • 3
  • 24
  • 49
  • Have you set up scopes for project (inside phpstorm)? – Kyslik Apr 04 '15 at 01:59
  • @Kyslik Can you clarify what you mean by that? Project is set up just like any other project I've ever done (we mainly used Silex prior to this foray into Laravel). Everything else works correctly, just not these few items on the models and builders. – Josh Apr 04 '15 at 02:49
  • @JoshJanusch i'm experiencing the same problem, have you found a solution? – Amitay May 21 '15 at 07:58
  • 2
    @Amitay No, I wasn't able to make any progress, unfortunately. I've just given up, pretty much. Too many things being called through `__call()`. Figuring out what is available to you in Laravel is really difficult, especially with how poor the documentation is and how little Ottwell documents method arguments. – Josh May 21 '15 at 15:30
  • @JoshJanusch after doing some more research i found that it's a phpstorm bug. i checked their latest version and it still has the bug. hopefully they will fix it soon... – Amitay May 21 '15 at 16:12
  • @Amitay Do you have a link to the bug in their bug tracker? I'd like to see what the cause is and track its progress. – Josh May 21 '15 at 17:33
  • @Amitay Another dev I am working with discovered that it is because `Eloquent\Builder` doesn't have all the methods (though does have access to them through `Query\Builder`). The easiest workaround seems to be calling `->getQuery()`. That will return a `Query\Builder` and give you code hinting, at least for normal SQL query building anyway – Josh May 22 '15 at 22:58
  • @JoshJanusch there are many issues open in the issue tracker, here is one https://youtrack.jetbrains.com/issue/WI-19953. – Amitay May 23 '15 at 11:41

8 Answers8

94

For future Googlers, and perhaps OP as well if you are still sticking to Laravel.

The laravel-ide-helper package solves this issue for you quite elegantly, with what I believe is a relatively new feature; generated model PHPDocs.

You can generate a separate file for all PHPDocs with this command:

php artisan ide-helper:models

The generated metadata will look something like this for each class:

namespace App {
/**
 * App\Post
 *
 * @property integer $id
 * @property integer $author_id
 * @property string $title
 * @property string $text
 * @property \Carbon\Carbon $created_at
 * @property \Carbon\Carbon $updated_at
 * @property-read \User $author
 * @property-read \Illuminate\Database\Eloquent\Collection|\Comment[] $comments
 */
class Post {}
}

This caused issues for me in PHPStorm however, where the software was complaining about multiple class definitions. Luckily an option is readily available for writing directly to the model files:

php artisan ide-helper:models -W

There are a few more options and settings available if you need to tweak the behavior, but this is the gist of it.

Erik Johansson
  • 1,646
  • 1
  • 14
  • 19
20

Add in model PHPDoc@mixin

/**
 * Class News
 * @property int $id
 * @property string $created_at
 * @property string $updated_at
 * @mixin \Eloquent
 * @package App
 */
class News extends Model
{

}

In PHPStorm works

miken32
  • 42,008
  • 16
  • 111
  • 154
  • This is really a nice solution, seems to solve the problem without requiring an extra dependency, I think. – Jonathan Ma Mar 08 '21 at 00:14
  • This should be the definitive solution. – CXJ Feb 17 '22 at 16:49
  • 2
    `@mixin \Eloquent` doesn't work for me - it isn't recognized as a class. However I ended up using `@mixin Builder` (with `use Illuminate\Database\Query\Builder`), and that works - with the additional complication that I was using `[Model]::where(...)`, which for some reason also works, but to get rid of the PHPStorm hint I have to use `[Model]::query()->where(...)`. No idea what's going on here, but hey, as long as it works... – rob74 May 20 '22 at 14:56
6

If you're using BarryVHD's Laravel IDE Helper package, run:

php artisan ide-helper:eloquent

This will write /** @mixin \Eloquent */ into the vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php file.

GTCrais
  • 2,039
  • 2
  • 26
  • 32
5

You can try Laravel plug-in for PhpStorm and you need to specifically activate it in your project settings.

mirza
  • 5,685
  • 10
  • 43
  • 73
  • 5
    This definitely improved some things (route creation, config interaction, some additional Facade hinting), but it did not help with the models, unfortunately. Thanks for the suggestion, though. This will definitely be helpful. – Josh Apr 06 '15 at 15:36
5

A little late but I recently had the same problem so I thought I would put a note down:

This is because Database\Eloquent\Model.php has a query() function which returns \Illuminate\Database\Eloquent\Builder and the Eloquent\Builder has a line:

use Illuminate\Database\Query\Builder as QueryBuilder;

Then it uses 'magic' __call methods to call to functions in Query\Builder. (look for __call method in Eloquent\Builder)

See: http://php.net/manual/en/language.oop5.overloading.php#object.call

__call() is triggered when invoking inaccessible methods in an object context.

So, indeed the method you are calling is inaccessible :) There is not much that the IDE can do.

There are workarounds like using @method tags but it is unmaintainable. An alternative is to use @mixin (but this is not standards based). See: https://github.com/laravel/framework/issues/7558

I think this all be resolved when they get rid of all the magic calls in the Laravel code and use PHP 'traits' instead. See last message here. :)

Marcin Orlowski
  • 72,056
  • 11
  • 123
  • 141
Evren Yurtesen
  • 2,267
  • 1
  • 22
  • 40
5

Just import Eloquent Builder in your Model class and add mixin:

use Illuminate\Database\Eloquent\Builder;
/** @mixin Builder */

To cover all the models at once — add the mixin to the src/Illuminate/Database/Eloquent/Model.php)

imprfekt
  • 317
  • 4
  • 9
2

I wanted to have some kind of explicit "casting" when interacting with the query builder. Example...

$user = User::query()->findOrFail($id);
$user->myUserSpecialMethod(); // <-- IDE syntax error

Since all my models are extending my custom base Model which in turn extends Eloquent, I've ended up creating this method in my custom base model:

/**
 * Explicit type-hinting
 *
 * @return static
 */
static public function hint(BaseModel $model)
{
    return $model;
}

This way, it solves the IDE invalid error and helps me:

$user = User::hint(User::query()->findOrFail($id));
$user->myUserSpecialMethod(); // <-- all OK !

Please note that this is not OOP type casting. It is only a hint to help the IDE. In my example, the returned Model was already a User. If I woud use this method on a derived class like SuperUser, only the IDE will be fooled...

An nice alternative also is to put meta information directly over the assignment statement:

/** @var User $user */
$user = User::query()->findOrFail($id);
$user->myUserSpecialMethod(); // <-- all OK !

Or next to it...

$user = User::query()->findOrFail($id); /** @var User $user */
$user->myUserSpecialMethod(); // <-- all OK !
Isometriq
  • 371
  • 3
  • 8
1

Verified on Laravel 8, just added @mixin Builder to Illuminate\Database\Eloquent\Model.php annotation solved it.

// Illuminate\Database\Eloquent\Model.php
/**
  * @mixin Builder
*/
abstract class Model
n.y
  • 3,343
  • 3
  • 35
  • 54
Homer
  • 449
  • 4
  • 9
  • 2
    Couldn't you just vote up [existing same answer](https://stackoverflow.com/a/68130762/4774747) instead of adding another one? :D – imprfekt Jul 02 '21 at 08:44