jak888's avatar

efficiently retrieving values from many to many relationship

I asked this on the discord the other day, but since we're way off-topic from that original post, I've decided to come here for some additional guidance. Can't include links yet, but can provide if necessary. To sum up my original issues: 1) I was using composite primary keys 2) I was calling to the pivot table quite a lot a la EventUsers::where()...

Based on the advice I received there, I'm trying to remedy this. I have 3 tables in my app, that represent the roles a user plays in an event. Here's how I've set this up:

event
- id
- name
- location
user
- id
- name
- email
role
- id
- role

user_event_roles
- id
- user_id
- event_id
- role_id

First of all, does this make sense? I was told in the other thread that I should keep the ID around in the pivot table, but I'm also reading quite often that it's unnecessary.

And for the models: event:

public function users(): BelongsToMany
{
	return $this->belongsToMany(User::class, 'user_event_roles')->withPivot('role_id')->withTimestamps();
}

public function roles(): BelongsToMany
{
       return $this->belongsToMany(Role::class, 'user_event_roles')->withPivot('user_id');
}

user:

public function events(): BelongsToMany
{
	return $this->belongsToMany(Season::class, 'user_event_roles')->withPivot('role_id')->withTimestamps();
}

public function roles(): BelongsToMany
{
	return $this->belongsToMany(Role::class, 'user_event_roles');
}

role:

public function events(): BelongsToMany
{
	return $this->belongsToMany(Season::class, 'user_event_roles')->withPivot('user_id')->withTimestamps();
}

public function users(): BelongsToMany
{
	return $this->belongsToMany(User::class, 'user_event_roles');
}

Now, as I start implementing my functions, I'm increasingly wondering if I'm doing this correctly, so I'd appreciate the insight on what the best practice or the "right" way of utilizing this setup is.

Here are the tasks, that I'm trying to accomplish:

Attach with the role string:

$user->events()->attach([$event_id => ['role_id' => Role::where('role', $roleString)->first()->id]])

Is there a more direct way than retrieving the id first and then writing it?

Get a specific user's role in a specific event:

$eventUserRole = $event->users()->where('users.id', $user->id)
    ->join('roles', 'user_event_roles.role_id', '=', 'roles.id')
    ->select('users.*', 'roles.role')
    ->first()->role;

Seems a bit convoluted to ask: I have an event, I have a user, what role do they play?

Get all users in an event along with their role:

 $event_users = User::join('user_event_roles', 'users.id', '=', 'user_event_roles.user_id')
	->join('roles', 'user_event_roles.role_id', '=', 'roles.id')
	->where('user_event_roles.season_id', $id)
	->select('users.*', 'roles.role')
	->get();

Again, seems like a lot. Previously I tried this, but it returns all roles for the user, not just the ones for this event. It gave me the user and then an array of roles instead of a user with role for each user/role combination.

 $event_users = $event->users()
	->with(['roles' => function ($query) {
		$query->select('role');
	}])
	->select('users.id', 'users.first_name', 'users.last_name', 'users.email')
	->groupBy('users.id', 'users.first_name', 'users.last_name')
	->get();

How can I improve any of these? Also, is there a way of attaching a whole list of users with the same role to an event, e.g. my entire morning shift? I mean, I could foreach over them, but I'm wondering.

I'm also beginning to suspect I would have been better off just using a string for the role instead of referencing it from another table. That way I wouldn't have to join it every time.

Thanks in advance for any pointers!

0 likes
2 replies
jak888's avatar

@katorymnd : I don't know why your answer is not showing here...

I have some follow up questions. For getting a user's role for an event you suggested:

$role = $user->events()->where('event_id', $event->id)->first()->pivot->role_id;

What if I want the role string?

And for getting all users in an event with role, the same question, really. I have queries in place, that are working, but I'm really trying to make the most of my new setup.

$users = $event->users;
foreach($users as $user) {
    echo $user->name . ' has role ' . $user->pivot->role_id;
}

Thanks for the other pointers! This already helped me a lot and gave me the reassurance to move forward!

jak888's avatar

So here's an additional question: I might end up with multiple entries for the same user/event combination, like so:

user	event	role
12		3			3
12		3			4

How would I go about only deleting 12-3-3?

NVM, this does it: $User::find(12)->events()->wherePivot('role', 3)->detach(3);

Please or to participate in this conversation.