Laravel User Authentication Scaffolding -> Saving data into two tables - php

I am relatively new to laravel and have a project that requires a bit of manual configuration.
Background:
Working with Authentication Scaffolding (handles the user
registration and login)
I have two tables: Profile and Users.
All Users have one Profile.
But not all Profiles have a user.
Setup:
Profile table => id, name, avatar, etc.
User Table => profile_id, email, password
Since the Laravel Auth (Scaffold) handles the Registration and Login. I am trying to save data into the Profile table before saving the user table.
protected function create(array $data)
{
$profile = Profile::create
([
'slug' => $data['name'],
'name' => $data['name'],
]);
$user = User::create
([
'profile_id' => $profile->id,
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
return $user;
}
But this is not working. And the error message is telling me there is no profile_id assigned in the query.
What am I doing wrong?

How are your models set up? If Eloquent and your DB is set up correctly you can do this:
$profile = new Profile;
$profile->slug = $data['name'];
$profile->name = $data['name'];
$profile->save();
// At this point now you'll be able to get an ID of the profile
$user = new User;
$user->profile_id = $profile->id;
$user->name = $data['name'];
$user->email = $data['email'];
$user->password = bcrypt($data['password']);
$user->save();
return $user;

Related

How can I fetch user ID after overriding it with different primary key in Eloquent ORM?

User model is returning id=null, while debug I found out the the reason behind this issue is that in my User model I override the $primary_key with a custom one
User Model
class User extends Authenticatable
{
use Notifiable;
// Set the primary key to the generated version instead of the regular ID
protected $primaryKey = 'user_code';
// Set the key type
protected $keyType = 'string';
// Diable the auto-increment option
public $incrementing = false;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'user_code',
'fname',
'mname',
'lname',
'email',
'dob',
'age',
'gender',
'insurance_number',
'ssn',
'avatar',
'is_active',
'userable_id',
'userable_type',
];
}
I have the following code that generate a new user_code that uses the id
$user = new User;
$user = $user->create([
'fname' => $request->fname,
'lname' => $request->lname,
'email' => $request->email,
]);
// Save the user in the DB
// Generate a usercode for the newely created user
$userCode = "ur" . date('y') . date('m') . $user->id;
Users Migration:
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('user_code')->unique()->nullable();
$table->string('fname')->default('tbd');
$table->string('mname')->default('tbd');
$table->string('lname')->default('tbd');
$table->string('email')->unique()->nullable();
$table->date('dob')->default('1000-01-01');
$table->integer('age')->default(0);
$table->string('gender')->default('tbd');
$table->integer('insurance_number')->default(0);
$table->integer('ssn')->default(0);
$table->string('avatar')->default('tbd');
$table->boolean('is_active')->default(false);
$table->string('userable_code')->default('tbd');
$table->string('userable_type')->default('tbd');
$table->timestamp('email_verified_at')->nullable();
$table->rememberToken();
$table->timestamps();
});
$user->id is returning null, why such behavior is happening?
You've set $user as a new model instance:
$user = new User;
But then you're trying to create a new user from that instance, that won't work:
$user = $user->create([ ...
Since that doesn't work, you're not really saving anything to the DB, and you won't get an ID.
The second part of your problem is (as #TimLewis pointed out in the comments) that you are trying to create and save a model with a blank primary key (user_code). That won't work, so you'll need to work out what the ID is before trying to save it.
// Remove this line:
// $user = new User;
// Find the current highest ID:
$last = User::max('id');
// Your new user will have this ID
$newID = $last->id + 1;
// And just use the normal way to create and save a model:
$user = User::create([
'userCode' => "ur" . date('y') . date('m') . $newID,
'fname' => $request->fname,
'lname' => $request->lname,
'email' => $request->email,
]);
I may not know what you are trying to achieve here, but I'm just assuming that this is a very special use case.
Try this:
// Notice how we are using the User as a class, not instantiating it.
$user = User::create([
'fname' => $request->fname,
'lname' => $request->lname,
'email' => $request->email,
]);
// Save the user in the DB
// Generate a usercode for the newely created user
$userCode = "ur" . date('y') . date('m') . $user->id;
This assumes that your id column in your database table is still INCREMENTS and PRIMARY KEY

How to save multiple related models at the same time

In my RegistrationController#store, I am doing the following:
Create a user (User: hasMany() Points, hasOne() Profile, belongsToMany() Role)
Attach a role (Role: belongsToMany() User)
Create a profile (Profile: belongsTo() User)
Set initial score to 0 (Points: belongsTo() User)
I realized if one of these steps fails, for example if due to slow connectivity the profile is somehow not created, my app will break. Here is my store method:
public function store(){
$profile = new Profile();
$points = new Points();
$this->validate(request(),[
'name' => 'required',
'username' => 'required|unique:users',
'email' => 'required|email|unique:users',
'password' => 'required|confirmed',
'role_id' => 'required|between:1,2'
]);
$user = User::create([
'name' => request('name'),
'username' => request('username'),
'email' => request('email'),
'password' => bcrypt(request('password'))
]);
$role_student = Role::where('title', 'Student')->first();
$role_parent = Role::where('title', 'Parent')->first();
if(request()->input('role_id') == 1){
$user->roles()->attach($role_student);
} else if(request()->input('role_id') == 2){
$user->roles()->attach($role_parent);
}
$profile->user_id = $user->id;
$profile->date_of_birth = Carbon::createFromDate(request('year'),request('month'),request('day'));
$profile->institution = request('institution');
$profile->class = request('class');
$profile->division = request('division');
$profile->photo = 'propic/default.png';
$profile->bio = 'I am a Member!';
$profile->social_link = 'http://facebook.com/zuck';
$profile->save();
auth()->login($user);
\Mail::to($user)->send(new Welcome($user));
$points->updateOrCreateAndIncrement(0);
return redirect()->home();
}
It is worth mentioning that, all of the data is coming from a registration form. How can I execute all these steps at a time (or, in one statement) so that if the statement executes successfully, I will have a perfectly synchronized registration, otherwise, no data will be persisted to the database?
Use transactions.
Before the first DB operation, in your case: User::create, use this:
DB::beginTransaction();
From now on, put all of your db operations in try/catch braces and if any exception happens, use:
DB::rollBack();
And at the end of the function, before return statement, put this:
DB::commit();
If you can have all your operations in the same place, it's easier to do this:
DB::transaction(function () {
//all your db opertaions
}, 2);

Bypassing email = unique:users in laravel

I am using Laravel 5.2, and I am trying to create a dashboard where the user can update his information, but I am facing one problem which is bypassing unique:users in validator.
if the user wants to keep same email, validator gives an error of 'The email has already been taken', also user should not change email to another email which is reserved by another user.
How can I avoid this validation in case if this user is the only user has this email.
my controller function:
public function update(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|max:255',
'email' => 'required|email|max:255|unique:users',
'password' => 'required|min:6|confirmed',
]);
// if fails, return response with errors
if($validator->fails())
return back()->withErrors($validator)->withInput();
$user = Auth::user();
$user->name = $request->input('name');
$user->email = $request->input('email');
$user->password = bcrypt($request->input('password'));
$user->update();
return back()->withInput();
}
Laravel's unique validator can take additional parameters that can help you exclude given ID from the unique check.
The syntax is:
unique:<table>,<column>,<id_to_exclude>
In your case, you'll need the follwing validation rule:
'email' => 'required|email|max:255|unique:users,email,'.$id
Just change your code to:
public function update(Request $request)
{
$id = Auth::user()->id;
$validator = Validator::make($request->all(), [
'name' => 'required|max:255',
'email' => 'required|email|max:255|unique:users'.$id,
'password' => 'required|min:6|confirmed',
]);
// if fails, return response with errors
if($validator->fails())
return back()->withErrors($validator)->withInput();
$user = Auth::user();
$user->name = $request->input('name');
$user->email = $request->input('email');
$user->password = bcrypt($request->input('password'));
$user->update();
return back()->withInput();
}
Why this works? Well the laravel Unique validation searches for the unique value in the table specified. So unique:users searches if the email exists in db. The user id here works as a way to exclude the check for this user.
Also, if you want that email should not be edited, then just exclude it from the request.
$input = $request->excpet(['email']); check docs

Laravel 5.1 bcrypt and login

When I'm registering a new user in the Laravel framework, I'm currently doing it like this,
// Creating a new user
$user = new User;
$user->firstname = $data['firstname'];
$user->lastname = $data['lastname'];
$user->email = $data['email'];
$user->password = bcrypt($data['password']);
$user->save();
This works great, and I am able to login to the application. However, I want the user to have an option to change their passwords in their settings page. Doing this, i used the same technique, using
$newPass = bcrypt($response->new_password);
and updating the user field. However, after doing this, I'm not able to login? I'm using the built in authentication service in laravel for the registration/login.
What am I doing wrong here? and should i do it another way?
I also tried to bcrypt my current password, and i got an completely different hash than the one stored in the database.
This so confusing..
Updated with controller code,
// Validation
$this->validate($request, [
'email' => 'email',
'password' => 'min:8|confirmed',
'current_password' => 'required',
]);
// Getting the user ID
$userId = Auth::id();
// Dummy hack check, change later.
if(!Auth::attempt(['id' => $userId, 'password' => $request->current_password]))
{
return redirect('settings')->with('alert','current password is wrong.');
}
// Everything is validated and ok to proceed
if($request->email)
{
$data['email'] = $request->email;
}
if($request->password)
{
$data['password'] = bcrypt("helloworld");
}
$user = User::where('id',$userId)->update($data);
dd($data);
Dump data for the inputs,
+request: ParameterBag {#40 ▼
#parameters: array:5 [▼
"_token" => "JQIIuCjiKQmbK0X5zCM6czYD1vIoh4PGjLO4qrFm"
"email" => "testing#gmail.com"
"password" => "thisisnewpass"
"password_confirmation" => "thisisnewpass"
"current_password" => "helloworld"
]
}
This code is closer to how Laravel handles resetting a user's password internally. Give it a try.
// Getting the User
$user = Auth::user(); // Gets the currently logged in User
$credentials = [
'id' => $user->id,
'password' => $request->input('current_password')
];
// Make sure current password is correct
if (!Auth::validate($credentials)) { // Checks the User's credentials
return redirect('settings')->with('alert','current password is wrong.');
}
// Change the password
if ($request->has('password')) {
$user->password = bcrypt($request->input('password'));
}
// Save any changes
$user->save();
It looks like you're using the same form to update the User's email address too, so update the code to fit your needs.
Storing the password in an new variable seems to fix the issue (not sure why?) however, this is the code that made everything work,
// Validation
$this->validate($request, [
'email' => 'email',
'password' => 'min:8|confirmed',
'current_password' => 'required',
]);
// Getting the user ID
$userId = Auth::id();
$newPassword = $request->password;
// Dummy hack check, change later.
if(!Auth::attempt(['id' => $userId, 'password' => $request->current_password]))
{
return redirect('settings')->with('alert','Wrong password.');
}
// Everything is validated and ok to proceed
if($request->email)
{
$data['email'] = $request->email;
}
if($request->password)
{
$data['password'] = bcrypt($newPassword);
}
// Getting, and checking if the current password is corrent.
$user = User::where('id',$userId)->update($data);
echo $newPassword . "<br><br>";
dd($data);
If there is any explanations that i don't see, please let me know why. However, it's working now.
For Laravel in year 2017, this is how we roll:
//create a setter method in your controller
public function setPasswordAttribute( $password ) {
if ( $password !== null ) {
if ( is_null(request()->bcrypt) ) {
$this->attributes['password'] = bcrypt($password);
} else {
$this->attributes['password'] = $password;
}
}
}
Check this link they all are talking about placing it in model but it works inside my own controller.

Laravel 5.1 Model Create - Column not found: 1054

I am trying to register a user using User model and calling the create() method and passing array of inputs but getting Column not found: 1054.
public function postRegister(RegisterRequest $request)
{
$user = User::create([
'email' => $request->input('register-email'),
'password' => $request->input('register-password'),
'lastname' => $request->input('register-lastname'),
'firstname' => $request->input('register-firstname'),
]);
}
The keys inside the create() method is my database email, password, lastname, firstname and I added 'register-' in my html inputs because I have another form that use the name 'email and password'
so you can use the create method with array as a parameter, you must declare the columns in the fillable parameter
see: http://laravel.com/docs/5.1/eloquent#mass-assignment
you can also do it this way
$user = new User;
$user->email = $request->input('register-email');
$user->password = $request->input('register-password');
$user->lastname = $request->input('register-lastname');
$user->firstname = $request->input('register-firstname');
$user->save();

Categories