Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Undefined method when scope is an interface but implementation class is expected or configured #1051

Closed
kczereczon opened this issue Mar 9, 2020 · 29 comments
Milestone

Comments

@kczereczon
Copy link

Describe the bug
There is missing autocompletion when a trait is applied to the class.

To Reproduce
First I've been added Laravel/Passport package. Then added use HasApiTokens trait to User model. Then I tried to call createToken method and Undefined method 'createToken'.intelephense(1013) occurs.

Expected behavior
createToken method should appear in the list of autocompletion.

Screenshots
image
image

Platform and version
Windows 10, WSL(Ubuntu-18.04), Intelephense 1.3.11

@bmewburn
Copy link
Owner

bmewburn commented Mar 9, 2020

This line below is probably setting the type of $user to an interface that does not have the method defined.

$user = auth()->user();

When you hover on $user of the above code what type does it show?

@Chimi-S
Copy link

Chimi-S commented Apr 9, 2020

I have got the same error. Help Please!

@yanlyan
Copy link

yanlyan commented May 7, 2020

This line below is probably setting the type of $user to an interface that does not have the method defined.

$user = auth()->user();

When you hover on $user of the above code what type does it show?

I also have the same problem
when I hover on $user it shows :

Illuminate\Contracts\Auth\Guard::user
Get the currently authenticated user.
<?php
public function user() { }
@return \Illuminate\Contracts\Auth\Authenticatable|null

@johanvanhelden
Copy link

johanvanhelden commented May 15, 2020

Yes, this is the source of many issues in my projects as well. Because if you use Auth::user() the extension does not know it's actually a user model. So my workaround for these issues is:

        /** @var \App\Models\User */
        $currentUser = Auth::user();

That really clutters the code tho, that's a downside. I wish intelephense would automatically know it's an instance of a user model.

@bmewburn - is that enough information?

@garygreen
Copy link

Laravel's docblock for Auth::user() is that it returns a contract:

<?php

namespace Illuminate\Contracts\Auth;

interface Guard
{
    /**
     * Determine if the current user is authenticated.
     *
     * @return bool
     */
    public function check();

    /**
     * Determine if the current user is a guest.
     *
     * @return bool
     */
    public function guest();

