Skip to content

Conversation

@imliam
Copy link

@imliam imliam commented Mar 15, 2025

This adds a user() helper function to the base application, which is type-hinted to return an instance of the App\Models\User class by default.

Why?

auth()->user() is handy, and widely used in Laravel projects, but its return typehint is for a Illuminate\Contracts\Auth\Authenticatable instance.

While this may be valid from a framework perspective, and there are many uses for Authenticatables that are not the User model, most Laravel applications use the defaults. Therefore, users expect an instance of the User model returned.

This means that, without the help of third-party tools like laravel-ide-helper, as developers we get a poor experience when getting the authenticated user and expecting the model:

  • No IDE support for chaining methods/properties
  • Static analysis fails if we chain anything not on the Authenticatable interface
  • We have to typehint /** @var \App\Models\User $user */ everywhere we use the helper

By adding this to the basic application scaffold, the new user() helper can get proper typehinting for the out-of-the-box User model that ships with a fresh application, supporting proper typehinting. As it is in the userland instead of the framework, it can be changed to support other Authenticatables if the developer wishes.

Why should this be in the base application?

Yes, users could always add this helper manually to their app, but having a standard way the framework does it will eventually bring consistency to new applications—and I believe this utility is used commonly enough that it warrants being in the base.

There's clearly some interest in it, and people have a dozen of their own home-grown solutions for the same problem, something this could provide.

Why not in the framework?

App\Models\User is defined in the user's application, so the framework has no knowledge of it or if it changes names/namespaces/etc.

@James4645
Copy link

James4645 commented Mar 16, 2025

I don't get the point of this. Seems like yet another worthless file I'll have to remove from every app I make like the console routes file. And seems directly contrary to th direction Laravel went with 11.x for slimming the skeleton.

If you want it just add it yourself to your app.

@joshmanders
Copy link
Contributor

Yeah this should be either apart of the framework's existing helpers or added yourself to your own project.

With the new --using flag in the installer you can make your own starter with this

@taylorotwell taylorotwell marked this pull request as draft March 16, 2025 23:51
@AhmedAlaa4611
Copy link
Contributor

We already have Laravel's default middleware auth and guest, this is useless.

throw new Exception('The current user is not authenticated.');
}

if (! $currentUser instanceof User) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the function has a return type of App\Models\User this check is unnecessary.

If an object which is not an instance of App\Models\User is returned, PHP will error out automatically.

Copy link
Author

@imliam imliam Mar 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would throw a TypeError you're right, but I made it explicit here so that static analysis tools can tell what's going on

Another option would be to docblock the $currentUser variable for static analysis and allow the TypeError to be thrown at runtime

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have the user model defined in the config: auth.providers.users.model.
It should use that.

I’m experiencing the same frustration with PHPStan. I’ll probably implement something like this in my projects, but I feel like a better version should be implemented at the framework level.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so

@rodrigopedra
Copy link
Contributor

I usually add a helpers.php file to my app's ./app folder and namespace it on App, something like this:

<?php

namespace App;

function foo() {}

It is still short enough to use in views ({{ \App\foo() }}), and in regular code I can import it. And has the benefit of not polluting the global namespace.

I would vote to have the helpers file under ./app, as it is application's code.

I can see the benefit of having this file included on the likes to show users how easy, and useful, it is to have a helpers file.

Much like what we have in ./route/console.php, which ships with the inspiring command, and anyone who doesn't want that command can easily remove it on their app.

@msamgan
Copy link

msamgan commented Mar 20, 2025

It also has the additional benefit of an added helper file that will come with the project, most of the devs had to add that file to their project manually. Maybe bootstrap/ is not the right place, but the idea in itself is not bad.

throw new Exception('The current user is not authenticated.');
}

if (! $currentUser instanceof User) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Helpful idea, but I think using App\Models\User directly could be limiting. In many cases, the authenticated user isn't always a User instance — it can be any Authenticatable implementation, defined via auth.providers.users.model and possibly located in a different namespace.

