How to "Refresh" the User object in Laravel? - laravel

In Laravel you can do this:
$user = Auth::user();
Problem is, if I do changes on items on that object, it will give me what was there before my changes. How do I refresh the object to get the latest values? I.e. To force it to get the latest values from the DB?

You can update the cache object like this.
Auth::setUser($user);
for Example
$user = User::find(Auth::user()->id);
$user->name = 'New Name';
$user->save();
Auth::setUser($user);
log::error(Auth::user()->name)); // Will be 'NEW Name'

[This answer is more appropriate for newer versions of Laravel (namely Laravel 5)]
On the first call of Auth::user(), it will fetch the results from the database and store it in a variable.
But on subsequent calls it will fetch the results from the variable.
This is seen from the following code in the framemwork:
public function user()
{
...
// If we've already retrieved the user for the current request we can just
// return it back immediately. We do not want to fetch the user data on
// every call to this method because that would be tremendously slow.
if (! is_null($this->user)) {
return $this->user;
}
...
}
Now if we make changes on the model, the changes will automatically be reflected on the object. It will NOT contain the old values. Therefore there is usually no need to re-fetch the data from the database.
However, there are certain rare circumstances where re-fetching the data from the database would be useful (e.g. making sure the database applies it's default values, or if changes have been made to the model by another request). To do this run the fresh() method like so:
Auth::user()->fresh()

Laravel does do that for you, HOWEVER, you will not see that update reflected in Auth::user() during that same request. From /Illuminate/Auth/Guard.php (located just above the code that Antonio mentions in his answer):
// If we have already retrieved the user for the current request we can just
// return it back immediately. We do not want to pull the user data every
// request into the method because that would tremendously slow an app.
if ( ! is_null($this->user))
{
return $this->user;
}
So if you were trying to change the users name from 'Old Name' to 'New Name':
$user = User::find(Auth::user()->id);
$user->name = 'New Name';
$user->save();
And later in the same request you try getting the name by checking Auth::user()->name, its going to give you 'Old Name'
log::error(Auth::user()->name)); // Will be 'Old Name'

A little late to the party, but this worked for me:
Auth::user()->update(array('name' => 'NewName'));

Laravel already does that for you. Every time you do Auth::user(), Laravel does
// First we will try to load the user using the identifier in the session if
// one exists. Otherwise we will check for a "remember me" cookie in this
// request, and if one exists, attempt to retrieve the user using that.
$user = null;
if ( ! is_null($id))
{
$user = $this->provider->retrieveByID($id);
}
It nulls the current user and if it is logged, retrieve it again using the logged id stored in the session.
If it's not working as it should, you have something else in your code, which we are not seeing here, caching that user for you.

Related

The Laravel $model->save() response?