    /**
     * Get the currently authenticated user.
     *
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function user();

.... the User model/class in your application uses this interface.

Technically intelphense doesn't know that so it says that methods don't exist when in fact they most likely do. So you have to type hint to intelephense what's returned is a version of your user model:

<?php
/** @var \App\User|null $user */
$user = $this->auth->user();

As someone else mentioned, this does clutter your code a fair bit. I'm not sure what the alternative would be though, Laravel does this pretty much everywhere - it returns a "Contract" interface in the docblock.

Another issue with Laravel support for intelephense is with facades. It often complains:

<?php

Route::get('some-url');
?>

This will be marked as not knowing what Route is, it's a facade added by Laravel. I guess the workaround for that is to use https://github.com/barryvdh/laravel-ide-helper ?

@bmewburn bmewburn changed the title Missing traits autocompletion. Undefined method when scope is an interface but implementation class is expected or configured May 23, 2020
@bmewburn
Copy link
Owner

@garygreen is correct. Intelephense is working as expected here. Using @var is kind of equivalent to a cast to the User model but it is verbose. Other option is to create a helper function annotated to return the expected type and call it instead. Will leave this one open to see if this can be improved somehow.

@dgloriaweb
Copy link

dgloriaweb commented May 26, 2020

This line below is probably setting the type of $user to an interface that does not have the method defined.

$user = auth()->user();

When you hover on $user of the above code what type does it show?

Illuminate\Support\Facades\Auth::user

<?php
public static function user() { }
@return \Illuminate\Contracts\Auth\Authenticatable|null

@bmewburn
Copy link
Owner

In 1.5 intelephense will prefer definitions not found in vendor. This enables you to override vendor definitions by providing a helper stub somewhere in the workspace. It also means that _ide_helper.php will be preferred.

For the specific case of auth()->user() I don't think _ide_helper.php has a stub for this so it will probably still return an interface rather than a concrete class. It is up to the user to add stub methods/functions where desired.

@Grldk
Copy link

Grldk commented Jul 14, 2020

Using 1.5.2 this still doesn't seem to work.

Screenshot 2020-07-14 at 11 03 34

Auth::user() seems to return \App\Models\User, but when assigning it to a variable Intelephense still thinks the variable is the contract instead of the concrete User class as defined by _ide_helper.php:

Screenshot 2020-07-14 at 11 06 08

@bmewburn
Copy link
Owner

You're right @NEGits . Fixed in 1.5.3

@bmewburn bmewburn modified the milestones: 1.5, 1.5.3 Jul 16, 2020
@jiahong96
Copy link

Using 1.5.4 this still doesn't seem to work.

image

@orditeck
Copy link

Using 1.5.4 and I also still have this issue.

image

@earnjam
Copy link

earnjam commented Oct 10, 2020

Same here, still seeing the issue in 1.5.4 when using Auth::user().

It's detecting the definition in _ide_helper.php, but it's choosing the definition from vendor over it.

image

image

image

@smenzer
Copy link

smenzer commented Dec 20, 2020

this still seems to be an issue (I have the same problem as described above, running 1.5.4)...is there another Issue tracking this or shit it be reopened?

@Nemesis19
Copy link

+1

@izzycart
Copy link

+1 ... Those underlines makes me feel uncomfortable

@dantofema
Copy link

@mkopcic
Copy link

mkopcic commented Feb 3, 2021

Still not fixed.

@Dopey117
Copy link

Dopey117 commented Feb 6, 2021

+1

@bmewburn
Copy link
Owner

bmewburn commented Feb 6, 2021

If you are using ide-helper then calling Auth::user() should return the concrete model and not the interface. If you are calling auth()->user() then ide-helper doesn't seem to have an override for this barryvdh/laravel-ide-helper#755 . Instead you can create your own helper file in your project root and add an alternate return type for user().

<?php

namespace Illuminate\Contracts\Auth {
    interface Guard
    {
        /**
         * Get the currently authenticated user.
         *
         * @return \App\User|null
         */
        public function user();
    }
}

@earnjam
Copy link

earnjam commented Feb 6, 2021

If you are using ide-helper then calling Auth::user() should return the concrete model and not the interface.

As I noted in my comment above, I'm using ide-helper and calling Auth::user() and it is still defaulting to the interface over the model.

I haven't had time to dig into the underlying cause in intelephense, but just wanted to say that this is still an existing bug.

@bmewburn
Copy link
Owner

bmewburn commented Feb 6, 2021

@earnjam please open a new ticket with a minimal reproducible example. I'm not seeing this in my laravel tests.

@earnjam
Copy link

earnjam commented Feb 6, 2021

@earnjam please open a new ticket with a minimal reproducible example. I'm not seeing this in my laravel tests.

Thanks, I'll gather as much info as I can and put it into a new ticket.

@earnjam
Copy link

earnjam commented Feb 8, 2021

@bmewburn Just as a quick update, I created a fresh Laravel project to try to minimally replicate the issue and it worked fine. But my existing projects all had this problem.

So I dug into the VS workspace configuration and figured out that it was happening because of the way the folders were configured.

I had them configured like this:

"folders": [
    {
        "path": "."
    }
]

I typically have project directory setups that look something like this:

- project-name/
    - laravel/
    - other-project-stuff/
    - .lando.yml
    - project-name.code-workspace
    - TODO
    ...other various files

I'd usually add the whole project directory to the workspace so that I could quickly change workspace config, dev environment config, etc.

If I change the workspace configuration and explicitly include the laravel application folder instead of ., then Intelephense correctly determines to prioritize the concrete model over the Authenticatable interface.

Not sure why that is exactly, but I'll see if I can reconfigure my workspace setups to account for this and maybe this will help others who are encountering the same problem.

@getupkid
Copy link

getupkid commented Aug 1, 2023

Not sure why this is closed...none of the above helps...still an issue

@kczereczon
Copy link
Author

my solution was moving to php storm 🫢

@callmejed
Copy link

I have the same issue whilst using Spatie permissions. auth()->user()->can(), "Undefined method 'can'"

@farindra
Copy link

farindra commented Mar 2, 2024

I have the same issue whilst using Spatie permissions. auth()->user()->can(), "Undefined method 'can'"

try this
#2135

and modified it to

namespace Illuminate\Contracts\Auth;

interface Authenticatable
{
    /**
     * Get the name of the unique identifier for the user.
     *
     * @return string
     */
    public function can();
}

@callmejed
Copy link

I have the same issue whilst using Spatie permissions. auth()->user()->can(), "Undefined method 'can'"

try this #2135

and modified it to

namespace Illuminate\Contracts\Auth;

interface Authenticatable
{
    /**
     * Get the name of the unique identifier for the user.
     *
     * @return string
     */
    public function can();
}

Sorry for the incredible delay in response, but this works great. Thanks for pointing out the other issue too!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests