Laravel 5.2 save user IP adress to DB - php

I'm trying save user IP adress after login on website. I'm using laravel 5.2 framework. I got user table and login_ip row. My code looks like that:
$user = User::where('login_ip', Request::getClientIp());
$user->save();
But it does not saving. What i'm doing wrong? Sorry for my bad english :)

If you want to save IP for current user, you can do this:
auth()->user()->update(['login_ip' => Request::getClientIp()]);
This will not create additional query as code in shoieb0101, Amit and Ronald answers.
Don't forget to add login_ip to the $fillable array in User model:
protected $fillable = ['login_ip'];
If you want to save IP for only logged in users and ignore guests, you can do a check:
!auth()->check() ? : auth()->user()->update(['login_ip' => Request::getClientIp()]);

Try
$user = User::find(auth()->user()->id);
$user->login_ip = Request::getClientIp();
$user->save();

//assuming $userid is also requested
$user = User::where('id', $userid);
$user->login_ip = Request::getClientIp();
$user->save();

You can try it as:
auth()->user()->login_ip = Request::getClientIp();
auth()->user()->save();
OR
auth()->user()->save(['login_ip' => Request::getClientIp()]);
Note - It will update the user's login_ip in single query.

You dont have to get logged user form db, all info about your user you have in Auth::user() so:
Auth::user()->login_ip = Request::getClientIp();
Auth::user()->save();
or
Auth::user()->login_ip = $request->ip();
Auth::user()->save();
but you need to have Request $request as parameter of your method.

I am probably stating the obvious, but your first line...
$user = User::where('login_ip', Request::getClientIp());
... returns an Eloquent query builder, right?
So, a save() on this will never work?
$user = User::where('login_ip', Request::getClientIp())->first();
... will return an actual User (if in the DB), which makes save() also possible.
Or did you make a typo in your OP?

Related

Eloquent query not finding user by email

I'm trying to find a user form a database with 'email' = $email and send them an email. When I try to echo the $user, I receive an error that it's not a string.
public function sendEmail($user)
{
Mail::to($user['email'])->Send(new VerifyEmail($user));
}
public function verifyEmail($email)
{
$user = User::where('email',$email);
$this->sendEmail($user ); //mail won't send
return view('auth.email.verifyemail')->with('email', $email);
}
Please help, thank you!
Your code has a few more issues than not being able to find the user, but let's address this first.
Explanation
Calling the where() method on the User::class is returning an instance of Illuminate\Database\Query\Builder and not the record that you are looking for.
To get a collection of records you can call fluently the get() method or in your case you can just get the first result by calling first().
More on the topic: Retrieving Single Models / Aggregates
Solution
$user = User::where('email', '=', $email)->first();
if(!$user) {
// handle the case if no user is found
}
echo $user->email // access the user's email address
Then you can call your sendmail method or whatever you need and pass the $user instance like this $this->sendmail($user)
$thisUser has only a query. You need to execute it to get the user. Try to get the first record:
$thisUser = User::where('email',$email)->first();

How to create a password reset method in Laravel when using the Database User Provider

I've been searching the internet and have yet to find a solution to the following problem...
We currently have a website developed using Laravel which the user table is a remote Microsoft SQL database. The driver in config/auth.php has been set to "database". All is working fine except for the password reset functionality, which we get the following error:
UnexpectedValueException in PasswordBroker.php line 238: User must implement CanResetPassword interface.
From my limited understanding of Laravel (this is my first experiance with Laravel), the Eloquent driver has support for the CanResetPassword functionality, however, this has not been implemented in the Database User Provider by Laravel, hence the error.
So my question is thus, has anyone had a configuration where they have the driver to “Database” and implemented a reset password functionality? All the examples I have seen to date relate to using the Eloquent model, which from my understanding of Laravel is not an option since during the initial development we had to change the driver from Eloquent to database to get the remote Microsoft SQL server working in the first place. Moving the Microsoft SQL database to a local database is not an option I’m afraid.
Alternatively, if anyone has implemented another method of a user resetting their password using an email address I would be open to suggestions.
To write your own password reset logic, you can still use the default migration that comes out of the box or simply create yours. The most important part is the token. Because you are making your own password reset, you have a couple of decisions to make:
Will the token expire?
Can a user use the same token multiple times?
You will need 2 pages, 4 different routes and 4 different functions in the same controller. The 'I forgot my password' page and the 'Reset password' page. In the first page, display a form where you take the user email. And post to the following controller.
//to be added on top as use statements
use DB;
use Auth;
use Hash;
use Carbon;
use App\User;
public function sendPasswordResetToken(Request $request)
{
$user = User::where ('email', $request->email)-first();
if ( !$user ) return redirect()->back()->withErrors(['error' => '404']);
//create a new token to be sent to the user.
DB::table('password_resets')->insert([
'email' => $request->email,
'token' => str_random(60), //change 60 to any length you want
'created_at' => Carbon::now()
]);
$tokenData = DB::table('password_resets')
->where('email', $request->email)->first();
$token = $tokenData->token;
$email = $request->email; // or $email = $tokenData->email;
/**
* Send email to the email above with a link to your password reset
* something like url('password-reset/' . $token)
* Sending email varies according to your Laravel version. Very easy to implement
*/
}
Second part, when the user clicks on the link
/**
* Assuming the URL looks like this
* http://localhost/password-reset/random-string-here
* You check if the user and the token exist and display a page
*/
public function showPasswordResetForm($token)
{
$tokenData = DB::table('password_resets')
->where('token', $token)->first();
if ( !$tokenData ) return redirect()->to('home'); //redirect them anywhere you want if the token does not exist.
return view('passwords.show');
}
Display a page with a form containing 2 inputs
- New password password or whateveer you want
- New password confirmation password_confirm or whatever you want
The form should post to the same URL mapped to the following controller. Why? because we still need to use the token to find the actual user.
public function resetPassword(Request $request, $token)
{
//some validation
...
$password = $request->password;
$tokenData = DB::table('password_resets')
->where('token', $token)->first();
$user = User::where('email', $tokenData->email)->first();
if ( !$user ) return redirect()->to('home'); //or wherever you want
$user->password = Hash::make($password);
$user->update(); //or $user->save();
//do we log the user directly or let them login and try their password for the first time ? if yes
Auth::login($user);
// If the user shouldn't reuse the token later, delete the token
DB::table('password_resets')->where('email', $user->email')->delete();
//redirect where we want according to whether they are logged in or not.
}
Don't forget to add routes
Route::get('password-reset', 'PasswordController#showForm'); //I did not create this controller. it simply displays a view with a form to take the email
Route::post('password-reset', 'PasswordController#sendPasswordResetToken');
Route::get('reset-password/{token}', 'PasswordController#showPasswordResetForm');
Route::post('reset-password/{token}', 'PasswordController#resetPassword');
Note: There might be typos or syntax errors because I did not test this and wrote it here directly from the top of my head. If you see an error/exception, don't panick, read the error and search google.
Just to add to what #eddythedove said.
Instead of str_random(60) I used the Laravel way of creating a token:
private function generateToken()
{
// This is set in the .env file
$key = config('app.key');
// Illuminate\Support\Str;
if (Str::startsWith($key, 'base64:')) {
$key = base64_decode(substr($key, 7));
}
return hash_hmac('sha256', Str::random(40), $key);
}
If you find an error in str_random, make sure you import the module first:
use Illuminate\Support\Str;
Then call with Str::random (60).
$key = config('app.key');
if (Str::startsWith($key, 'base64:')) {
$key = base64_decode(substr($key, 7));
}
$token = hash_hmac('sha256', Str::random(40), $key);
$dbToken = app(Hasher::class)->make($token);
DB::insert('password_resets', [
'email' => 'email#mail.com',
'token' => $dbToken,
]);
This should work in Laravel 8
The default way Laravel handles the Reset Password has a few security issues.
No track record reset password attempts (Delete the token in the table after success attempt is not acceptable)
No expiry date
No token used time
We always better keep track of these security functions.
I have altered the default table like this on my db migration:
public function up()
{
Schema::table('password_resets', function (Blueprint $table) {
$table->bigIncrements('id');
$table->enum('is_used', ['t', 'f'])->default('f');
$table->dateTime('updated_at')->nullable();
});
}
Instead of deleting the record I simply update the table 'is_used' to 't' and updated_at column.
I use following query to filter is_used = 'f' and created on the same day to gather with token.
$data = PasswordReset::where('token', $token)->where('is_used', 'f')
->whereDate('created_at', '>=', Carbon::today()->toDateString())->first();
i try some code after that i get some solution that will work in laravel 8+.
$key = config('app.key');
if (Str::startsWith($key, 'base64:')) {
$key = base64_decode(substr($key, 7));
}
$token = hash_hmac('sha256', Str::random(40), $key);
$dbToken =Hash::make($token);

phalcon 2.0.13 set data with magic setter to related model

I have a problem with phalcon model magic getter and setter.
I want to update like this tutorial :
https://docs.phalconphp.com/en/latest/reference/models.html#storing-related-records
But the thing is my proj is multi module and separated models folder.
So I have to use alias for hasOne and belongsTo
$this->hasOne('user_id', '\Models\UserProfile', 'user_id', array('alias' => 'UserProfile'));
and
$this->belongsTo('user_id', '\Models\CoreUser', 'user_id', array('alias' => 'CoreUser'));
What i want to do is like this.
$CoreUser = new CoreUser();
$user = $CoreUser->findFirst(array(
//...condition here to find the row i want to update
));
$user->assign($newUserData);
$user->setUserProfile($newProfileData);
$user->update();
But above this code only save user data and don't save Profile data at all. (have profile data -- confirmed)
So do you have any idea what the error is? if u know, Please help me or give me a tip.
I got it now.. when assigning like $user->UserProfile = $newUserProfile;
$newUserProfile should b a Model Object.
So my new code is
$CoreUser = new CoreUser();
$user = $CoreUser->findFirst(array(
//...condition here to find the row i want to update
));
$profile = $user->UserProfile; //$profile is now model object which related to $user
//assign new array data
$profile->assign($newProfileData);
$user->assign($newUserData);
/*
* can also assign one by one like
* $user->first_name = $newProfileData['first_name'];
* but cannot be like $profile = $newProfileData or $user->UserProfile = $newProfile
* since it's gonna override it the model with array
*/
$user->UserProfile = $profile;
$user->update(); // it's working now
Thanks to #Timothy for the tips too .. :)
Instead of doing
$profile = $user->UserProfile;
You should instantiate a new UserProfile object
// find your existing user and assign updated data
$user = CoreUser::findFirst(array('your-conditions'));
$user->assign($newUserData);
// instantiate a new profile and assign its data
$profile = new UserProfile();
$profile->assign($newProfileData);
// assign profile object to your user
$user->UserProfile = $profile;
// update and create your two objects
$user->save();
Note that this will always create a new UserProfile. If you want to use the same code to update and create a UserProfile, you can maybe do something like:
// ...
// instantiate a (new) profile and assign its data
$profile = UserProfile::findFirstByUserId($user->getUserId());
if (!$profile) {
$profile = new UserProfile();
}
$profile->assign($newProfileData);
// ...

Yii deleteAll() records with condition

I've set up a log in process where a verification code is generated, and when successful, is then removed. However, i want to make sure that if there's multiple verification codes for the same user, upon log in success, delete all records for that user.
Here's my code
if ($model->validate() && $model->login()) {
//delete this verification code
$verificationCode->delete();
//delete all existing codes for user_id
VerificationCode::model()->deleteAll('user_id',$user->id);
Yii::app()->user->setReturnUrl(array('/system/admin/'));
$this->redirect(Yii::app()->user->returnUrl);
}
However, this seems to just delete all the records, regardless on different user_id's in table. Can anyone see where I'm going wrong?
If you want to delete record with specified attributes, the cleanest way for this is to use deleteAllByAttributes():
VerificationCode::model()->deleteAllByAttributes(['user_id' => $user->id]);
Seems you call the function delete() in wrong way ... try passing value this way
VerificationCode::model()->deleteAll('user_id = :user_id', array(':user_id' => $user->id));
For Yii2, the documented way is to use the function deleteAll().
I normally pass the arguments as an array, like so:
VerificationCode::deleteAll(['user_id' => $user->id]);
Also, you can use the afterDelete method, to make sure that everytime or everywhere someone deletes one verificationCode, your application will also delete every userVerificationCode. Put this in your verificationCode model class:
protected function afterDelete()
{
parent::afterDelete();
VerificationCode::model()->deleteAll('user_id = :user:id',[':user_id' =>$this->user_id]);
//... any other logic here
}
You can use below method for deleting all user_id entry from database:
$criteria = new CDbCriteria;
// secure way for add a new condition
$criteria->condition = "user_id = :user_id ";
$criteria->params[":user_id"] = $user->id;
// remove user related all entry from database
$model = VerificationCode::model()->deleteAll($criteria);
or you can use another method directly in controller action
VerificationCode::model()->deleteAll("user_id= :user_id", [":user_id"
=>$user->id]);
use below method for redirecting a URL
$this->c()->redirect(Yii::app()->createUrl('/system/admin/'));

Models incorrectly try to be replaced instead of update

I try to update the name of a user id=1. I tried following code (v 1.3). But instead of updating, it try to relace the user and var_dump($n->getMessages()); output error relating to not null attributes.
class UserApi extends Phalcon\DI\Injectable{}
$n=new User;
$n->id=1;
$n->name='Tom';
$n->save(); //or even $n->update()
User::findFirst(1)->save(); works. But I need to use a single code for bothe create and update operations.
If you want phalcon to do an update and not an insert you have to load the model from the database before changing its properties.
To use the same code for both the create and update operations simply do the following.
$user = User::findFirst($userId);
if (!$user) {
// Create new user
$user = new User();
$user->id = $userId;
}
// Set/update values
$user->name = $userName;
$user->save();
Hope this helps.

Categories