Framework: Can't have access to auth user on Controller on L5.3

Created on 8 Aug 2016  路  32Comments  路  Source: laravel/framework

Is that a bug or is the new way to handle this?

I used to declare my auth user on the App\Controllers\Controller.php
and then share with all my views and SubControllers.

So should I change this or wait until 5.3 is release?

Most helpful comment

I have the same problem. Downgraded to 5.2 because of it.

All 32 comments

Can't have access to auth user on Controller

How do you normally access the auth user?

$user = auth()->user();
And then I share the $user on all my views, but on 5.3 it's null all time

@socieboy Checked that the correct middlewares are in place?

What are the correct middlewares?

Please ask on the forums and I'm sure someone will help you there.

Hi @GrahamCampbell,

All middlewares are in place. There is already a thread for this issue in the forums. The problem happens when you try to access auth::user() from the controller constructor, which I use to share my user across the controller and views. Please see:
https://laracasts.com/discuss/channels/laravel/laravel53-controller-auth-problems

I hope you can help.

Confirmed that using auth facade or global helper returns null when using the code in the base controllers constructor in 5.3.

But if you follow the docs it works fine.

https://laravel.com/docs/master/views#sharing-data-with-all-views

The problem is that I cannot access the auth helper in the constructor of any controller.

Not only I need to share the authenticated user in every controller (I would need to do this in every method), but also I share the user and other entities (cached relationships from the user) across all controllers. Is there any workaround I can implement?

Im using a Fresh installation of Latavel 5.3 and I can't have access to the auth()->user() on the constructor of any controller, no body here has found a solution for this, and you just close the issue? That's sucks!

You can't access the user in the constructor. This is expected.

I could to this on 5.2 and older versions of Laravel. Is there any reason to change it??

I could to this on 5.2 and older versions of Laravel.

If it was never intended to be supported, therefore it would be hard to expect that anyone would actually tests or ensure that this "feature" would work when refactoring for a major version.

Is there any reason to change it??

The reason that it's not working now could be due to middleware or routing refactors in 5.3. auth()->user() returning null is a sign that StartSession middleware is not executed yet when you construct the controller.

Thanks for the explanation @crynobody. What is the best way to have access to the auth user on all my controllers if can't declare a property on the extended Controller?

Is it available from the Request object?

public function __construct(Request $request)
{
    $this->request = $request;
    $this->user = ($this->request->user()) ?: null;
    view()->share('_user', $this->user);
}

this is a pattern i've used successfully for awhile.

I don't think so! I have using this method to share the auth user a cross all the subcontrollers not only on all views, so I think would be good to have the way to decelerate the auth user on constructors back again!

@axyr - You say "allow boot() method on controllers" and then closed the issue.

What does this mean? The underlying question remains: how do we access the Auth::user() from the base controller? Are we supposed to call boot() from the constructor? BaseController doesn't have a boot() method as of v5.3 downloaded 8/23/16.

I only referenced to this issue?
If I did close it, It was by accident!!

Please see this alternative :

https://github.com/laravel/framework/pull/14834#issuecomment-241533275

@axyr - Thanks for the alternative. Unfortunately, the user is getting authenticated _after_ the controller is initialized, so listening for an auth event doesn't help to set permission-based variables. Yes, I can litter my app with instances of Auth::user() and calls to the same core method over and over, but this doesn't seem like a clean way to do it. Surely, I'm missing something.

this frigging change in 5.3 breaks my whole app..arghh.. found a fix for 1 place.. and other place screams for help too... if you cant use auth in constructor... how do you passes variable that you want to use in most controller method

I solved it with this :

<?php

namespace App\Http\Controllers\Admin\Models;

use Illuminate\Support\Facades\Event;
use Illuminate\Auth\Events\Authenticated;
use App\Http\Controllers\Admin\AdminController;

abstract class AbstractApiAdminController extends AdminController
{
    protected $user;