If you are thinking this question is a beginner's question, maybe you are right. But really I was confused.
In my code, I want to know if saving a model is successful or not.
$model = Model::find(1);
$model->attr = $someVale;
$saveStatus = $model->save()
So, I think $saveStatus must show me if the saving is successful or not, But, now, the model is saved in the database while the $saveStatus value is NULL.
I am using Laravel 7;
save() will return a boolean, saved or not saved. So you can either do:
$model = new Model();
$model->attr = $value;
$saved = $model->save();
if(!$saved){
//Do something
}
Or directly save in the if:
if(!$model->save()){
//Do something
}
Please read those documentation from Laravel api section.
https://laravel.com/api/5.8/Illuminate/Database/Eloquent/Model.html#method_getChanges
From here you can get many option to know current object was modified or not.
Also you can check this,
Laravel Eloquent update just if changes have been made
For Create object,
those option can helpful,
You can check the public attribute $exists on your model
if ($model->exists) {
// Model exists in the database
}
You can check for the models id (since that's only available after the record is saved and the newly created id is returned)
if(!$model->id){
App::abort(500, 'Some Error');
}

Laravel Auth::User() performance

I have a question that whenever we call Auth::User() then its execute the query to fetch record or it have a saved instance?
Example 1
echo Auth::User()->name;
echo Auth::User()->email;
echo Auth::User()->phone;
Example 2
$userInfo=Auth::User();
echo $userInfo->name;
echo $userInfo->email;
echo $userInfo->phone;
Which one should be used performance wise?
Answer and example
Call to the database will be made only the first time you call Auth::user(), after that Laravel will store the user data and each call after that will get the stored instance rather then query the database again.
You can take a look at the vendor\laravel\framework\src\Illuminate\Auth\SessionGuard.php file under user() method. This is the code I copied from my current project which uses Laravel 7.x and this is the function called by Auth::user().
/**
* Get the currently authenticated user.
*
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function user()
{
if ($this->loggedOut) {
return;
}
// If we've already retrieved the user for the current request we can just
// return it back immediately. We do not want to fetch the user data on
// every call to this method because that would be tremendously slow.
if (! is_null($this->user)) {
return $this->user;
}
$id = $this->session->get($this->getName());
// First we will try to load the user using the identifier in the session if
// one exists. Otherwise we will check for a "remember me" cookie in this
// request, and if one exists, attempt to retrieve the user using that.
if (! is_null($id) && $this->user = $this->provider->retrieveById($id)) {
$this->fireAuthenticatedEvent($this->user);
}
// If the user is null, but we decrypt a "recaller" cookie we can attempt to
// pull the user data on that cookie which serves as a remember cookie on
// the application. Once we have a user we can return it to the caller.
if (is_null($this->user) && ! is_null($recaller = $this->recaller())) {
$this->user = $this->userFromRecaller($recaller);
if ($this->user) {
$this->updateSession($this->user->getAuthIdentifier());
$this->fireLoginEvent($this->user, true);
}
}
return $this->user;
}
Debugbar options
Also as the comment before me pointed out, it is good to download Debugbar for Laravel https://github.com/barryvdh/laravel-debugbar. It will enable you to take a look into queries being executed, views being rendered, requests being sent, and much more.
Other option is Laravel's native solution https://laravel.com/docs/8.x/telescope. I have never personally used it and IMO the first one is simpler to use.
Notes on good practice
Although both examples will essentially do the same thing, I think it is much better to use the second example. Not because of the performance, but rather to make your code readable in future. If you define the user only one time and assign Auth::user() result to it, in future it will be more obvious what it is, and plus, you can easily change what are you assigning to the $userInfo variable (maybe you want to get user from another guard in future, etc.) without having to change it on N places throughout the code.

Laravel 7 loginUsingId not persisting / causing log out

I am trying to create a "Shadow" user feature, it's basically just to allow admins to log in as another user to use the system as the "shadowed user" would.
I've used Auth::loginUsingId before but i can't figure out why the below isn't working.
public function shadowUser($id, Request $request){
$user = User::query()->find($id);
$previousUserId = $request->user()->id;
Session()->flush();
Session()->put('shadow.user.id', $previousUserId);
$shadowedUser = Auth::loginUsingId($user->id);
dump(Auth::check());
return redirect()->route('home');
}
If I dump out the $shadowedUser it shows the correct user and the Auth::check() returns true.
I have also tried these but they made no difference: Auth::loginUsingId(1, true);, Auth::guard($guard)->loginUsingId($user->ID); and Auth::login($user, true);
There's no crazy middleware just laravel's defaults.
I've tried a few things like removing the session flush but it always just logs me out.
I found that Laravel sessions are a bit funky in this scenario, it looks like you're trying to log someone in when the session is already active, log the current user out, flush the session then log in the new user, this way it will tell Laravel that this is a new user signing in and reset the session.
In your script, once you've logged the user out, flushed the session and logged the new user in, add the previous users id to the new session otherwise it will get deleted then you'll be able to see the previous user who was logged in, or the person shadowing you in this case.
public function shadowUser($id, Request $request){
$user = User::query()->find($id);
$previousUserId = $request->user()->id;
Auth::logout($guard);
Session()->flush();
Auth::loginUsingId($user->id);
Session()->put('shadow.user.id', $previousUserId);
dump(Auth::check());
return redirect()->route('home');
}

Laravel - can't get model after deleting related model records

I have a function postShippingmethods in which I delete, update or create new shipping methods depending what the user specified in the form. It works like it should , except that if any methods are deleted, I get empty result when trying to retrieve the user model with the updated methods.
To delete methods, I first get the user:
$user = \App\User::where('id',$id)->with(['shipping_profiles' => function($query) {
$query->where('default', 1);
}, 'shipping_profiles.methods' ])->first();
I compare the stored methods to the ones in the request; if any are missing , they should be deleted.
This code does the deletion:
foreach($non_included_ids as $id){
$method = \App\ShippingMethod::find($id);
$method->suppliers()->detach();
$method->delete();
}
Then I get the user once again, to get the updated data:
$user = \App\User::where('id',$id)->with(['shipping_profiles' => function($query) {
$query->where('default', 1);
}, 'shipping_profiles.methods' ])->first();
^^ this works well if nothing was deleted, but for some reason if something was deleted, the above code will return nothing. And when I try to use the $user to retrieve data I of course get "Trying to get property of non object".
So can anyone explain why this happen and what I should do to get the user with the updated data?
Use a different variable name in the loop:
foreach($non_included_ids as $non_included_id){
Otherwise, the loop changes the value of $id and \App\User::where('id',$id) fetches the wrong user.

How to handle data before delete from model in Laravel?

I have the following method:
public function destroy($id)
{
$id = \JWTAuth::parseToken()->authenticate();
$offer = Offer::findOrFail($id);
$offer->delete();
return response()->json(["offer" => $offer]);
}
How handle data before deleting? I need to check if user has permit to delete data or not
When you use the authenticate() method, the user model is retrieved so it means the id you have is not an id but a User. Have you checked the documentation of JWT Because first and foremost you have to retrieve the user and this is sufficient:
$user = \JWTAuth::parseToken()->authenticate();
Then if you have a field for example in your users table to tell if the user have the right say admin which can be 1 or 0 then you can do the following:
if($user->admin == 1)
{
$offer = Offer::findOrFail(1); //say id
$offer->delete();
return response()->json(["offer" => $offer]);
}
return response()->json(['error' => 'you dont have the right to delete this'], 403);
Just a little scratch on the idea, but my best advice is to do some searches on how JWT is implemented, I am pretty sure you will find tons of them online.
I would recommend using the Model's delete event:
https://github.com/laravel/framework/blob/5.2/src/Illuminate/Database/Eloquent/Model.php#L1122
and handle it.
This will guarantee that if you use the delete method on a model, you always check permissions.

Resources