Access Auth in parent constructor in Laravel 5.3 [duplicate] - php

This question already has answers here:
Laravel 5.3 auth check in constructor returning false
(3 answers)
Closed 6 years ago.
Im working on an app in Laravel 5.3. I have created an AppController. I would like for all of my controllers to extend AppController so that they all share a few common properties and methods.
Given the below 2 implementations below, my intention is to set the current user to $this->user when AppController is constructed so it will be available to all controllers extending AppController.
I expect that when I call \App\Http\Controllers\User\PermissionController#test I should get my user object dumped twice. Once when AppController is initialized, and again when PermissionController#test is called.
However, inside of AppController __construct(), $this->user is always NULL while PermissionController#test dumps the user as expected.
AppController:
<?php
namespace App\Http\Controllers\App;
use App\Http\Controllers\Controller;
class AppController extends Controller
{
/**
* The current user when logged in
* #var \User
*/
protected $user;
public function __construct()
{
if(\Auth::check() ) {
$this->user = \Auth::user();
}
var_dump($this->user);
}
}
PermissionController:
<?php
namespace App\Http\Controllers\User;
use App\Http\Controllers\App\AppController;
class PermissionController extends AppController
{
public function test()
{
if(\Auth::check() ) {
// Do something
$this->user = \Auth::user() ;
}
var_dump($this->user);
}
}
If it matters, I'm using https://github.com/Adldap2/Adldap2-Laravel to auth against Active Directory.
Clearly I'm misunderstanding something hear. Why does AppController __construct() not dump the user?

This is because of changes in Laravel 5.3. If you need to use Auth in constructor you need to do it like so:
public function __construct()
{
$this->middleware(function ($request, $next) {
if(\Auth::check() ) {
$this->user = \Auth::user();
}
var_dump($this->user);
return $next($request);
});
}
This change was described in upgrade guide to Laravel 5.3 - https://laravel.com/docs/5.3/upgrade#upgrade-5.3.0 look at section Session In The Constructor

Please check my answer here.
You need to move this check in middleware if you're using Laravel 5.3

