updateExistingPivot using the primary key instead of the foreign key - laravel

I think this question is similar to this one, however I also think my use case is slightly different.
I am successfully able to update a pivot table using updateExistingPivot when a single user exists; however instead of referencing the FK on the pivot table, I need to reference the id.
I have three tables: users, roles and role_user.
users
id|name|email
roles
id|title
role_user
id|active|user_id|role_id
For example, if I have two users in my application, the users table would look like:
id: 1|name: foo|email: fooexample#email.com
id: 2|name: bar|email: barexample#email.com
My roles table looks like this:
id: 1|title: fizz
id: 2|title: buzz
id: 3|title: bang
Each user is given 3 roles by default. So my role_user table looks like this:
id: 1|active: true|user_id: 1|role_id: 1
id: 2|active: true|user_id: 1|role_id: 2
id: 3|active: true|user_id: 1|role_id: 3
id: 4|active: true|user_id: 2|role_id: 1
id: 5|active: true|user_id: 2|role_id: 2
id: 6|active: true|user_id: 2|role_id: 3
Because updateExistingPivot looks at the FK to decide what to update, everything works great with a single user. However, when I have duplicate user_id and/or role_id FK's things start to fall apart. Instead of looking at the FK, I need to be able to look at the id in my pivot table.
I am passing my roles to a vue component from my controller like this:
// Get the roles(s) that belong to the user.
$roles = $user->roles
->sortByDesc('title')
->toJson();
I am rendering the list on screen, and when a user clicks a role, they can update it's status:
...
async handleRoleClick(event, role) {
console.log('updating role: ', role);
try {
let response = await axios.patch(`/my-path`, {
active: !this.active,
id: role.pivot.id,
});
// data getting passed is correct.
// active: false
// id: 5 <-- the id of the pivot table, not a FK value
if (response.status === 200) {
console.log('response.data', response.data);
this.active = response.data.role.pivot.active;
console.log('active: ', this.active);
} else {
console.error('Error: could not update role. ', response);
}
} catch (error) {
console.error('Error: sending patch request. ', error);
}
},
...
My update method in my controller looks like this:
$attributes = request()->validate([
'active' => 'required',
'id' => 'required',
]);
// Get the authenticated user.
$user = auth()->user();
// Update the roles' status in the pivot table.
$user->roles()->updateExistingPivot($attributes['id'], $attributes);
// Get the role that was just updated via the relationship.
$role_with_pivot = $user->roles()->where('role_user.id', $attributes['id'])->first();
return response()->json(['role' => $role_with_pivot], 200);
The data I am sending in my request headers is correct. I am not getting any errors, however my pivot table is not getting the values I am passing to it. I assume it's because I am not correctly sending the id. In my example above it would be id: 5. Because there isn't a user_id of 5 or role_id of 5, Laravel isn't sure what to do.
How can I tell Laravel to look at the id of the pivot table, and not a foreign key?

The way I am doing it though seems kind of sketchy. I'd be really interested to see if there is a more "Laravel way" of doing this. Everything else is above is the same, here is what my update method on my controller looks like.
controller
public function update(Request $request, Role $role)
{
$attributes = request()->validate([
'active' => 'required',
'id' => 'required',
]);
// Get the authenticated user.
$user = auth()->user();
// Get the requested role (row) from the pivot table.
$role = $user->roles()->where('role_user.id', $attributes['id'])->first();
// Update the requested role.
$role->pivot->active = $attributes['active'];
// Save the requested role.
$role->pivot->save();
// Get the role that was just updated via it's relationship.
$role_with_pivot = $user->roles()->where('role_user.id', $attributes['id'])->first();
return response()->json(['role' => $role_with_pivot], 200);
}

You should retrieve role id in you View instead of pivot primary key, and use role_id to update pivot table.
As stated your user has multiple roles, but only one combination of role and user id.
async handleRoleClick(event, role) {
console.log('updating role: ', role);
try {
let response = await axios.patch(`/my-path`, {
active: !this.active,
id: role.id, //change this
});
// data getting passed is correct.
// active: false
// id: 5 <-- the id of the pivot table, not a FK value
if (response.status === 200) {
console.log('response.data', response.data);
this.active = response.data.role.pivot.active;
console.log('active: ', this.active);
} else {
console.error('Error: could not update role. ', response);
}
} catch (error) {
console.error('Error: sending patch request. ', error);
}
},

it's late, but might help some artisans in the future.
You may use DB method directly or create a dedicated model/entity for that table.
e.g
DB::table('role_user')->where('id', $id);
or
RoleUser::find($id);

Related

How do I return the ID field in a related table in Laravel request

I have two related tables and I want to return all fields including the ID (key) field. My query below returns all fields except the ID. how do I return the ID field from one of the tables?
'programmes' => ProgrammeInstances::with('programmes')->get(),
the query below returns Unknown column 'programmes.programme_title' as it is looking for it in the table 'programme_instances'
'programmes' => ProgrammeInstances::with('programmes')->select('programmes.programme_title', 'programmeInstances.id', 'programmeInstances.name', 'programmeInstances.year')->get(),
Laravel provides multiple relationships, one of these is the hasMany() relationship which should return a collection where a User hasMany rows inside of your database
For example, inside your User model :
public function programmes() {
return $this->hasMany(Program::class);
}
Now in your controller, you can do :
public function edit($id) {
$programmes = User::find($id)->with('programmes')->get();
return view('user.edit')->with('programmes', $programmes);
}
And then you can loop over it inside your view
#forelse($programmes->programmes as $program)
// provide the data
#empty
// the user doesn’t have any programmes
#endforelse
a solution i found below - still not sure why ID isnt automatically returned when i get all fields, but works when i specify individual fields:
'programmes' => ProgrammeInstances::with('programmes')
->get()
->transform(fn ($prog) => [
'programme_title' => $prog->programmes->programme_title,
'id' => $prog->id,
'name' => $prog->name,
'year' => $prog->year,
]),

