Laravel 5 : Does Auth::user() query the database everytime I use it? - laravel

On the edit profile page for a user, I want to show the existing values of the current logged-in user details like name, email, gender etc. My questions are as follows
Is it recommendable to user Auth::user()->name , Auth::user()->email directly to populate the form fields ? Or shall I create a variable like $user = Auth::user(); in my controller and pass it on to my view to $user like a regular object?
Does using Auth::user(), multiple times on a given view file hit my database each time I use it?
Thanks in advance.

If you look at the SessionGuard.php file in Illuminate\Auth, you'll see the method user() which is used to retrieve the currently authenticated 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.
$user = null;
if (! is_null($id)) {
if ($user = $this->provider->retrieveById($id)) {
$this->fireAuthenticatedEvent($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.
$recaller = $this->getRecaller();
if (is_null($user) && ! is_null($recaller)) {
$user = $this->getUserByRecaller($recaller);
if ($user) {
$this->updateSession($user->getAuthIdentifier());
$this->fireLoginEvent($user, true);
}
}
return $this->user = $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;
}
So, calling the user() multiple times won't make multiple calls to the database.

You'll get only 1 request to database, so using Auth::user() multiple times is not a problem.
I recommend you using Laravel Debugbar as the most comfortable way for app optimization.

Related

Laravel retrieve deleted users multiple times

In my application, I have a button for deleting the account.
This is the function code:
public function deleteAccount()
{
$user = auth()->user();
auth()->logout();
request()->session()->invalidate();
request()->session()->regenerateToken();
$user->delete();
return redirect(route('login'))->with([
'status' => __('Your account has been deleted')
]);
}
I signed in with the same account with the "Remember" option on both Edge and Firefox browsers. When I click "Delete Account" on Edge, Laravel also dismisses me on Firefox because it deleted my account.
The problem is: On Firefox, Laravel is still trying to retrieve my account with Session and Cookies info because it hasn't been updated. And it runs again and again everywhere I use the auth() function.
How to avoid this? Or how do other sessions know that their information is out of date?
(I think it should only check once, like when it finds a user)
Demo code: https://github.com/tungwoodboi/demo-laravel-deleted-user
I found this in the SessionGuard class:
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;
}

Is it necessary to protect destroy method in Laravel from an authenticated user that did not create a post

I have the following middleware in my PostController.
public function __construct()
{
$this->middleware('auth')->except(['index', 'show']);
}
I understand any authenticated user can still edit a post by visiting localhost/posts/{post}/edit so I've protected that by the following code.
public function edit(Post $post)
{
if(auth()->user()->id === $post->user_id){
$categories = Category::all();
return view('edit-post', compact(['post', 'categories']));
} else{
abort(403, 'Unauthorized.');
}
}
Now, I'm wondering is it necessary to protect the destroy method? Is it possible for an authenticated user to delete a post they didn't create in this case? If they can could I kindly know how they can?
My destroy method
public function destroy(Post $post)
{
Storage::disk('public')->delete($post->imagePath);
$post->delete();
return redirect(route('posts.index'))->with('flash', 'Post Deleted Successfully');
}
one easy way to protect all your methods that need authentification is to use relations.
You are sending a post id in the URL by use model injection to preload $post from DB. Avoid that and use the id of the post yourself
public function destroy($postId)
{
$post = auth()->user()->posts()->findOrFail($postId);
Storage::disk('public')->delete($post->imagePath);
$post->delete();
return redirect(route('posts.index'))->with('flash', 'Post Deleted Successfully');
}
The route will return a 404 if the post id is not one of the user owned posts.
Same for the edit
public function edit($postId)
{
$post = auth()->user()->posts()->findOrFail($postId);
$categories = Category::all();
return view('edit-post', compact(['post', 'categories']));
}
As for the change in response code 404 instead of a 403, it is on purpose, since the user is authenticated and you dont want any user to know if another post with random ID that is not his exists or not hence 404. like if he put a non existing post id to delete or edit.
First, you should be read about Laravel policy, which will make your code more clear.
For destroy method,
I will give you an example, you can try it BTW
post_table
id
user_id
title
1
40
Post1
2
50
Post2
If the user id: 40 tries to delete the post witch id is id: 1 there is no problem,
BUT let's say the user knows about the web app and just changes the id in the URL localhost/posts/2/delete, He/She will delete any post without policy.

How to add expiry date condition to login function in laravel 5.2

In laravel 5.2, i want to add the condition so that only users where their expiry date is greater than today's date to login.
protected function getCredentials(Request $request)
{
return ['email' => $request->{$this->loginUsername()}, 'password' => $request->password];
}
The code does not accept adding:
'expires' => gte(Carbon::now())
Any help is appreciated
I don't think this is possible, even in Laravel 5.5. Taking a look at the retrieveByCredentials method in Illuminate\Auth\EloquentUserProvider which is used to get the user from the database, you can see that the query passes simple key/value combinations to the where method on the $query object, which equate to where key = value. This is from 5.5:
public function retrieveByCredentials(array $credentials)
{
if (empty($credentials) ||
(count($credentials) === 1 &&
array_key_exists('password', $credentials))) {
return;
}
// First we will add each credential element to the query as a where clause.
// Then we can execute the query and, if we found a user, return it in a
// Eloquent User "model" that will be utilized by the Guard instances.
$query = $this->createModel()->newQuery();
foreach ($credentials as $key => $value) {
if (! Str::contains($key, 'password')) {
$query->where($key, $value);
}
}
return $query->first();
}
To achieve what you are after I would recommend doing this check after the user has logged in, in your controller for instance:
// Imagine this is the controller method where you're dealing with user logins
public function login(array $credentials)
{
if (! auth()->attempt($credentials)) {
// Handle what happens if the users credentials are incorrect.
}
$user = auth()->user();
if (Carbon::now()->gte($user->expires)) {
// User's account has expired, lets log them out.
auth()->logout();
// Return a redirect with a message or something...
}
// Handle a successful login.
}
I'm not sure if the auth() helper is available in 5.2, but you should be able to use the Auth facade to do the same thing, e.g. Auth::attempt(...).

how to generate error message in laravel

Hello stackoverflow geeks, I'm in my final stages of the laravel learning curve all thanks to you guys.
However, i need to generate a warning message like "You cannot delete a role assigned to a user" every time a user tries to delete a role assigned to a user.
instead it loads a page with an sql error. how to i do it?
And how do i avoid a password that has been already been stored from being hashed again. eg:- $2y$10$p8JwI5P4yE2UFo2.vHP99.0dP2jU7ll/9w73IzUa9/yegKOSTHJWq is always hashed every time i edit a user's information.
Thanks you all who've made learning laravel easy for me by answering in time
code
public function destroy(Request $request,$id)
{
// delete
// $role = Role::find($id);
//$role->delete();
$role = Role::find ($id);
if ($role->users() !=null) {
return redirect()->back()->withInput(['warning' => 'Not allowed']);
}
$role->delete();
// redirect
Session::flash('message', 'Record successfully deleted!');
Session::flash('alert-type', 'success');
return Redirect::to('role');
}
This highly depends on how you want to handle the errors. You can either catch the sql exception and display your custom error OR what is probably better for you is to handle the incoming request, validate it and return an error if validation fails.
Here are the validation docs : https://laravel.com/docs/5.3/validation
You have multiple options on how to validate a request. Simple example to validate a title is unique in the table posts and is maximum 255 chars long:
$this->validate($request, [
'title' => 'required|unique:posts|max:255'
]);
If you cannot find a rule that is helping you simply define your own validation rule https://laravel.com/docs/5.3/validation#custom-validation-rules
Ofcourse you can also do the validation manually. In your request or in your controller (depends on your setup) just check for it
// assuming you want to delete an entry
public function delete(Request $request, $id)
{
$role = App\Role::findOrFail($id);
if ($role->users() != null) {
return redirect()->back()->withInput(['message' => 'Not allowed']);
// now you can output $message
}
$role->delete();
return ...
}

Symfony2 rest and oauth, can't persist session

I'm using FosRestBundle and FosOauthServerBundle for an api with Symfony.
I have a route /login/username/password, in this action i loggin the user manually :
$encoder_service = $this->get('security.encoder_factory');
$encoder = $encoder_service->getEncoder($user);
$encoded_pass = $encoder->encodePassword($password, $user->getSalt());
if ($user->getPassword() == $encoded_pass) {
$token = new UsernamePasswordToken($user, $password, "api", $user->getRoles());
$this->get("security.context")->setToken($token);
$event = new InteractiveLoginEvent($this->get("request"), $token);
$this->get("event_dispatcher")->dispatch("security.interactive_login", $event);
}
If i check in my database after that (i save session in my database) i have a new line, and at the end of my action, if i check $this->getUser() i have the good one.
But after that, in another action, if i check $this->getUser() i have noting... I can't retrieve a user session from an action to another.
Do you have any ideas ?
Ediy: If i check $this->container->get('security.context')->getToken()i have :
{"roles":[{"role":"ROLE_USER"}],"authenticated":true,"attributes":[],"token":"MYTOKEN"}
But i haven't my user..
You should be able to fetch your user from security context. I usually define this as a separate function in the controllers where i need it :
public function getConnectedUser()
{
$user = null;
if ($this->get('session')->has('_security_member') && !is_null($this->get('security.context')->getToken())) {
$user = $this->get('security.context')->getToken()->getUser();
}
return $user;
}
You can then access it from every action of your controller.
$user = $this->getConnectedUser();

Resources