Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

nanodreams's avatar

Laravel Eloquent Query to retrieve effective users (morph relationship)

Hi there,

Thanks for the help in advance. I have a database schema with User, Group and Profile. To assign permissions I created a pivot table called memberable and added a morph relationship as follow:

	users
			- id
			- ...
	groups
			- id
			- ...
	group_user
			- group_id
			- user_id
	profiles
			- id
			- ...
	memberables
			- id
			- memberable_id
			- memberable_type
			- profile_id

So far so good.

  • I have users that belongs to groups.
  • I have users assigned to profiles.
  • I have groups assigned to profiles.

Now, my doubt is how to create a relationship in laravel to retrieve the "effective" users or the "effective" profiles. For example:

User::first()->effectiveProfiles => I would like to retrieve the profies that are either assigned directly to the user via morph or profiles assigned to a group via morph where the user is part of.

I can initially create a effectiveProfiles method in User model as:

		public function effectiveProfiles()
		{ 
					return Profile::whereHas("users", fn($users) => $users->whereId($this->id))
								->orWhereHas("groups", fn($groups) => $groups->whereHas("users", => $users->whereId($this->id)));
		}

But this will be a method rather than a relationship where I can't do eagler loading and all beauty of Eloquent.

Does anyone came up with a solution for this type of scenarios?

0 likes
8 replies
nanodreams's avatar

@DhPandya Thanks for your fast reponse.

That's actually the one I'm using. Whereas you replace the "Video" and "Post" by "User" and "Group" ... and then "Tag" by "Profile" and it's the same code as I implemented. The difference is that between my User and Group ... I do have a relationship, so then here is where the real "question is". How can I get the Users that belong to a Profile, either via the ManyToMany(polymorphic) or via the ManyToMany(polymorphic) + ManyToMany(group_user).

That's the tricky one.

DhPandya's avatar

@nanodreams If your model User and profile are directly connected with each other, You should use belongsTo() on the profile and hasOne() on the User model. It will be very easy to access data for the user and profile and vice-versa or consider using hasManyThrough().

nanodreams's avatar

@DhPandya Let me put here some details:

User: /** * Relationships */

public function groups()
{
    return $this->belongsToMany(Group::class);
} 

public function profiles()
{
    return $this->morphToMany(Profile::class, 'membershipable', 'membershipables');
}

Profile: /** * Relationships */

public function users()
{
    return $this->morphedByMany(User::class, 'membershipable', 'membershipables');
}

public function groups()
{
    return $this->morphedByMany(Group::class, 'membershipable', 'membershipables');
}

Group: /** * Relationships */

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

public function profiles()
{
    return $this->morphToMany(Profile::class, 'membershipable', 'membershipables');
}

So, within this code, you can see I'm using manyToMany polymorph, so that part is clear and it works. Now I don't know I could do something like:

Profile: public function effectiveUsers() { return ???????? }

So that it will be a "relationship and it will retrive all the users from "$profile->users()" in combiantion with the User::whereHas('groups', fn($groups) => $groups->whereHas('profiles', fn($profiles) => $profiles->where('id', $profile->id))).

THe idea is that I can assign permissions to a user via "Group" or directly to the "User". So it's more flexible

Many thanks for your feedback BTW.

krisi_gjika's avatar

@nanodreams what if the user has a profile directly AND via it's groups, or the user is assigned to multiple groups and each of the groups has multiple profiles? What profile would you receive back?

nanodreams's avatar

@krisi_gjika The use case I have .. is that a user can have multiple profiles. So as an example: Within a company you can have different roles, such as Manager, Administrator, Accountant. Let say a single user might be doing different tasks, so when you create "tasks" you will be assigning those "task" to a profile. If a user has many profiles ... the available tasks will be the combination of all the tasks across all the profiles that the user is part of.

That's why it's important to have this "effective" relationship so it can be usefull when doing nested queries to retrieve tasks, actions or anything that belongs to a profile ...

The idea is to be able to assign profiles to users or groups, making it very flexible. If a team needs permissions, it will be done via group, then if a single user requires access, then it can be assigned by user, without having to create a group.

PS: Alternativelly I was thinking ... is to create a "group" just dedicated to a "users" ... then it will be resolved with a single relationship, but not sure if that would be too "standard" way of doing things.

krisi_gjika's avatar

@nanodreams I don't think an $user->effectiveProfiles relation would be possible, but I think a relation $profile->effectiveUser should be possible.

However why not assign a profile to the all the group users, when a profile is assigned to a group and avoid this problem? Basically: group is assigned a profile -> loop group users -> assign profile to each user, user is added/removed from group -> assign/detach profile from user if not in use

nanodreams's avatar

@krisi_gjika Thanks for the hint. I think I'll change the approach a bit and kind of what you indicated, but slightly different.

I'll share here, in case someone needs it.

Some details:

  • Users: synced from an AD.
  • Groups: synced from an AD (every time a user login, it will sync the AD groups for the user).
  • Profiles: Models where the laravel app will be assigned different functionalities. A single user can can multiple profiles (employee, department A, late shift ....)

So my idea will be to create the Groups model with additional fields:

  • is_user_group

A group will be (for now), either synced by the AD ... or created by the laravel APP (aka "is_user_group"). The idea is to create a single group for a single user, so it can be assigned, something like using a many to many but for just one group - one user. Independently .. groups will be attached / detached based on the AD groups payload.

So if I have a user ... I can retrieve their profiles with user->profiles (being a hasManyThrow) and from a profile I can see all the users with the same approach but reversed. Also .. I can easily see if permissions are assigned by "group" level or "user level" by filtering profile->groups->groupBy('is_user_group')

Then .. I can avoid the use of morph and more complex idea.

PS: Thanks for those that helped to this idea .... also feel free to share any other approach ;-). The main goal here is to be "flexible" to assign "profiles" by groups or by individual users. Some use cases, usually will ask a team to be assigned, but an edge case will be .... "please do add this user too" without changing the AD structure (on the app level).

Please or to participate in this conversation.