The controller construct executes before any middleware, so there is no user yet (https://laravel.com/docs/5.3/upgrade#upgrade-5.3.0)
You can use closure middleware as described in the guideline.
From the docs
In previous versions of Laravel, you could access session variables or
the authenticated user in your controller's constructor. This was
never intended to be an explicit feature of the framework. In Laravel
5.3, you can't access the session or authenticated user in your controller's constructor because the middleware has not run yet
As an alternative, you may define a Closure based middleware directly in your controller's constructor. Before using this feature, make sure that your application is running Laravel 5.3.4 or above:
public function __construct()
{
$this->middleware(function ($request, $next) {
$this->user = auth()->user();
return $next($request);
});
}

Related

Feature test for authenticated user with auth middleware missing

I have a class that sets auth middleware in controller, like this:
class MyController extends Controller
{
public function __construct()
{
$this->middleware('auth:api');
}
public function something()
{
dd(Auth::user());
}
}
And a test like this:
class MyControllerTest extends TestCase
{
public function testSomething()
{
$user = factory(User::class)->create();
$this->actingAs($user);
$this->get('api/something');
}
}
The expected output is a dump of the user.
Everything works as expected, both in real application and in feature test.
The problem begins here: If I remove this line: $this->middleware('auth:api'); the output is the dump of user in tests but in real application it gets null. This is a problem as I can't rely on feature test.
With actingAs() Laravel seems to authenticate the user under the hood and then the test pass but the application fails. Is this a bug or there is something I'm missing here?
Using Laravel 7.x
If I understand right - you want test "is route has middleware". Than add to your tests:
public function testAuth()
{
//without acting as
$this->get('api/something')->assertForbidden();
}
With this, you can be you can be sure that Auth::user() is not null.

Laravel 5.2 -> 5.3 Auth::user() returning null

I've upgraded from 5.2 -> 5.3 and the Auth::user() is returning null.
Route
Route::group(['middleware' => ['auth']], function () {
Route::get('/test', 'MyController#showMain');
}
Controller with constructor calling Auth::check() returns null
public $user;
public function __construct() {
$this->user = Auth::user();
}
public function showMain() {
return $this->user;
}
Controller with showMain calling Auth::check() returns User (as expected).
public function __construct() {
// Nothing
}
public function showMain() {
return Auth::user();
}
I've also looked at the difference between a clean install of 5.3 and 5.2->5.3 upgraded. There are 2 extra classes in 5.3 that are not in the upgraded version.
Authenticate.php
Authorize.php
And these classes are being called by the Kernel.php in protected $routeMiddelware
I've also looked into \Auth::user() is null in 5.3.6?, not only this doesn't solve my specific problem, I also don't think it's a good solution.
Can someone explain to me why am I running into this problem?
Starting with Laravel 5.3 one cannot get the currently logged in user in controller constructor because the middleware has not run yet, but in other controller methods, as you have showMain, there is not problem getting it.
Laravel migration guide excerpt:
In previous versions of Laravel, you could access session variables or the authenticated user in your controller's constructor. This was never intended to be an explicit feature of the framework. In Laravel 5.3, you can't access the session or authenticated user in your controller's constructor because the middleware has not run yet.
https://laravel.com/docs/5.3/upgrade#5.3-session-in-constructors
To get access to Auth::user() in the __constructor() (starting with Laravel 5.3) you'll need to run:
public $user;
public function __construct() {
$this->middleware(function ($request, $next) {
$this->user = Auth::user();
return $next($request);
});
}

Laravel 5: Global Auth $user variable in all controllers

In my BaseController I have this:
public function __construct()
{
$user = Auth::user();
View::share('user', $user);
}
I can access the $user array throughout all of my views but what if I want it to be a global variable available in all of my Controllers?
I have the following working code:
use Auth;
use DB;
class UsersController extends BaseController
{
public function index()
{
$user = Auth::user();
$users = DB::table('users')->where('user_id', $user->user_id)->get();
return view('pages.users.index', compact('users');
}
}
I want the $user variable available in all of my controllers and within all of the public functions within each on the controllers but I don't want to have to redeclare "use Auth;" and "$user = Auth::user();"
I tried adding it to a __construct in the BaseController but that didn't seem to work, Undefined $user variable error. Any thoughts? I know it's possible.
Has shown in other answers, you can:
set it as a property of your base controller (I don't recommend this, it just adds clutter)
create a custom helper (see below)
just do \Auth::user()
Another technique, you can use auth() helper:
auth()->user()
It is even documented.
According to this PR, there is no plan to add an user() helper in Laravel.
If you consider it is worth adding this helper to your project, here is the code:
function user()
{
// short and sweet
return auth()->user();
// alternative, saves one function call!!
return app('Illuminate\Contracts\Auth\Guard')->user();
}
I do not see why something like this would not work:
public function __construct()
{
$this->user = \Auth::user();
\View::share('user', $this->user);
}
This way it's available to all your views and as long as your controllers are extending BaseController, it's available in all your controllers as well with $this->user.
Why don't you use a helper function. It won't give you a $user variable, but it can give you a more cleaner way to accomplish this.
In your helpers.php add a function:
function getUser(){
return Auth::user();
}
In all other places (controller, views, models) call: getUser(). That's it.
If you don't have any helper file for your functions yet. You can follow this link to create one.
I don't see whats the issue with using Auth::user(), this class is using singleton pattern which means that if the Auth::user() is already initialized it won't check again against the database especially when most of the code shouldn't even be inside a controller so basically you would still need to do Auth::user() inside your services, models, form requests, etc..
abstract class Controller extends BaseController {
protected $user;
use ValidatesRequests;
public function ___construct(){
$this->user = \Auth::user();
}
}

Laravel Use Eloquent model from custom UserServiceProvider

I'm new to Laravel, just toying with it and getting my head back into MVC.
I'm trying to make my own User auth provider (custom password hashing) as a service that implements the UserProviderInterface within Laravel.
Inside app/controllers/Account.php:
public function postCreate() {
Auth::attempt(Input::all());
}
I have my app routing Auth::attempt through my custom provider class, and passing me the Input::all from the form into a retrieveByCredentials method.
Inside app/services/PasswordHash/PasswordHashUserProvider.php:
public function retrieveByCredentials(array $credentials) {
// Why can't I do this?
//Error: PasswordHash/User not found
User::find($credentials['username']);
dd($credentials);
}
I am lost at this point on how to access my User eloquent models from within this service class. I tried namespaces but had no luck.
The boot method on service providers use the service container to inject dependencies. To that end, you should be able to do the following (not using Facades, but I don't use facades that often).
class PasswordHashUserProvider extends ServiceProvider
{
protected $user;
public function boot(User $user)
{
$this->user = $user;
}
}
You can then access user via $this->user
Source: https://laravel.com/docs/master/providers#the-boot-method

How to make a REST API first web application in Laravel

I want to make an API first application in Laravel. I don't know what is the best approach to do this, I will explain what I am trying to do, but please feel free to give answers how to do this in a different way.
I don't want all my frontend to be written in javascript and parse the JSON output of the API with angular.js or something similar. I want my Laravel application to produce the HTML views. I am trying to go down the road of having two controllers one on for the API and one for the web. For the show User action my routes.php looks like this:
# the web controller
Route::controller('user', 'WebUserController');
# the api controller
Route::group(array('prefix' => 'api'), function() {
Route::resource('user', 'UserController');
});
So /user will take me to WebUserController and /api/user will take me to the UserController. Now I want to put all my logic in the API UserController, and call its actions from the WebUserController. Here is the code for both of them:
class UserController extends BaseController
{
public function show($id)
{
$user = User::find($id);
return Response::json(array('success'=>true,'user'=>$user->toArray()));
}
}
class WebUserController extends UserController
{
public function getView($id)
{
# call the show method of the API's User Controller
$response = $this->show($id);
return View::make('user.view')->with('data', $response->getData());
}
}
In the WebUserController I am able to get the json content of the response with getData(), but I am not able to get the headers and status code (they are protected properties of Illuminate\Http\JsonResponse).
I think that my approach might not be the best, so I am open to suggestions how to make this app.
EDIT: The question how to get the headers and status of the response has been answered by Drew Lewis, but I still think that there might be a better way how to design this
You should utilize the Repository / Gateway design pattern: please see the answers here.
For example, when dealing with the User model, first create a User Repository. The only responsibility of the user repository is to communicate with the database (performing CRUD operations). This User Repository extends a common base repository and implements an interface containing all methods you require:
class EloquentUserRepository extends BaseRepository implements UserRepository
{
public function __construct(User $user) {
$this->user = $user;
}
public function all() {
return $this->user->all();
}
public function get($id){}
public function create(array $data){}
public function update(array $data){}
public function delete($id){}
// Any other methods you need go here (getRecent, deleteWhere, etc)
}
Then, create a service provider, which binds your user repository interface to your eloquent user repository. Whenever you require the user repository (by resolving it through the IoC container or injecting the dependency in the constructor), Laravel automatically gives you an instance of the Eloquent user repository you just created. This is so that, if you change ORMs to something other than eloquent, you can simply change this service provider and no other changes to your codebase are required:
use Illuminate\Support\ServiceProvider;
class RepositoryServiceProvider extends ServiceProvider {
public function register() {
$this->app->bind(
'lib\Repositories\UserRepository', // Assuming you used these
'lib\Repositories\EloquentUserRepository' // namespaces
);
}
}
Next, create a User Gateway, who's purpose is to talk to any number of repositories and perform any business logic of your application:
use lib\Repositories\UserRepository;
class UserGateway {
protected $userRepository;
public function __construct(UserRepository $userRepository) {
$this->userRepository = $userRepository;
}
public function createUser(array $input)
{
// perform any sort of validation first
return $this->userRepository->create($input);
}
}
Finally, create your User web controller. This controller talks to your User Gateway:
class UserController extends BaseController
{
public function __construct(UserGatway $userGateway)
{
$this->userGateway = $userGateway;
}
public function create()
{
$user = $this->userGateway->createUser(Input::all());
}
}
By structuring the design of your application in this way, you get several benefits: you achieve a very clear separation of concerns, since your application will be adhering to the Single Responsibility Principle (by separating your business logic from your database logic) . This enables you to perform unit and integration testing in a much easier manner, makes your controllers as slim as possible, as well as allowing you to easily swap out Eloquent for any other database if you desire in the future.
For example, if changing from Eloquent to Mongo, the only things you need to change are the service provider binding as well as creating a MongoUserRepository which implements the UserRepository interface. This is because the repository is the only thing talking to your database - it has no knowledge of anything else. Therefore, the new MongoUserRepository might look something like:
class MongoUserRepository extends BaseRepository implements UserRepository
{
public function __construct(MongoUser $user) {
$this->user = $user;
}
public function all() {
// Retrieve all users from the mongo db
}
...
}
And the service provider will now bind the UserRepository interface to the new MongoUserRepository:
$this->app->bind(
'lib\Repositories\UserRepository',
'lib\Repositories\MongoUserRepository'
);
Throughout all your gateways you have been referencing the UserRepository, so by making this change you're essentially telling Laravel to use the new MongoUserRepository instead of the older Eloquent one. No other changes are required.
You should be use Repository for this design.
Example -
//UserRepository Class
class UserRepository {
public function getById($id)
{
return User::find($id);
}
}
// WebUser Controller
class WebUserController extends BaseController {
protected $user;
public function __construct(UserRepository $user)
{
$this->user = $user;
}
public function show($id)
{
return View::make('user.view')->with('data', $this->user->getById($id));
}
}
// APIUser Controller
class UserController extends BaseController {
protected $user;
public function __construct(UserRepository $user)
{
$this->user = $user;
}
public function show($id)
{
$data =>$this->user->getById($id);
return Response::json(array('success'=>true,'user'= $data->toArray()));
}
}
Checkout Laravel's RESTful controllers:
http://laravel.com/docs/controllers#restful-controllers
Their docs do a pretty good job.
But even better is this tutorial:
http://code.tutsplus.com/tutorials/laravel-4-a-start-at-a-restful-api-updated--net-29785
This is a video by Jeffrey Way he is one of the better Laravel developers. In this tutorial he is connecting a BackboneJS application to a RESTful service that he sets up in Laravel. It doesn't get any better then this. I can write you a lot of boilerplate, but just learn it by watching a nice video and having a coffee. ;)
https://www.youtube.com/watch?v=uykzCfu1RiQ
I have a response to the problem you are having with the Response.
You can get the headers, status code and data from the Response.
// your data
$response->getData();
// the status code of the Response
$response->getStatusCode();
// array of headers
$response->headers->all();
// array of headers with preserved case
$response->headers->allPreserveCase();
$response->headers is a Symfony\Component\HttpFoundation\ResponseHeaderBag which inherits from Symfony\Component\HttpFoundation\HeaderBag
I would also recommend using a repository.
Attempting to call one controller from another would be falling into a pattern called HMVC (Hierarchical model–view–controller).
This means that your entire application relies on lower modules.
In this case, your API would serve as a repository for your data (which isn't the worst thing in the world at first).
However, when you then modify the structure of how data is returned in your API, everything else relying on it would have to know how to respond.
Say you wanted to have authorization checks to see if a logged in user should be able to see the details of a returned user and there was an error.
In the API, you would return a Response object with a 403 forbidden code and some meta data.
Your HTML controller would have to know how to handle this.
Contrast this to a repository which could throw an exception.
public function findById ($id)
{
$user = User::findOrFail($id);
if (Auth::user->hasAccessTo($user)) {
return $user;
} else {
throw new UnauthorizedAccessException('you do not have sufficient access to this resource');
}
}
And your API controller would look more like this:
public function show($id)
{
try {
return $this->user->findById($id);
} catch (UnauthorizedAccessException $e) {
$message = $e->getMessage();
return Response::json('403', ['meta' => ['message' => $message]]));
}
}
Your HTML controller would then look like this:
public function show($id)
{
try {
$user = $this->user->findById($id);
} catch (UnauthorizedAccessException $e) {
Session::flash('error', $e->getMessage());
// Redirect wherever you would like
return Response::redirect('/');
}
}
This gives you very reusable code and let's you change your controller implementations independently without worry of changing the other's behavior.
I wrote more on how to implement the repository pattern in this post: you can ignore the interface and skip right to the implementations if you would like.

Categories