Laravel 5.2 Using Associate to Update a BelongsTo Relationship - php

I'm using Route Model Binding to get a User instance then update it if validation passes, and then update the associated belongs to relationship between User and Profile, but I keep getting an error. The update occurs on the User, but fails on updating the Profile. From what I've understood from the docs this appears to be correct. I can access Profile data using $user->profile so the relationship appears to be okay in the User and UserProfile models.
Can anyone see what is wrong with this controller action:
public function update(Request $request, User $user)
{
$this->validate($request, [
'username' => 'required|max:32|unique:users',
'email' => 'required|email|max:128|unique:users',
'first_name' => 'required',
'last_name' => 'required',
'phone_number' => 'regex:/^([0-9\s\-\+\(\)\.]*)$/',
]);
$user->update($request->all());
$profile = new UserProfile($request->all());
// Also tried:
//$profile = UserProfile::where(['user_id' => $user->id])->first();
$user->profile()->associate($profile);
$user->save();
return response()->json([
'message' => trans('user.updated'),
]);
}
Error
BadMethodCallException in Builder.php line 2161:
Call to undefined method Illuminate\Database\Query\Builder::associate()
User Model Relationships
/**
* A user has-one profile.
*
* #return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function profile()
{
return $this->hasOne('App\UserProfile');
}
UserProfile Model Relationship
/**
* A user profile belongs to a user.
*
* #return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function user()
{
return $this->belongsTo('App\User');
}
Solution
$user->fill($request->all())->save();
$profile = UserProfile::where('user_id', $user->id)->first();
$profile->fill($request->all());
$user->profile()->save($profile);

You must retrieve or create new profile entity first and put it in $profile. Also, you have One-to-one relation here, so you should save your user's profile like this:
$user->profile()->save($profile);

Change your code to this:
public function update(Request $request, User $user)
{
$this->validate($request, [
'username' => 'required|max:32|unique:users',
'email' => 'required|email|max:128|unique:users',
'first_name' => 'required',
'last_name' => 'required',
'phone_number' => 'regex:/^([0-9\s\-\+\(\)\.]*)$/',
]);
$profile = UserProfile::create($request->all());
$user->profile()->associate($profile);
$user->save();
return response()->json([
'message' => trans('user.updated'),
]);
}

Related

Best way to validate related data update in laravel 8

I have two models:
User:
id
Post:
id
user_id
belongsTo User
text
I want to update a Post record, which way to validate user_id is better?
Number 1
Pass user_id to controller with get method like:
Route::post('/post/edit/{user_id}/{post_id}', 'PostController#update')->name('post.update');
and validate it in controller:
public function update($user_id, $post_id, Request $request){
abort_if(!User::first($user_id), 404);
$request->validate([
'text' => 'string|...',
]);
Post::findOrFail($post_id)->update([
'user_id' => $user_id,
'text' => $request->text,
]);
Number 2
Pass user_id with hidden field with POST method like:
view:
<input type="hidden" name="user_id" value="{{ $user_id }}>
Routing:
Route::post('/post/edit/{post_id}', 'PostController#update')->name('post.update');
Controller:
public function update($post_id, PostUpdate $request){
Post::findOrFail($post_id)->update([
'user_id' => $request->user_id,
'text' => $request->text,
]);
}
PostUpdate request:
public function rules()
{
return [
'user_id' => 'required|exists:users,id',
'text' => 'string',
];
}
Which way is proper or better?
If the user is the authentified user, use the Auth::class
public function update($post_id, Request $request){
$request->validate([
'text' => 'string|...',
]);
Post::findOrFail($post_id)->update([
'user_id' => \Auth::id(),
'text' => $request->text,
]);
}
You can even make sure the post owner is the current user (which is better).
public function update($post_id, Request $request){
$request->validate([
'text' => 'string|...',
]);
Post::where('user_id', \Auth::id())->findOrFail($post_id)->update([
'text' => $request->text,
]);
}
//or (and this is the proper way to do it. It does the same amout of queries and is way easier to read/maintain).
public function update($post_id, Request $request){
$request->validate([
'text' => 'string|...',
]);
$user = \Auth::user();
$post = $user->posts()->findOrFail($post_id);
$post->text = $request->text;
$post->save();
}
No need for the user validation since all of that is handled at login and the rest is maintained through middleware.
Route:
Route::post('/post/edit/{post_id}', 'PostController#update')->middleware('auth')->name('post.update');
I suggest another way for you. Work with middlewares.
In the gate you can check the type of user, so users should be able to edit if they are the owner. In this case you can add some other rules just in one file and user everywhere you want.
In app/Providers/AuthServiceProvider.php define your gate:
Gate::define('update-post', function ($user, \App\Post $post) {
return $user->id === $post->user_id;
});
Then in your routes web.php:
Route::get('/edit/{post}', 'PostController#edit')
->name('edit_post')
->middleware('can:update-post,post');
Route::post('/edit/{post}', 'PostController#update')
->name('update_post')
->middleware('can:update-post,post');

How to stop auto login after registration in Laravel 8 breeze

Hi I am fairly new to Laravel framework. I am trying to create a separate controller replicating the registeredusercontroller.php in laravel breeze to have a function for admin user to be able to add new users. But I am having trouble with auto login and as soon as I add a new user it automatically logs in to the new user. How can I stop this from happening. I saw posts stating to remove
$this->guard()->login($user);
but when I see the app/Auth/registeredusercontroller.php I don't see that line.
public function store(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|confirmed|min:8',
]);
Auth::login($user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]));
$user->attachRole('user');
event(new Registered($user));
return redirect(RouteServiceProvider::HOME);
}
how can I stop the auto login after registration.
Any help is greatly appreciated.
You can do this proper way like using custom request handler.
The parameter you need is name, email, password.
So add CreateUserRequest above app/http/request
<?php
namespace App\Http\Requests;
use Illuminate\Http\Request;
class CreateUserRequest extends Request
{
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|confirmed|min:8',
];
}
}
And into your controller just do this;
public function store(CreateUserRequest $request) // add your custom request as a parameter
$user = User::create($request)
These codes makes your code structure clear and clean.

How to obtain the user model's attributes in Laravel via class accessor

I am trying to create a customer at the same time as a user is created in the RegisterController (part of the Laravel Auth package), if that user registers as a customer. However, I have tried adding an 'accessor' to the User class by adding the following code to my User model, to access the user_id property, but the accessor does not seem to work as intended. (SEE: https://laravel.com/docs/8.x/eloquent-mutators#defining-an-accessor)
public function getUserIdAttribute($user_id)
{
return $user_id;
}
Then I try to access it directly from the model in the RegisterController with this function:
protected function create(array $data)
{
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'username' => $data['username'],
'password' => Hash::make($data['password']),
'utype' => $data['utype'],
]);
if ($data['utype'] == "customer") {
Customer::create([
'user_id' => $user->user_id,
]);
}
return $user;
}
For some reason, the $user->user_id returns NULL though. What is the issue? Why is the accessor not being applied properly?
With the help of #lagbox, I figured out what the issue was. Turns out, the column name was simply 'id'.

Laravel Socialite For Different Role User

I'm creating a project with two user roles.
I use the role_id field to distinguish roles from users.
When a user registers an account via manual input I make use of the hidden input to store the role_id.
But how can I save the role_id of users when they register for an account using a google account?
This is my controller
public function redirect($provider)
{
return Socialite::driver($provider)->redirect();
}
public function callback($provider)
{
$getInfo = Socialite::driver($provider)->user();
$user = $this->createUser($getInfo, $provider);
auth()->login($user);
return redirect()->to('/');
}
function createUser($getInfo, $provider){
$user = User::where('provider_id', $getInfo->id)->first();
if(!$user) {
$user = User::create([
'name' => $getInfo->name,
'email' => $getInfo->email,
'provider' => $provider,
'provider_id' => $getInfo->id,
'email_verified_at' => Carbon\Carbon::now()
]);
}
return $user;
}
This is my route
Route::get('/auth/redirect/{provider}/', 'LoginUserController#redirect');
Route::get('/callback/{provider}/', 'LoginUserController#callback' );
My View
a href="{{ url('/auth/redirect/google') }}" class="link-custom">{{ __('Google Account') }}</a>
It is not entirely clear how you save the user role. Is it in a different table? On which basis do you assign roles to users? Nevertheless, you could always adjust the createUser method and save the other related info after creating the user. For example:
private function createUser($getInfo, $provider){
$user = User::where('provider_id', $getInfo->id)->first();
if(!$user) {
$user = User::create([
'name' => $getInfo->name,
'email' => $getInfo->email,
'provider' => $provider,
'provider_id' => $getInfo->id,
'email_verified_at' => Carbon\Carbon::now()
]);
// Here you can save other stuff,
// E.g. the user role supposing it is saved in a different table
UserRole::create([
'user_id' => $user->id,
'role_id' => 1 // Assigned role id
]);
}
return $user;
}
Two things you need to consider:
Don't use hidden field to store a role_id. What's up with an user edit the code inline and change the role_id?
You should to set up a default value for the role_id field, then all the users will have that default role.

Laravel get id of latest inserted user

I'm using the Laravel Auth to make users able to register. What I'm now trying is: After users register (if they have a special role selected), there is another row inserted into another table (then users) which holds the relating user id. This is the code for it:
<?php
namespace App\Http\Controllers\Auth;
use App\User;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;
use App\Complaint;
class RegisterController extends Controller
{
/*
|--------------------------------------------------------------------------
| Register Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users as well as their
| validation and creation. By default this controller uses a trait to
| provide this functionality without requiring any additional code.
|
*/
use RegistersUsers;
/**
* Where to redirect users after login / registration.
*
* #var string
*/
protected $redirectTo = '/home';
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest');
}
/**
* Get a validator for an incoming registration request.
*
* #param array $data
* #return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|max:255',
'email' => 'required|email|max:255|unique:users',
'password' => 'required|min:6|confirmed',
'username' => 'required|unique:users',
'role' => 'required'
]);
}
/**
* Create a new user instance after a valid registration.
*
* #param array $data
* #return User
*/
protected function create(array $data)
{
$user = new User;
$user->name = $data['name'];
$user->email = $data['email'];
$user->username = $data['username'];
$user->password = bcrypt($data['password']);
$user->role = $data['role'];
$user->templateURL = "";
/*$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'username' => $data['username'],
'password' => bcrypt($data['password']),
'role' => $data['role'],
'templateURL' => ""
]);*/
$user->save();
if($data['role'] == 'Verkäufer'){
$complaintRow = Complaint::create([
'user_id' => $user->id,
'complaintCount' => 0
]);
}
switch($data['role']){
case 'Käufer':
$user->attachRole(2);
break;
case 'Verkäufer':
$user->attachRole(3);
break;
default:
$user->attachRole(2);
break;
}
return $user;
}
}
But it's not working correctly, the user is inserted as well as a row for the complaints, but somehow $user->id seems to be null, the column always has user_id set to 0. Any ideas why this could be like this?
EDIT: I got it working now... It was actually not the code I posted, I just didn't make the user_id field fillable in the complaint table, that's why there was 0 in always, because 0 was the default value, so it just didn't set it.
Thanks all for the answers anyway.
As per Laravel Eloquent ORM, $user->id will return the Id of user.
If you are getting null, then there might be error in saving. (https://stackoverflow.com/a/21084888/6628079)
Try printing $user after saving it.
UPDATE:
It would be better if you add data in complaint table if user is saved successfully.
protected function create(array $data)
{
$user = new User;
$user->name = $data['name'];
$user->email = $data['email'];
$user->username = $data['username'];
$user->password = bcrypt($data['password']);
$user->role = $data['role'];
$user->templateURL = "";
/*$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'username' => $data['username'],
'password' => bcrypt($data['password']),
'role' => $data['role'],
'templateURL' => ""
]);*/
if ($user->save()) {
if ($data['role'] == 'Verkäufer') {
$complaintRow = Complaint::create([
'user_id' => $user->id,
'complaintCount' => 0
]);
}
switch ($data['role']) {
case 'Käufer':
$user->attachRole(2);
break;
case 'Verkäufer':
$user->attachRole(3);
break;
default:
$user->attachRole(2);
break;
}
return $user;
} else {
// Your code if user doesn't save successfully.
}
}
This is because, Eloquent save method bool but not the instance of newly created Entity. For confirmation checkout this link: https://laravel.com/api/5.3/Illuminate/Database/Eloquent/Model.html
So, if you want to get the newly created instance you can either user create method or make another query to get newly inserted instance. First one is better ans easy. Here is how you can do it:
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'username' => $data['username'],
'password' => bcrypt($data['password']),
'role' => $data['role'],
'templateURL' => ""
]);
Now, you have $user variable containing User instance. But to do this you need to consider fillable/guared issue in your model. I mean, in your model you have to add the following line:
protected $fillabe = ['name', 'email', 'username', 'password', 'role', 'templateURL']
Is column id exists?
Try to set $primaryKey = 'id' on model (user).
After $user->save you can access to id like $user->id
Or after $user->save try to get max id from your table.
$user = User::select('id')->max('id')->first();
Try this:
$lastId = User::create($userData)->id;
$user->id will tell you created user's ID right after using save() method.
You did all most correct you just need to change,
when you are saving user object it will return saved user object so just grab that object and use in furcher conditions.
$saved_user = $user->save();
if(!empty($saved_user)){
if($data['role'] == 'Verkäufer')
{
$complaintRow = Complaint::create([
'user_id' => $saved_user->id,
'complaintCount' => 0
]);
}
} else {
throw new error(500);
}
Hope this will help thanks :)

Categories