    public function __construct()
    {
        parent::__construct();

        Event::listen(Authenticated::class, function ($event) {
            $this->user = $event->user;
        });
    }
}

I have the same problem. Downgraded to 5.2 because of it.

Passing for the same issue. I hope somebody address this matter. Btw, I a upgrading my apps

Passing for the same issue. I hope somebody address this matter. Btw, I a upgrading my apps

TBH, it's not going to be reverted. each 5.x is a major release and this breaking change is an expected behaviour (side affect to the changes).

Ok thats cool to me. Why I would like to know is how can face this because in a fresh 5.3 installation everything is alright, even this behavior

Why I would like to know is how can face this because in a fresh 5.3 installation everything is alright, even this behavior

In 5.2

User request -> Application boot -> Run middleware through pipeline -> Construct matched Controller -> Run controller's middleware through pipeline -> Call the controller's action.

in 5.3

User request -> Application boot -> Construct matched Controller -> Run all middleware through pipeline -> Call the controller's action.

With this changes, pipeline is loop only once, resulting to better performance, the side affect is pipeline is handled after constructing the controller.

Oh ok, thanks. So, how can we reproduce something similar in this new scenario? For example, something simple like sharing the user throughtout controllers from the base one.

Does it have to be done form the middleware then?

Thanks

I had a very similar scenario I was going mental as parent Controller constructor wouldn't give me the authenticated user via injected Guard:

class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;

    /**
     * @var User
     */
    protected $operator;

    /**
     * BaseController constructor.
     *
     * @param Guard; $guard
     */
    public function __construct(Guard $guard)
    {
        $this->operator = $guard->user();
    }
}

It's really unfortunate that the only way to get the authenticated user now is directly from the action method.
Also - @axyr - thanks for the suggestion, but relying on the event to set your property - really not sure about it.

You can use this to get the authenticated user, i'll let this here as a reference since more ppl will come here:

         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
        });

@fernandobandeira - yes I've seen this response by @axyr, but I'm really not excited about it if I'm honest - although it is probably the only option at the moment.

Yep, also this is not the only issue you can check #15048 that is related, this BC is causing a lot of pain actually.

Also this is the beginning of this story:

14824 and PR that tried to provide an easy solution: #14834

I guess we will come up with good solutions and document them in the future.

Taylor has updated the docs to address this issue https://laravel.com/docs/5.3/upgrade#5.3-session-in-constructors

What I've ended up doing - in case someone might find it useful, here's a content of my base controller

class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;

    /**
     * @var array
     */
    protected $view_shares = [];

    /**
     * Get authenticated user instance.
     *
     * @return User
     */
    public function operator() : User
    {
        return Auth::user();
    }

    /**
     * Get view.
     *
     * @return View
     */
    protected function view() : View
    {
        $arguments = func_get_args();

        $this->viewShares();

        return view(...$arguments);
    }

    /**
     * Variables to be shared with view.
     *
     * @return void
     */
    protected function viewShares()
    {
        $shares = array_merge(
            $this->view_shares,
            [
                'operator' => $this->operator()
            ]
        );

        view()->share($shares);
    }

    /**
     * Add item to the array of items needed to be passed to the view.
     *
     * @param mixed $key
     * @param null|mixed $value
     * @return Controller
     */
    protected function addToShare($key, $value = null) : Controller
    {
        if (is_array($key)) {

            $this->view_shares = array_merge(
                $this->view_shares,
                $key
            );

            return $this;

        }

        $this->view_shares[$key] = $value;

        return $this;
    }

}

So now rather than having user instantiated from within the constructor in the base controller - I simply have a method operator() and then share it with all views from within the viewShares() method, which is called when I call $this->view() - replacing the view() helper.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

gabriellimo picture gabriellimo  路  3Comments

shopblocks picture shopblocks  路  3Comments

PhiloNL picture PhiloNL  路  3Comments

Fuzzyma picture Fuzzyma  路  3Comments

ghost picture ghost  路  3Comments