How to set the query in a relationship in laravel?

I have this tables
usercontract
user
invite
Invite contain the invitations to participate in a contract and they are send by email. I want to use the email as id in order to get all the contracts a user as been invited to participate to.
And I need to do it as a relationship on my User model, so I can use the logged user email as filter.
This is the relationship I have defined but is using the id of the user to match with the foreing_key (usercontract) on the invite table. I don't understand very well why.
public function nonAcceptedContracts()
{
return $this->belongsToMany(UserContract::class, 'invite', 'email', 'usercontract');
}
I need it as a relationship because I want to call it on my repository, like this.
public function showRelated(User $user)
{
$parentUser = $user->parentUser;
$usercontract = UserContract
::with(['topics', 'contracttax', 'contractproperty', 'persons', 'contractwarranty', 'contractencumbrance', 'users', 'contracts', 'tags'])
->whereHas('users', function ($query) use ($user, $parentUser) {
$user->loadMissing('nonAcceptedContracts');
dd($user->nonAcceptedContracts);
$query->where('id', $user->getAuthIdentifier());
if ($parentUser) {
$query->orWhere('parent', $parentUser->id);
}
if ($user->siblingUsers()->count() > 0) { // get contracts of the sibling-users of the user
$query->orWhereIn('id', $user->siblingUsers()->pluck('id'));
}
if ($user->subUsers->count() > 0) { // get contracts of the users sub-users
$query->orWhereIn('id', $user->subUsers->pluck('id'));
}
return $query;
})->orderBy('created_at', 'desc')->get();
}
I guess my question is how to set a relationship in order to get the usercontracts for that user (the object) using his email as a key to get the usercontract id (usercontract) on the invite table.
SELECT * FROM `usercontract` WHERE usercontract.id IN (SELECT invite.id FROM invite WHERE `invite`.`email` = 'someemail#gmail.com')
public function nonAcceptedContracts()
{
return $this->belongsToMany(UserContract::class, 'invite', 'email', 'usercontract', 'email')->where('status', 'pending');
}
That relationship uses my "invite" table as pivot using the "email" and "usercontract" has keys and filtering the results with the "email" of the "user" table.

How to relate additional tables to Auth::user

On documentation of Laravel, to retrieve the authenticated user:
$user = Auth::user();
This is my table schema (example)
tblUser
id // 1
userid // admin
password // 12345
tblUserInfo
id // 1
userid // admin
first_name // Roi
Is it possible to relate the tblUserInfo for the authenticated user? like for example
Auth::user()->with('userinfo')->first_name;
or is there a way around to get the first_name from authenticated user before I hit the view?
You should be able to just do
$firstname = Auth::user()->userinfo->first_name;
This assumes you have your relationship defined properly. Something along the lines of
class User extends Authenticatable
{
// ...
public function userinfo()
{
return $this->hasOne(UserInfo::class);
}
}
Given your non-standard table schema you may need to explicitly specify column names for foreign and local keys in hasOne() method - 2nd and 3rd parameters accordingly.
return $this->hasOne(UserInfo::class, 'id', 'id'); // or 'userid' depending on what you're using as a FK
^^^^^^^^^

Laravel 5.4 with Entrust: How to get users with roles

I am trying to get all users with roles, but "roles" field is always an empty array.
There are 4 users, 2 of them have at least 1 role attached, verified.
UserController.php
public function getUsers()
{
$users = User::select('name', 'email', 'type')->with('roles')->get();
return view('user.list', ['users' => $users, 'lang' => Lang::getLocale()]);
}
I get the 'roles' field as an empty array, even if I don't use "with->('roles')" in the controller. Weird?
If i print each user role in the view, I get this:
object(Illuminate\Database\Eloquent\Collection)#281 (1) { ["items":protected]=> array(0) { } }
What I am doing wrong?
You have to select also primary key, on which roles are related.
Eloquent is first selecting all users and then based on their IDS (primary and foreign) is selecting relations. If you dont select primary key, he cant relate relations.
$users = User::select('id', 'name', 'email', 'type')->with('roles')->get();
Also, if you want specifi which columns from roles you want, you have to select foreign key too.
$users = User::select('id', 'name', 'email', 'type')->with(['roles' => function ($query) {$query->select('id', 'name', 'user_id');}])->get();

laravel - eloquent - get sum of related model specific column

assuming that I have the table
orders
with fields
id, userId, amount, description
and the table
user
with various fields
how if I wand to get all the users (with all its fields) and also the sum of the "amount" column of the orders related to that user?
assuming that I have:
user:{id:15,firstName:jim,lastName:morrison,gender:male}
and
order:{id:1,userId:15,amount:10,description:"order xxx"},
order:{id:3,userId:15,amount:40,description:"order yyy"}
I would like to receive:
user:{id:15,firstName:jim,lastName:morrison,gender:male,orderAmount:50}
Of course I would like to avoid the foreach statement.
I've setted this on my user model
public function userOrder (){
return $this->hasMany('Order', 'userId');
}
And I've tryed this:
return $this->hasMany('Order', 'userId')->sum('amount');
without any luck...
Some thaughts and hopefully an answer to your question:
I would rename the user table to users to stick to laravel conventions.
http://laravel.com/docs/4.2/eloquent#basic-usage
I would name the method in the User model orders
public function orders()
{
return $this->hasMany('Order', 'userId');
}
To query a user, his orders and sum afterwards his orders amount values:
$userdata = User::with( 'orders' )->where( 'userId', 15 )->first();
$sum = $userdata[ 'orders' ]->sum( 'amount' );

Resources