Also, when calling auth()->user() (or this helper if added), developers typically expect an Authenticatable, not specifically a User model. This might lead to confusion or unexpected type errors in custom setups.


if (! $currentUser instanceof User) {
throw new Exception('The currently authenticated user is not an instance of '.User::class);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This breaks in apps with multiple auth guards. It assumes only User gets authenticated.

@MR-Sharifi
Copy link

In my opinion, it's not a necessary functionality. Even if it was, it does not consider all possible situations. For example, what if someone (like me) uses a custom-curated class for the user entity that does not extend the App\Models\User?

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

if ($currentUser === null) {
throw new Exception('The current user is not authenticated.');

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe, this could be a dedicated UnauthenticatedException or the like 🤔

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can add the guard as a parameter to the function so we can get the user for a specific guard

Comment on lines +12 to +14
if ($currentUser === null) {
throw new Exception('The current user is not authenticated.');
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want, you could shorten this

Suggested change
if ($currentUser === null) {
throw new Exception('The current user is not authenticated.');
}
throw_if($currentUser === null, Exception::class, 'The current user is not authenticated.');

@inmanturbo
Copy link

inmanturbo commented Apr 18, 2025

I think this is an issue for the vscode extension honestly. There has got to be a good way somehow to configure static analysis to bind preferred, current or default implementations for contracts in general.

I honestly feel the OP's pain. I do, but this is part of a larger problem. Authenticatable is one of many cross cutting, integral contracts provided and fulfilled by the framework. If only we could globally configure the ones we're most concerned with somehow using some kind of config map? Preferably not in yaml. 😅

@RyanPaiva56
Copy link

Agree that this is necessary. Also agree that improving the IDE extension is the way to go. Laravel needs a best-in-class IDE that works out of the box without having to install a bunch of things to get it to work.

Now that Laravel Cloud exists, the IDE is the last painful point that sucks for literally every developer. PHPStorm wont do anything and defers everything to the IDEHelper (BLESS BARRYV) but it adds a ton of bloat.

VSCode is the obvious choice and the official extension is really the future. We shouldn't be making framework choices based on IDEs being bad at what they do. Instead we should be improving the IDE extension.

@rodrigopedra
Copy link
Contributor

PHPStorm wont do anything and defers everything to the IDEHelper (BLESS BARRYV) but it adds a ton of bloat.

The Laravel IDEA plugin is worth every cent, and works incredible well with (most) type analysis needed on a Laravel project.

PHP Storm offers the Laravel IDEA plugin at a discounted rate when licensing both the IDE and the plugin together.

I am not affiliated to PHP Storm nor to the Laravel IDEA plugin, just a satisfied client of both tools

@RyanPaiva56
Copy link

PHPStorm wont do anything and defers everything to the IDEHelper (BLESS BARRYV) but it adds a ton of bloat.

The Laravel IDEA plugin is worth every cent, and works incredible well with (most) type analysis needed on a Laravel project.

PHP Storm offers the Laravel IDEA plugin at a discounted rate when licensing both the IDE and the plugin together.

I am not affiliated to PHP Storm nor to the Laravel IDEA plugin, just a satisfied client of both tools

I've paid for both and went to vscode anyway. Caleb's make vscode awesome already puts it ahead of phpstorm, and with intelephense (the paid version) and the Laravel vscode extension, it's miles ahead. If we can invest more in a 1st party extension, it solves all of these issues. Case in point, we have talented developers sending pull requests for functions that have been around from day 1 because IDEs are still painful even for veterans.

@benfaerber
Copy link

benfaerber commented Apr 23, 2025

I always create a method called User::auth() on my user to get proper hints.

class User {
    public static function auth(): self {
        $user = auth()->user();
        throw_if(! $user, Exception::class, "Called from invalid auth context!");
        return $user;
    }
}

@shaedrich
Copy link

shaedrich commented Apr 23, 2025

I always create a method called User::auth() on my user to get proper hints.

This might be interesting to add to \Illuminate\Auth\Authenticatable, however, that won't solve @imliam's original problem 🤔

@nazmulpcc
Copy link

Ignoring other issues, this will need manual tweaking as soon as someone uses another Authenticatable class.
What I do is add something like this in a .ide.php file

namespace Illuminate\Contracts\Auth {

    interface Guard
    {
        public function user(): \App\Models\User;
    }
}

I don't usually reach for barryvdh/laravel-ide-helper in simple cases like this.

@Aluisio-Pires
Copy link
Contributor

I think your idea is excellent, but the way it’s been implemented doesn’t align with the framework’s current structure. Depending on the project, we might have multiple authentication models or models organized into separate folders in modular architectures. The helper should be flexible enough to handle these cases. If you can refine your approach, it would make a wonderful contribution ❤️🚀

@pintend
Copy link

pintend commented Aug 5, 2025

I always create a method called User::auth() on my user to get proper hints.

This might be interesting to add to \Illuminate\Auth\Authenticatable, however, that won't solve @imliam's original problem 🤔

If you add public static function current(): ?static to Authenticatable and call User::current() you get null|\App\Models\User

@benfaerber
Copy link

benfaerber commented Aug 5, 2025

I always create a method called User::auth() on my user to get proper hints.

This might be interesting to add to \Illuminate\Auth\Authenticatable, however, that won't solve @imliam's original problem 🤔

If you add public static function current(): ?static to Authenticatable and call User::current() you get null|\App\Models\User

I guess there's nothing wrong with that. auth()->user() can return null so it will be consistent. The user will just have to manage the null check. It still solves the problem of not being aware of methods / properties on the model.

@SanderMuller
Copy link

With the Laravel Idea Plugin being free now in PhpStorm, the vscode extension should fix this if it doesn't already, then this helper shouldn't be needed. I also think having a user(), auth()->user() and request()->user() is too confusing for new developers.

@noxsii
Copy link

noxsii commented Oct 9, 2025

please not..

@MadBox-99
Copy link

auth()->user() can be Auth::user() so u dont need helper, mostly try to use all time facade for geting authed user.

@Tchilly
Copy link

Tchilly commented Oct 15, 2025

I think this is an overall good idea, I usually implement aliases for user(), and use inertia(), auth() everywhere I can, it's simply shorter to type than Auth::user() or Inertia::render()... if people want to customize the "driver" for it, introduce a config variable for it, or an optional param?

@RyanPaiva56
Copy link

Why not make an improvement to the VSCode plugin instead of making changes? Most of the helper functions yield IDE warnings like url()->full() auth()->user() etc. No need to make user() or full() separate things.

@imliam
Copy link
Author

imliam commented Oct 15, 2025

Plenty of comments here about using the PHPStorm/VSCode plugin, but that has 2 problems:

  1. You shouldn't have to rely on a very specific IDE to get a good experience for what can be supported with a fundamental PHP feature
  2. That doesn't solve the problem for third-party tools outside those IDEs, like static analysis (which currently have to do workarounds for this common use case)

@MadBox-99 - auth()->user() can be Auth::user() so u dont need helper, mostly try to use all time facade for geting authed user.

The issue this aims to resolve is to get the right type hinted for the user returned, which those functions don't do for the scenario described.

@SanderMuller
Copy link

Plenty of comments here about using the PHPStorm/VSCode plugin, but that has 2 problems:

  1. You shouldn't have to rely on a very specific IDE to get a good experience for what can be supported with a fundamental PHP feature
  2. That doesn't solve the problem for third-party tools outside those IDEs, like static analysis (which currently have to do workarounds for this common use case)

@MadBox-99 - auth()->user() can be Auth::user() so u dont need helper, mostly try to use all time facade for geting authed user.

The issue this aims to resolve is to get the right type hinted for the user returned, which those functions don't do for the scenario described.

I agree, but I don't think a global helper is the solution, but that requires a return type that handles the multiple possible implementations of the Authenticatable interface.

A solution could be a trait that adds a static function on the User model or a User facade, then you can choose a concrete implementation of the Authenticatable to get the authed user from. Otherwise the framework gets a helper method that tells a lie whenever you application has a non-user Authenticatable.

@noxsii
Copy link

noxsii commented Oct 16, 2025

“You shouldn’t have to rely on a very specific IDE to get a good experience for what can be supported with a fundamental PHP feature”

That’s true in theory — but in practice, this proposal introduces more assumptions and magic than it solves.

You’re enforcing a single, hardcoded model (App\Models\User) in a helper that pretends to simplify things, but in reality:
• It breaks in multi-auth apps
• It assumes default Laravel scaffolding
• It hides nullability checks behind a false sense of safety
• It pollutes the global namespace for a marginal gain

If this was really about improving static analysis, you’d push for better support in IDEs and tools like PHPStan — not inject app-specific logic into framework-level conventions.

“You shouldn’t need a specific IDE”

You also shouldn’t need to overwrite framework defaults with brittle shortcuts just to avoid typing a docblock. This isn’t developer experience — it’s developer laziness disguised as convenience.

@imliam
Copy link
Author

imliam commented Oct 16, 2025

@noxsii it makes an assumption but it does not enforce anything. The function explicitly resides in the user's application and allows them to change it if they use a different Authenticatable than the default.

You also shouldn’t need to overwrite framework defaults with brittle shortcuts just to avoid typing a docblock. This isn’t developer experience — it’s developer laziness disguised as convenience.

I'd argue the framework defaults are the problem here - PHP can already support what needs to be done here

@noxsii
Copy link

noxsii commented Oct 16, 2025

If the framework defaults are the problem, then the solution should be to improve the defaults — not introduce yet another userland workaround that:
• Assumes a model structure (App\Models\User)
• Duplicates existing functionality (auth()->user())
• Introduces hidden type failures in multi-auth scenarios
• Relies on devs “just knowing” they can change it

Calling this “not enforcing anything” is disingenuous — because the moment you ship this in a skeleton, you’re implicitly enforcing a convention, and every beginner will take it as canonical.

"PHP can already support what needs to be done here"

Yes, and Laravel already provides the flexibility for devs to override contracts, bind implementations, and configure their app however they want. Why is a hardcoded helper the answer to a tooling gap?

This isn’t solving the problem — it’s papering over it in a way that breaks as soon as you leave the happy path.

If the goal is better static analysis, IDE support, and DX:
solve that at the tooling layer — not by bloating the global helper space with fragile assumptions.

@shaedrich
Copy link

You shouldn't have to rely on a very specific IDE to get a good experience for what can be supported with a fundamental PHP feature

💯

You’re enforcing a single, hardcoded model (App\Models\User) […] You also shouldn’t need to overwrite framework defaults

It's not that people suddenly started implementing the Authenticatable interface in their custom User models. It's the User model that is provided by the framework itself implementing the interface by default, making the User model the default implementation, that the framework tends to rely on. So you can't blame that intermixing on the individual devs but on the framework.

@noxsii
Copy link

noxsii commented Oct 16, 2025

@shaedrich Sure, Laravel ships with App\Models\User as the default model — but it never guarantees or enforces that you’ll keep using it.

That’s the entire point of the auth.providers.users.model config: to allow replacing it.

“So you can’t blame that intermixing on the individual devs but on the framework”

Except we can — when individual devs start hardcoding assumptions (like App\Models\User) in global helpers without checking for configurability, that’s not Laravel’s fault.

The framework provides sensible defaults — not mandates.

This PR introduces a global helper that pretends to simplify things, while:
• Ignoring the auth config
• Failing in multi-auth scenarios
• Throwing runtime errors if the assumption breaks
• And offering no benefit that can’t already be handled via IDE helpers, docblocks, or better static analysis config

Let’s not blame Laravel for flexible defaults just because we choose to misuse them.

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

Successfully merging this pull request may close these issues.