-
Notifications
You must be signed in to change notification settings - Fork 11.3k
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
[5.3] Allow boot() method on controllers #14834
Conversation
Given the fact that, even before the release of 5.3 is official, there are raised some issues about using Yes, one could answer : "You should not use a constructor for auth" or "it is never supported, so don't expect it to work in this release", without giving an alternative, but my guess is that these closed issues will come back once 5.3 is official : https://laracasts.com/discuss/channels/laravel/laravel53-controller-auth-problems I guess a lot of devs use the constructor like that and there is a demand for a central place to have auth/session/etc available in all Controller methods, without the need to check them in every method separately. So if the This 'hack' seems to work fine though : class MyController extends Controller
{
public function callAction($method, $parameters)
{
dd(app('auth')->user()); // this works
return parent::callAction($method, $parameters);
}
} but this will probably gets a reply (in a next major release) like : its not supported or you should not do it like that.... ;) |
Thanks for the feedback there. Validates that I'm not crazy for using this approach for a very large app with many base controllers to handle common variables and data calls. |
If am not mistaken you could inject the request class itself into the controller itself and get the user from there? And then just store that in a property on a perent class. |
The whole point is that the user is not available yet. It's only bound into the request class after authorization runs.
|
@JosephSilber was that comment to me? |
It's not just the user but any other stuff you may need for a group of similar controllers. Being able to extend a parent BaseController is pretty common functionality to make that process easier. 5.3 breaks that ability with anything that uses session, auth, csrf, etc so this is the reasonable alternative in my mind. |
@morloderex On 5.3 you can't access the user inside the construct anymore, also it's not supported to access any middleware inside the construct (since they are instantiated after the controller construct). So with this method you can get the user and access some middlewares after the controller is constructed and the middlewares were instantiated. 👍 |
You couldn't on 5.2 either. Not reliably. |
I have been doing it reliably on a large 5.2 app that heavily utilizes extended controllers with tens of thousands of users per day. It's been working since 4.0 without issue until the change I pointed out in #14824 with cached controller objects. |
So why did it work on 5.2 but not on 5.3? |
It never worked in 5.2 for controller middleware. Only for route middleware.
Since 5.3 consolidated both middleware into a single pipeline, you now can't rely on the route middleware either.
|
@taylorotwell the route middleware part worked previously because the controller was being re instantiated once all of the middleware was finished. In 5.3 the controller object is only created once then cached so it's __construct method never has access to data mutated by middleware. This new proposed |
Hello, Any news on this? Some of my apps also relly on controllers' constructor and this seems like a good solution. |
Taylor's suggestion via some DM's I have had with him, was to use an event listener for the user to be logged in. He has some concerns about the testability of the The event listener idea kinda works but we need another event on the auth class to make that work. Right now the I have resorted to adding a custom event called Not ideal but it works for now to get me moving again on my 5.3 upgrade. If that is a better solution than this |
In that case the user is still not available in controllers' constructors (and also constructors of any typehinted classes in them)? |
This situation can be also be solved by adding a method to your controller that returns the value you were setting as a property before. If necessary, the method can cache the result to a protected property and return the same result each time on subsequent calls. I want to avoid a |
Yeah I forgot to mention that I solved most of it via a wrapper method like you're talking about @taylorotwell but there were still some cases where the event approach worked better (setting shared view data, setting the current module, breadcrumbs, etc) so some use that instead. |
Can you provider a sample how you solved it? @tomschlick |
@tomschlick some example code for the Event solution would be much appreciated 👍 |
You can do so like this. However the authenticated event doesn't exist yet in the core. I have a PR for it with #14946 class BaseController extends Controller
{
public function __construct()
{
Event::listen(Authenticated::class, function ($event) {
$this->currentUser = $event->user;
view()->share('currentUser', $this->currentUser); // simple example
// other code that may need to bootstrap stuff for this user in the base controller
});
}
} |
This works for me. thanks. |
ah...searched for the wrong keyword..no wonder cant find it XD... guess this could work |
@taylorotwell - with the deepest respect, I'd suggest you might want to rethink this if for no other reason than you may have to pave a cowpath, here. Given the number of comments I'm reading from people who are in the same boat as me, this appears to be a major show-stopper to the 5.2->5.3 upgrade for a non-trivial number of Laravel devs. Intended or not, it was very much possible to call Auth::user() from the base controller in 5.2, and, right or wrong, devs like me built their app around that ability. Since it's completely broken in 5.3 and I have neither the time nor the budget to refactor my production app to account for this, I'm forced to downgrade back to 5.2 and stay there. In a perfect world, I'd like to see a notional 5.3.1 that allows me to access Auth from the base controller like you can in 5.2. As a side note, I'd love to learn from you the proper way this (setting app-wide Auth-based parameters) should be accomplished. I've been taught my whole OOP and MVC career two things: 1) avoid re-writing code as much as possible, and 2) logic like permission gates should be done at the controller level, not the view. Given this, putting Auth logic in the baseController class constructor would seem to me to be exactly the correct approach. Am I missing something? I'd love your input. Many thanks, |
If you haven't see it, Taylor added an option to fix this. https://laravel.com/docs/5.3/upgrade#5.3-session-in-constructors |
I gotta try it tomorrow. I just really would like to know why this is better working this way than before. I really like laravel, and would like as always see a goos explanation about something like this matter. Thanks to you all |
It was necessary in order to allow route model binding to have access to the authorized user.
Here's a little more information:
https://josephsilber.com/posts/2016/07/10/authentication-improvements-in-laravel-5-3#route-model-binding-and-global-scopes
|
@JosephSilber I still have an issue tho, and don't know if I am missing something. I do not have access to the logged user from any My big question, why is it so important? I aware of the many explanations there are out there about this matter, but nobody has clarified the why of it. Also, I read what taylor posted in the docs, and still do not figure it out. This is something that seems to be simple, I could access to the logged user from any __construct. thanks in advance to you all. |
Has anyone got solution for this? I am using Laravel 5.4 and still have the same issue, I tried to implement the ways suggested in some of the links referred in the comments, but it still doesn't work.... any help on this is greatly appreciated, as I am completely stuck and cannot go ahead on one of the app I am building :( this is sheer waste of time, I was loving laravel until now.. just this thing is making me think did I consider it right or not. |
@testingtoolsco I've written up a few strategies for dealing with this here: https://josephsilber.com/posts/2017/01/23/getting-current-user-in-laravel-controller-constructor |
@JosephSilber Solution #4: Inline middleware works for me, thank you ! |
This is related to #14824 where not all the middleware executes before a controller is initiated which can lead to issues with auth, session, etc.
Adding a
boot()
method allows us to have BaseControllers that interact with "middlewarey" things without having to duplicate code / calls in each controller action.